PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.4
MxChat – AI Chatbot & Content Generation for WordPress v2.0.4
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / js / chat-script.js

chat-script.js in MxChat – AI Chatbot & Content Generation for WordPress 2.0.4, at js/chat-script.js

1,559 lines 52.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 jQuery(document).ready(function($) {
2 //console.log('mxchatChat object:', mxchatChat);
3 //console.log('Link Target Toggle Value:', mxchatChat.link_target_toggle);
4 // Add these variables at the top of your chat-script.js file
5 const toolbarIconColor = mxchatChat.toolbar_icon_color || '#212121';
6
7 // Initialize color settings
8 var userMessageBgColor = mxchatChat.user_message_bg_color;
9 var userMessageFontColor = mxchatChat.user_message_font_color;
10 var botMessageBgColor = mxchatChat.bot_message_bg_color;
11 var botMessageFontColor = mxchatChat.bot_message_font_color;
12 // Add live agent message colors
13 var liveAgentMessageBgColor = mxchatChat.live_agent_message_bg_color;
14 var liveAgentMessageFontColor = mxchatChat.live_agent_message_font_color;
15
16
17 var linkTarget = mxchatChat.link_target_toggle === 'on' ? '_blank' : '_self';
18 let lastSeenMessageId = '';
19 let notificationCheckInterval;
20 let notificationBadge;
21 // Initialize session ID
22 var sessionId = getChatSession();
23
24 let pollingInterval; // Variable to store the interval ID
25 let processedMessageIds = new Set(); // Add this at the top with your other variables
26 //console.log('Live Agent BG Color:', liveAgentMessageBgColor);
27 //console.log('Live Agent Font Color:', liveAgentMessageFontColor);
28 let activePdfFile = null;
29 let activeWordFile = null;
30
31
32 // Function to create and append notification badge
33 // Function to create and append notification badge
34 function createNotificationBadge() {
35 console.log("Creating notification badge...");
36 const chatButton = document.getElementById('floating-chatbot-button');
37 console.log("Chat button found:", !!chatButton);
38
39 if (!chatButton) return;
40
41 // Remove any existing badge first
42 const existingBadge = chatButton.querySelector('.chat-notification-badge');
43 if (existingBadge) {
44 console.log("Removing existing badge");
45 existingBadge.remove();
46 }
47
48 notificationBadge = document.createElement('div');
49 notificationBadge.className = 'chat-notification-badge';
50 notificationBadge.style.cssText = `
51 display: none;
52 position: absolute;
53 top: -5px;
54 right: -5px;
55 background-color: red;
56 color: white;
57 border-radius: 50%;
58 padding: 4px 8px;
59 font-size: 12px;
60 font-weight: bold;
61 z-index: 10001;
62 `;
63 chatButton.style.position = 'relative';
64 chatButton.appendChild(notificationBadge);
65
66 console.log("Notification badge created and appended:", {
67 exists: !!notificationBadge,
68 parent: notificationBadge?.parentNode?.id,
69 display: notificationBadge?.style?.display
70 });
71 }
72
73 // Function to check for new messages
74 function checkForNewMessages() {
75 const sessionId = getChatSession();
76 const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
77
78 if (!chatPersistenceEnabled) return;
79
80 $.ajax({
81 url: mxchatChat.ajax_url,
82 type: 'POST',
83 data: {
84 action: 'mxchat_check_new_messages',
85 session_id: sessionId,
86 last_seen_id: lastSeenMessageId,
87 nonce: mxchatChat.nonce
88 },
89 success: function(response) {
90 if (response.success && response.data.hasNewMessages) {
91 showNotification();
92 }
93 }
94 });
95 }
96
97 // Function to show notification
98 function showNotification() {
99 const badge = document.getElementById('chat-notification-badge');
100 if (badge && $('#floating-chatbot').hasClass('hidden')) {
101 badge.style.display = 'block';
102 badge.textContent = '1';
103 }
104 }
105
106 function hideNotification() {
107 const badge = document.getElementById('chat-notification-badge');
108 if (badge) {
109 badge.style.display = 'none';
110 }
111 }
112 // Function to start notification checking
113 function startNotificationChecking() {
114 const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
115 if (!chatPersistenceEnabled) return;
116
117 createNotificationBadge();
118 notificationCheckInterval = setInterval(checkForNewMessages, 30000); // Check every 30 seconds
119 }
120
121 // Function to stop notification checking
122 function stopNotificationChecking() {
123 if (notificationCheckInterval) {
124 clearInterval(notificationCheckInterval);
125 }
126 }
127
128
129
130
131 function getChatSession() {
132 var sessionId = getCookie('mxchat_session_id');
133 //console.log("Session ID retrieved from cookie: ", sessionId);
134
135 if (!sessionId) {
136 sessionId = generateSessionId();
137 //console.log("Generated new session ID: ", sessionId);
138 setChatSession(sessionId);
139 }
140
141 //console.log("Final session ID: ", sessionId);
142 return sessionId;
143 }
144
145 function setChatSession(sessionId) {
146 // Set the cookie with a 24-hour expiration (86400 seconds)
147 document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
148 }
149
150 // Get cookie value by name
151 function getCookie(name) {
152 let value = "; " + document.cookie;
153 let parts = value.split("; " + name + "=");
154 if (parts.length == 2) return parts.pop().split(";").shift();
155 }
156
157 // Generate a new session ID
158 function generateSessionId() {
159 return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
160 }
161
162 // Function to send the message to the chatbot (backend)
163 function sendMessageToChatbot(message) {
164 var sessionId = getChatSession(); // Reuse the session ID logic
165
166 // Hide the popular questions section
167 $('#mxchat-popular-questions').hide();
168
169 // Show thinking indicator (no need to append the user's message again)
170 appendThinkingMessage();
171 scrollToBottom();
172
173 //console.log("Sending message to chatbot:", message); // Log the message
174 //console.log("Session ID:", sessionId); // Log the session ID
175
176 // Call the chatbot using the same call logic as sendMessage
177 callMxChat(message, function(response) {
178 // ** Ensure temporary thinking message is removed before adding new response **
179 $('.temporary-message').remove();
180
181 // Replace thinking indicator with actual response
182 replaceLastMessage("bot", response);
183 });
184 }
185
186
187
188
189 function sendMessage() {
190 var message = $('#chat-input').val(); // Get value from textarea
191 if (message) {
192 appendMessage("user", message); // Append user's message
193 $('#chat-input').val(''); // Clear the textarea
194 $('#chat-input').css('height', 'auto'); // Reset height after clearing content
195
196 // Hide the popular questions section
197 $('#mxchat-popular-questions').hide();
198
199 // Show typing indicator
200 appendThinkingMessage();
201 scrollToBottom();
202
203 callMxChat(message, function(response) {
204 // Replace typing indicator with actual response
205 replaceLastMessage("bot", response);
206 });
207 }
208 }
209
210
211
212 // Function to append a thinking message with animation
213 function appendThinkingMessage() {
214 // Remove any existing thinking dots first
215 $('.thinking-dots').remove();
216
217 // Retrieve the bot message font color and background color
218 var botMessageFontColor = mxchatChat.bot_message_font_color;
219 var botMessageBgColor = mxchatChat.bot_message_bg_color;
220
221
222 var thinkingHtml = '<div class="thinking-dots-container">' +
223 '<div class="thinking-dots">' +
224 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
225 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
226 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
227 '</div>' +
228 '</div>';
229
230 // Append the thinking dots to the chat container (or within the temporary message div)
231 $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
232 scrollToBottom();
233 }
234
235 // Trigger send button click when "Enter" key is pressed in the textarea
236 $('#chat-input').keypress(function(e) {
237 if (e.which == 13 && !e.shiftKey) { // Check if "Enter" is pressed without Shift
238 e.preventDefault(); // Prevent default "Enter" behavior
239 $('#send-button').click(); // Trigger send button click
240 }
241 });
242
243 // Handle send button click
244 $('#send-button').click(function() {
245 sendMessage();
246 });
247
248 // Handle click on popular questions
249 $('.mxchat-popular-question').on('click', function () {
250 var question = $(this).text(); // Get the text of the clicked question
251
252 // Append the question as if the user typed it
253 appendMessage("user", question);
254
255 // Send the question to the server (backend)
256 sendMessageToChatbot(question);
257 });
258
259
260 // Add this new function to handle markdown headers
261 function formatMarkdownHeaders(text) {
262 // Handle h1 to h6 headers
263 return text.replace(/^(#{1,6})\s(.+)$/gm, function(match, hashes, content) {
264 const level = hashes.length;
265 return `<h${level} class="chat-heading">${content}</h${level}>`;
266 });
267 }
268
269 // Update the linkify function to handle both URLs and markdown
270 function linkify(inputText) {
271 // First process markdown headers
272 let processedText = formatMarkdownHeaders(inputText);
273
274 // Then process links as before
275 var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
276 processedText = processedText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
277
278 // Replace standalone URLs not already in an <a> tag
279 var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
280 processedText = processedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
281
282 // Replace "www." prefixed URLs not already in an <a> tag
283 var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
284 processedText = processedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
285
286 return processedText;
287 }
288
289
290 function scrollElementToTop(element) {
291 var chatBox = $('#chat-box');
292 var elementTop = element.position().top + chatBox.scrollTop();
293 chatBox.animate({ scrollTop: elementTop }, 500);
294 }
295
296
297 // Optimized scrollToBottom function for instant scrolling
298 function scrollToBottom(instant = false) {
299 var chatBox = $('#chat-box');
300 if (instant) {
301 // Instantly set the scroll position to the bottom
302 chatBox.scrollTop(chatBox.prop("scrollHeight"));
303 } else {
304 // Use requestAnimationFrame for smoother scrolling if needed
305 let start = null;
306 const scrollHeight = chatBox.prop("scrollHeight");
307 const initialScroll = chatBox.scrollTop();
308 const distance = scrollHeight - initialScroll;
309 const duration = 500; // Duration in ms
310
311 function smoothScroll(timestamp) {
312 if (!start) start = timestamp;
313 const progress = timestamp - start;
314 const currentScroll = initialScroll + (distance * (progress / duration));
315 chatBox.scrollTop(currentScroll);
316
317 if (progress < duration) {
318 requestAnimationFrame(smoothScroll);
319 } else {
320 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
321 }
322 }
323
324 requestAnimationFrame(smoothScroll);
325 }
326 }
327
328
329 // Function to format text with **bold** inside double asterisks
330 function formatBoldText(text) {
331 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
332 }
333
334 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
335 function convertNewlinesToBreaks(text) {
336 // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
337 const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
338
339 // Wrap each paragraph in <p> tags
340 return paragraphs
341 .map(para => `<p>${para.trim()}</p>`)
342 .join('');
343 }
344 // Copy to clipboard function
345 // Function to copy text to clipboard
346 function copyToClipboard(text) {
347 var tempInput = $('<input>');
348 $('body').append(tempInput);
349 tempInput.val(text).select();
350 document.execCommand('copy');
351 tempInput.remove();
352 }
353
354
355 function updateChatModeIndicator(mode) {
356 const indicator = document.getElementById('chat-mode-indicator');
357 if (indicator) {
358 indicator.textContent = mode === 'agent' ? 'Live Agent' : 'AI Agent';
359 }
360
361 // Start or stop polling based on mode
362 if (mode === 'agent') {
363 startPolling();
364 } else {
365 stopPolling();
366 }
367 }
368
369 function callMxChat(message, callback) {
370 $.ajax({
371 url: mxchatChat.ajax_url,
372 type: 'POST',
373 dataType: 'json',
374 data: {
375 action: 'mxchat_handle_chat_request',
376 message: message,
377 session_id: getChatSession(),
378 nonce: mxchatChat.nonce
379 },
380 success: function(response) {
381 // Existing chat mode check
382 if (response.chat_mode) {
383 updateChatModeIndicator(response.chat_mode);
384 }
385 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
386 updateChatModeIndicator(response.fallbackResponse.chat_mode);
387 }
388
389 // Add PDF filename handling
390 if (response.data && response.data.filename) {
391 showActivePdf(response.data.filename);
392 activePdfFile = response.data.filename;
393 }
394
395
396 // Check for live agent response
397 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
398 updateChatModeIndicator('agent');
399 return;
400 }
401
402 // Handle other responses
403 let responseText = response.text || '';
404 let responseHtml = response.html || '';
405 let responseMessage = response.message || '';
406
407 if (responseText === 'You are now chatting with the AI chatbot.') {
408 updateChatModeIndicator('ai');
409 }
410
411 // Handle the message and show notification if chat is hidden
412 if (responseText || responseHtml || responseMessage) {
413 // Update the messages as before
414 if (responseText && responseHtml) {
415 replaceLastMessage("bot", responseText, responseHtml);
416 } else if (responseText) {
417 replaceLastMessage("bot", responseText);
418 } else if (responseHtml) {
419 replaceLastMessage("bot", "", responseHtml);
420 } else if (responseMessage) {
421 replaceLastMessage("bot", responseMessage);
422 }
423
424 // Check if chat is hidden and show notification
425 if ($('#floating-chatbot').hasClass('hidden')) {
426 const badge = $('#chat-notification-badge');
427 if (badge.length) {
428 badge.show();
429 }
430 }
431 } else {
432 console.error("Unexpected response format:", response);
433 replaceLastMessage("bot", "I'm sorry, something went wrong.");
434 }
435
436 if (response.message_id) {
437 lastSeenMessageId = response.message_id;
438 }
439 },
440 error: function(xhr, status, error) {
441 replaceLastMessage("bot", "An unexpected error occurred.");
442 }
443 });
444 }
445
446 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
447 //console.log("Appending message. Sender:", sender, "Content:", messageText);
448
449 try {
450 // Determine styles based on sender type
451 let messageClass, bgColor, fontColor;
452
453 if (sender === "user") {
454 messageClass = "user-message";
455 bgColor = userMessageBgColor;
456 fontColor = userMessageFontColor;
457 } else if (sender === "agent") {
458 messageClass = "agent-message";
459 bgColor = liveAgentMessageBgColor;
460 fontColor = liveAgentMessageFontColor;
461 } else {
462 messageClass = "bot-message";
463 bgColor = botMessageBgColor;
464 fontColor = botMessageFontColor;
465 }
466
467 const messageDiv = $('<div>')
468 .addClass(messageClass)
469 .css({
470 'background': bgColor,
471 'color': fontColor,
472 });
473
474 // Add CSS for paragraphs
475 messageDiv.css({
476 'margin-bottom': '1em'
477 });
478
479 // Format and process the message content
480 let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
481
482 // Add images if provided
483 if (images && images.length > 0) {
484 fullMessage += '<div class="image-gallery">';
485 images.forEach(img => {
486 fullMessage += `
487 <div style="margin-bottom: 10px;">
488 <strong>${img.title}</strong><br>
489 <a href="${img.image_url}" target="_blank">
490 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
491 </a>
492 </div>`;
493 });
494 fullMessage += '</div>';
495 }
496
497 // Append HTML content if provided
498 if (messageHtml) {
499 fullMessage += '<br><br>' + messageHtml;
500 }
501
502 messageDiv.html(fullMessage);
503
504 // Add a class for temporary messages if needed
505 if (isTemporary) {
506 messageDiv.addClass('temporary-message');
507 }
508
509 // Append the message to the chat box
510 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
511 if (sender === "bot") {
512 // After bot's message is displayed, scroll the last user message to the top
513 const lastUserMessage = $('#chat-box').find('.user-message').last();
514 if (lastUserMessage.length) {
515 scrollElementToTop(lastUserMessage);
516 }
517 }
518 });
519
520 if (messageText.id) {
521 lastSeenMessageId = messageText.id;
522 hideNotification();
523 }
524 } catch (error) {
525 console.error("Error rendering message with images:", error);
526 }
527 }
528
529
530 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
531 var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
532 var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();
533
534 // Determine styles
535 let bgColor, fontColor;
536 if (sender === "user") {
537 bgColor = userMessageBgColor;
538 fontColor = userMessageFontColor;
539 } else if (sender === "agent") {
540 bgColor = liveAgentMessageBgColor;
541 fontColor = liveAgentMessageFontColor;
542 } else {
543 bgColor = botMessageBgColor;
544 fontColor = botMessageFontColor;
545 }
546
547 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
548 if (responseHtml) {
549 fullMessage += '<br><br>' + responseHtml;
550 }
551
552 if (images.length > 0) {
553 fullMessage += '<div class="image-gallery">';
554 images.forEach(img => {
555 fullMessage += `
556 <div style="margin-bottom: 10px;">
557 <strong>${img.title}</strong><br>
558 <a href="${img.image_url}" target="_blank">
559 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
560 </a>
561 </div>`;
562 });
563 fullMessage += '</div>';
564 }
565
566 if (lastMessageDiv.length) {
567 lastMessageDiv.fadeOut(200, function() {
568 $(this)
569 .html(fullMessage)
570 .removeClass('bot-message user-message')
571 .addClass(messageClass)
572 .css({
573 'background-color': bgColor,
574 'color': fontColor,
575 })
576 .removeClass('temporary-message')
577 .fadeIn(200, function() {
578 if (sender === "bot" || sender === "agent") {
579 const lastUserMessage = $('#chat-box').find('.user-message').last();
580 if (lastUserMessage.length) {
581 scrollElementToTop(lastUserMessage);
582 }
583 // Show notification if chat is hidden
584 if ($('#floating-chatbot').hasClass('hidden')) {
585 showNotification();
586 }
587 }
588 });
589 });
590 } else {
591 appendMessage(sender, responseText, responseHtml, images);
592 }
593 }
594
595
596 function startPolling() {
597 // Clear any existing interval first
598 stopPolling();
599 // Start new polling interval
600 pollingInterval = setInterval(checkForAgentMessages, 5000);
601 //console.log("Started agent message polling");
602 }
603
604 function stopPolling() {
605 if (pollingInterval) {
606 clearInterval(pollingInterval);
607 pollingInterval = null;
608 //console.log("Stopped agent message polling");
609 }
610 }
611
612
613 // Update your checkForAgentMessages function
614 function checkForAgentMessages() {
615 const sessionId = getChatSession();
616 $.ajax({
617 url: mxchatChat.ajax_url,
618 type: 'POST',
619 dataType: 'json',
620 data: {
621 action: 'mxchat_fetch_new_messages',
622 session_id: sessionId,
623 last_seen_id: lastSeenMessageId,
624 nonce: mxchatChat.nonce
625 },
626 success: function (response) {
627 if (response.success && response.data?.new_messages) {
628 let hasNewMessage = false;
629
630 response.data.new_messages.forEach(function (message) {
631 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
632 hasNewMessage = true;
633 replaceLastMessage("agent", message.content);
634 lastSeenMessageId = message.id;
635 processedMessageIds.add(message.id);
636 }
637 });
638
639 if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
640 showNotification();
641 }
642
643 scrollToBottom(true);
644 }
645 },
646 error: function (xhr, status, error) {
647 console.error("Polling error:", xhr, status, error);
648 }
649 });
650 }
651 function loadChatHistory() {
652 var sessionId = getChatSession();
653 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
654
655 if (chatPersistenceEnabled && sessionId) {
656 $.ajax({
657 url: mxchatChat.ajax_url,
658 type: 'POST',
659 dataType: 'json',
660 data: {
661 action: 'mxchat_fetch_conversation_history',
662 session_id: sessionId
663 },
664 success: function(response) {
665 if (response.success && response.data && Array.isArray(response.data.conversation)) {
666
667
668 var $chatBox = $('#chat-box');
669 var $fragment = $(document.createDocumentFragment());
670 let highestMessageId = lastSeenMessageId;
671
672 if (response.data.chat_mode) {
673 updateChatModeIndicator(response.data.chat_mode);
674 }
675
676 $.each(response.data.conversation, function(index, message) {
677 // Skip agent messages if persistence is off
678 if (!chatPersistenceEnabled && message.role === 'agent') {
679 return;
680 }
681
682 var messageClass, messageBgColor, messageFontColor;
683
684 switch (message.role) {
685 case 'user':
686 messageClass = 'user-message';
687 messageBgColor = userMessageBgColor;
688 messageFontColor = userMessageFontColor;
689 break;
690 case 'agent':
691 messageClass = 'agent-message';
692 messageBgColor = liveAgentMessageBgColor;
693 messageFontColor = liveAgentMessageFontColor;
694 break;
695 default:
696 messageClass = 'bot-message';
697 messageBgColor = botMessageBgColor;
698 messageFontColor = botMessageFontColor;
699 break;
700 }
701
702 var messageElement = $('<div>').addClass(messageClass)
703 .css({
704 'background': messageBgColor,
705 'color': messageFontColor
706 });
707
708 var content = message.content;
709 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
710 content = decodeHTMLEntities(content);
711
712 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
713 messageElement.html(content);
714 } else {
715 var formattedContent = linkify(
716 formatBoldText(
717 convertNewlinesToBreaks(formatCodeBlocks(content))
718 )
719 );
720 messageElement.html(formattedContent);
721 }
722
723 $fragment.append(messageElement);
724
725 // In loadChatHistory, change this part:
726 if (message.id) {
727 highestMessageId = Math.max(highestMessageId, message.id);
728 processedMessageIds.add(message.id); // Add all message IDs to processed set
729 }
730 });
731
732 $chatBox.append($fragment);
733 scrollToBottom(true);
734
735 if (response.data.conversation.length > 0) {
736 $('#mxchat-popular-questions').hide();
737 }
738
739 // Update lastSeenMessageId after history loads
740 lastSeenMessageId = highestMessageId;
741
742 // Only update chat mode if persistence is enabled
743 if (chatPersistenceEnabled && response.data.conversation.length > 0) {
744 var lastMessage = response.data.conversation[response.data.conversation.length - 1];
745 if (lastMessage.role === 'agent') {
746 updateChatModeIndicator('agent');
747 }
748 }
749 } else {
750 console.warn("No conversation history found.");
751 }
752 },
753 error: function(xhr, status, error) {
754 console.error("Error loading chat history:", status, error);
755 appendMessage("bot", "Unable to load chat history.");
756 }
757 });
758 } else {
759 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
760 }
761 }
762
763 // Function to decode HTML entities
764 function decodeHTMLEntities(text) {
765 var textArea = document.createElement('textarea');
766 textArea.innerHTML = text;
767 return textArea.value;
768 }
769
770
771 // Update formatCodeBlocks function
772 function formatCodeBlocks(text) {
773 // First handle raw PHP tags
774 text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
775 return `<pre><code class="language-php">${escapeHtml(match)}</code></pre>`;
776 });
777
778 // Then handle code blocks with backticks
779 text = text.replace(/```php5?\n([\s\S]+?)```/gi, (match, code) => {
780 return `<pre><code class="language-php">${escapeHtml(code)}</code></pre>`;
781 });
782
783 return text;
784 }
785
786 // Update escapeHtml function to preserve existing code blocks
787 function escapeHtml(unsafe) {
788 // First check if it's already a code block
789 if (unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
790 return unsafe;
791 }
792
793 return unsafe
794 .replace(/&/g, "&amp;")
795 .replace(/</g, "&lt;")
796 .replace(/>/g, "&gt;")
797 .replace(/"/g, "&quot;")
798 .replace(/'/g, "&#039;");
799 }
800 // Utility function to escape HTML
801 function escapeHtml(unsafe) {
802 return unsafe
803 .replace(/&/g, "&amp;")
804 .replace(/</g, "&lt;")
805 .replace(/>/g, "&gt;")
806 .replace(/"/g, "&quot;")
807 .replace(/'/g, "&#039;");
808 }
809
810
811
812
813 // Function to convert newlines, skipping preformatted text
814 function convertNewlinesToBreaks(text) {
815 // Split while preserving code blocks
816 return text.split(/(<pre\b[^>]*>[\s\S]*?<\/pre>)/g).map(part => {
817 if (part.startsWith('<pre')) return part;
818 return part.replace(/(^|[^>])\n/g, '$1<br>');
819 }).join('');
820 }
821
822
823
824
825 // Helper function to check if a string is an image HTML
826 function isImageHtml(str) {
827 return str.startsWith('<img') && str.endsWith('>');
828 }
829
830 // Function to remove thinking dots
831 function removeThinkingDots() {
832 $('.thinking-dots').closest('.temporary-message').remove();
833 }
834
835 function isMobile() {
836 // This can be a simple check, or more sophisticated detection of mobile devices
837 return window.innerWidth <= 768; // Example threshold for mobile devices
838 }
839
840 function disableScroll() {
841 if (isMobile()) {
842 $('body').css('overflow', 'hidden');
843 }
844 }
845
846 function enableScroll() {
847 if (isMobile()) {
848 $('body').css('overflow', '');
849 }
850 }
851
852 // Pre-chat dismissal check function (wrapped in a function for reuse)
853 function checkPreChatDismissal() {
854 $.ajax({
855 url: mxchatChat.ajax_url,
856 type: 'POST',
857 data: {
858 action: 'mxchat_check_pre_chat_message_status',
859 _ajax_nonce: mxchatChat.nonce
860 },
861 success: function(response) {
862 if (response.success && !response.data.dismissed) {
863 $('#pre-chat-message').fadeIn(250);
864 } else {
865 $('#pre-chat-message').hide();
866 }
867 },
868 error: function() {
869 console.error('Failed to check pre-chat message dismissal status.');
870 }
871 });
872 }
873
874 // Function to show the chatbot widget
875 function showChatWidget() {
876 // First ensure display is set
877 $('#floating-chatbot-button').css('display', 'flex');
878 // Then handle the fade
879 $('#floating-chatbot-button').fadeTo(500, 1);
880 // Force visibility
881 $('#floating-chatbot-button').removeClass('hidden');
882 //console.log('Showing widget');
883 }
884
885 // Function to hide the chatbot widget
886 function hideChatWidget() {
887 $('#floating-chatbot-button').css('display', 'none');
888 $('#floating-chatbot-button').addClass('hidden');
889 //console.log('Hiding widget');
890 }
891
892 function initializeChatVisibility() {
893 //console.log('Initializing chat visibility');
894 const complianzEnabled = mxchatChat.complianz_toggle === 'on' ||
895 mxchatChat.complianz_toggle === '1' ||
896 mxchatChat.complianz_toggle === 1;
897
898 if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
899 // Initial check
900 checkConsentAndShowChat();
901
902 // Listen for consent changes
903 $(document).on('cmplz_status_change', function(event) {
904 //console.log('Status change detected');
905 checkConsentAndShowChat();
906 });
907 } else {
908 // If Complianz is not enabled, always show
909 $('#floating-chatbot-button')
910 .css('display', 'flex')
911 .removeClass('hidden no-consent')
912 .fadeTo(500, 1);
913
914 // Also check pre-chat message when Complianz is not enabled
915 checkPreChatDismissal();
916 }
917 }
918
919
920
921 function checkConsentAndShowChat() {
922 var consentStatus = cmplz_has_consent('marketing');
923 var consentType = complianz.consenttype;
924
925 //console.log('Checking consent:', {status: consentStatus,type: consentType});
926
927 let $widget = $('#floating-chatbot-button');
928 let $chatbot = $('#floating-chatbot');
929 let $preChat = $('#pre-chat-message');
930
931 if (consentStatus === true) {
932 //console.log('Consent granted - showing widget');
933 $widget
934 .removeClass('no-consent')
935 .css('display', 'flex')
936 .removeClass('hidden')
937 .fadeTo(500, 1);
938 $chatbot.removeClass('no-consent');
939
940 // Show pre-chat message if not dismissed
941 checkPreChatDismissal();
942 } else {
943 //console.log('No consent - hiding widget');
944 $widget
945 .addClass('no-consent')
946 .fadeTo(500, 0, function() {
947 $(this)
948 .css('display', 'none')
949 .addClass('hidden');
950 });
951 $chatbot.addClass('no-consent');
952
953 // Hide pre-chat message when no consent
954 $preChat.hide();
955 }
956 }
957
958 // Function to dismiss pre-chat message for 24 hours
959 function handlePreChatDismissal() {
960 $('#pre-chat-message').fadeOut(200);
961 $.ajax({
962 url: mxchatChat.ajax_url,
963 type: 'POST',
964 data: {
965 action: 'mxchat_dismiss_pre_chat_message',
966 _ajax_nonce: mxchatChat.nonce
967 },
968 success: function() {
969 $('#pre-chat-message').hide();
970 },
971 error: function() {
972 console.error('Failed to dismiss pre-chat message.');
973 }
974 });
975 }
976
977 // Handle pre-chat message dismissal on button click
978 $(document).on('click', '.close-pre-chat-message', function(e) {
979 e.stopPropagation();
980 handlePreChatDismissal();
981 });
982
983 // Toggle chatbot visibility on floating button click
984 $(document).on('click', '#floating-chatbot-button', function() {
985 var chatbot = $('#floating-chatbot');
986 if (chatbot.hasClass('hidden')) {
987 chatbot.removeClass('hidden').addClass('visible');
988 $(this).addClass('hidden');
989 $('#chat-notification-badge').hide(); // Hide notification when opening chat
990 disableScroll();
991 $('#pre-chat-message').fadeOut(250);
992 } else {
993 chatbot.removeClass('visible').addClass('hidden');
994 $(this).removeClass('hidden');
995 enableScroll();
996 checkPreChatDismissal();
997 }
998 });
999
1000 $(document).on('click', '#exit-chat-button', function() {
1001 $('#floating-chatbot').addClass('hidden').removeClass('visible');
1002 $('#floating-chatbot-button').removeClass('hidden');
1003 enableScroll();
1004 });
1005
1006 // Close pre-chat message on click
1007 $(document).on('click', '.close-pre-chat-message', function(e) {
1008 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
1009 $('#pre-chat-message').fadeOut(200, function() {
1010 $(this).remove();
1011 });
1012 });
1013
1014 // Open chatbot when pre-chat message is clicked
1015 $(document).on('click', '#pre-chat-message', function() {
1016 var chatbot = $('#floating-chatbot');
1017 if (chatbot.hasClass('hidden')) {
1018 chatbot.removeClass('hidden').addClass('visible');
1019 $('#floating-chatbot-button').addClass('hidden');
1020 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
1021 disableScroll(); // Disable scroll when chatbot opens
1022 }
1023 });
1024
1025 // If the chatbot is initially hidden, ensure the button is visible
1026 if ($('#floating-chatbot').hasClass('hidden')) {
1027 $('#floating-chatbot-button').removeClass('hidden');
1028 }
1029
1030 function setFullHeight() {
1031 var vh = $(window).innerHeight() * 0.01;
1032 $(':root').css('--vh', vh + 'px');
1033 }
1034
1035 // Set the height when the page loads
1036
1037
1038 // Set the height on resize and orientation change events
1039 $(window).on('resize orientationchange', function() {
1040 setFullHeight();
1041 });
1042
1043
1044 // Now handle the close button to dismiss the pre-chat message for 24 hours
1045 var closeButton = document.querySelector('.close-pre-chat-message');
1046 if (closeButton) {
1047 closeButton.addEventListener('click', function() {
1048 $('#pre-chat-message').fadeOut(200); // Hide the message
1049
1050 // Send an AJAX request to set the transient flag for 24 hours
1051 $.ajax({
1052 url: mxchatChat.ajax_url,
1053 type: 'POST',
1054 data: {
1055 action: 'mxchat_dismiss_pre_chat_message',
1056 _ajax_nonce: mxchatChat.nonce
1057 },
1058 success: function() {
1059 //console.log('Pre-chat message dismissed for 24 hours.');
1060
1061 // Ensure the message is hidden after dismissal
1062 $('#pre-chat-message').hide();
1063 },
1064 error: function() {
1065 //console.error('Failed to dismiss pre-chat message.');
1066 }
1067 });
1068 });
1069 }
1070
1071
1072
1073
1074 // Event listener for Add to Cart button
1075 $(document).on('click', '.mxchat-add-to-cart-button', function() {
1076 var productId = $(this).data('product-id');
1077 // Add a special prefix to indicate this is from button
1078 appendMessage("user", "add to cart");
1079 sendMessageToChatbot("!addtocart"); // Special command to indicate button click
1080 });
1081
1082
1083 if (document.getElementById('pdf-upload-btn')) {
1084 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
1085 document.getElementById('pdf-upload').click();
1086 });
1087 }
1088
1089 if (document.getElementById('word-upload-btn')) {
1090 document.getElementById('word-upload-btn').addEventListener('click', function() {
1091 document.getElementById('word-upload').click();
1092 });
1093 }
1094
1095 // PDF file input change handler
1096 document.getElementById('pdf-upload').addEventListener('change', async function(e) {
1097 const file = e.target.files[0];
1098
1099 if (!file || file.type !== 'application/pdf') {
1100 alert('Please select a valid PDF file.');
1101 return;
1102 }
1103
1104 if (!sessionId) {
1105 console.error('No session ID found');
1106 alert('Error: No session ID found');
1107 return;
1108 }
1109
1110 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
1111 console.error('mxchatChat not properly configured:', mxchatChat);
1112 alert('Error: Ajax configuration missing');
1113 return;
1114 }
1115
1116 // Disable buttons and show loading state
1117 const uploadBtn = document.getElementById('pdf-upload-btn');
1118 const sendBtn = document.getElementById('send-button');
1119 const originalBtnContent = uploadBtn.innerHTML;
1120
1121 try {
1122 const formData = new FormData();
1123 formData.append('action', 'mxchat_upload_pdf');
1124 formData.append('pdf_file', file);
1125 formData.append('session_id', sessionId);
1126 formData.append('nonce', mxchatChat.nonce);
1127
1128 uploadBtn.disabled = true;
1129 sendBtn.disabled = true;
1130 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1131 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1132 </svg>`;
1133
1134 const response = await fetch(mxchatChat.ajax_url, {
1135 method: 'POST',
1136 body: formData
1137 });
1138
1139 const data = await response.json();
1140
1141 if (data.success) {
1142 // Hide popular questions if they exist
1143 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1144 if (popularQuestionsContainer) {
1145 popularQuestionsContainer.style.display = 'none';
1146 }
1147
1148 // Show the active PDF name
1149 showActivePdf(data.data.filename);
1150
1151 appendMessage('bot', data.data.message);
1152 scrollToBottom();
1153 activePdfFile = data.data.filename;
1154 } else {
1155 console.error('Upload failed:', data.data);
1156 alert('Failed to upload PDF. Please try again.');
1157 }
1158 } catch (error) {
1159 console.error('Upload error:', error);
1160 alert('Error uploading file. Please try again.');
1161 } finally {
1162 uploadBtn.disabled = false;
1163 sendBtn.disabled = false;
1164 uploadBtn.innerHTML = originalBtnContent;
1165 this.value = ''; // Reset file input
1166 }
1167 });
1168
1169 // Word file input change handler
1170 document.getElementById('word-upload').addEventListener('change', async function(e) {
1171 const file = e.target.files[0];
1172
1173 if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1174 alert('Please select a valid Word document (.docx).');
1175 return;
1176 }
1177
1178 if (!sessionId) {
1179 console.error('No session ID found');
1180 alert('Error: No session ID found');
1181 return;
1182 }
1183
1184 // Disable buttons and show loading state
1185 const uploadBtn = document.getElementById('word-upload-btn');
1186 const sendBtn = document.getElementById('send-button');
1187 const originalBtnContent = uploadBtn.innerHTML;
1188
1189 try {
1190 const formData = new FormData();
1191 formData.append('action', 'mxchat_upload_word');
1192 formData.append('word_file', file);
1193 formData.append('session_id', sessionId);
1194 formData.append('nonce', mxchatChat.nonce);
1195
1196 uploadBtn.disabled = true;
1197 sendBtn.disabled = true;
1198 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1199 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1200 </svg>`;
1201
1202 const response = await fetch(mxchatChat.ajax_url, {
1203 method: 'POST',
1204 body: formData
1205 });
1206
1207 const data = await response.json();
1208
1209 if (data.success) {
1210 // Hide popular questions if they exist
1211 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1212 if (popularQuestionsContainer) {
1213 popularQuestionsContainer.style.display = 'none';
1214 }
1215
1216 // Show the active Word document name
1217 showActiveWord(data.data.filename);
1218
1219 appendMessage('bot', data.data.message);
1220 scrollToBottom();
1221 activeWordFile = data.data.filename;
1222 } else {
1223 console.error('Upload failed:', data.data);
1224 alert('Failed to upload Word document. Please try again.');
1225 }
1226 } catch (error) {
1227 console.error('Upload error:', error);
1228 alert('Error uploading file. Please try again.');
1229 } finally {
1230 uploadBtn.disabled = false;
1231 sendBtn.disabled = false;
1232 uploadBtn.innerHTML = originalBtnContent;
1233 this.value = ''; // Reset file input
1234 }
1235 });
1236
1237 // Function to show active PDF name in toolbar
1238 function showActivePdf(filename) {
1239 const container = document.getElementById('active-pdf-container');
1240 const nameElement = document.getElementById('active-pdf-name');
1241
1242 if (!container || !nameElement) {
1243 console.error('PDF container elements not found');
1244 return;
1245 }
1246
1247 nameElement.textContent = filename;
1248 container.style.display = 'flex';
1249 }
1250
1251 // Function to show active Word document name in toolbar
1252 function showActiveWord(filename) {
1253 const container = document.getElementById('active-word-container');
1254 const nameElement = document.getElementById('active-word-name');
1255
1256 if (!container || !nameElement) {
1257 console.error('Word document container elements not found');
1258 return;
1259 }
1260
1261 nameElement.textContent = filename;
1262 container.style.display = 'flex';
1263 }
1264
1265 // Function to remove active PDF
1266 function removeActivePdf() {
1267 const container = document.getElementById('active-pdf-container');
1268 const nameElement = document.getElementById('active-pdf-name');
1269
1270 if (!container || !nameElement || !activePdfFile) return;
1271
1272 fetch(mxchatChat.ajax_url, {
1273 method: 'POST',
1274 headers: {
1275 'Content-Type': 'application/x-www-form-urlencoded',
1276 },
1277 body: new URLSearchParams({
1278 'action': 'mxchat_remove_pdf',
1279 'session_id': sessionId,
1280 'nonce': mxchatChat.nonce
1281 })
1282 })
1283 .then(response => response.json())
1284 .then(data => {
1285 if (data.success) {
1286 container.style.display = 'none';
1287 nameElement.textContent = '';
1288 activePdfFile = null;
1289 appendMessage('bot', 'PDF removed.');
1290 }
1291 })
1292 .catch(error => {
1293 console.error('Error removing PDF:', error);
1294 });
1295 }
1296
1297 // Function to remove active Word document
1298 function removeActiveWord() {
1299 const container = document.getElementById('active-word-container');
1300 const nameElement = document.getElementById('active-word-name');
1301
1302 if (!container || !nameElement || !activeWordFile) return;
1303
1304 fetch(mxchatChat.ajax_url, {
1305 method: 'POST',
1306 headers: {
1307 'Content-Type': 'application/x-www-form-urlencoded',
1308 },
1309 body: new URLSearchParams({
1310 'action': 'mxchat_remove_word',
1311 'session_id': sessionId,
1312 'nonce': mxchatChat.nonce
1313 })
1314 })
1315 .then(response => response.json())
1316 .then(data => {
1317 if (data.success) {
1318 container.style.display = 'none';
1319 nameElement.textContent = '';
1320 activeWordFile = null;
1321 appendMessage('bot', 'Word document removed.');
1322 }
1323 })
1324 .catch(error => {
1325 console.error('Error removing Word document:', error);
1326 });
1327 }
1328
1329 // Add remove button click handlers
1330 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1331 e.preventDefault();
1332 e.stopPropagation();
1333 removeActivePdf();
1334 });
1335
1336 document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
1337 e.preventDefault();
1338 e.stopPropagation();
1339 removeActiveWord();
1340 });
1341
1342 // Check initial document status
1343 function checkInitialDocumentStatus() {
1344 if (!sessionId) return;
1345
1346 // Check PDF status
1347 fetch(mxchatChat.ajax_url, {
1348 method: 'POST',
1349 headers: {
1350 'Content-Type': 'application/x-www-form-urlencoded',
1351 },
1352 body: new URLSearchParams({
1353 'action': 'mxchat_check_pdf_status',
1354 'session_id': sessionId,
1355 'nonce': mxchatChat.nonce
1356 })
1357 })
1358 .then(response => response.json())
1359 .then(data => {
1360 if (data.success && data.data.filename) {
1361 showActivePdf(data.data.filename);
1362 activePdfFile = data.data.filename;
1363 }
1364 })
1365 .catch(error => {
1366 console.error('Error checking PDF status:', error);
1367 });
1368
1369 // Check Word document status
1370 fetch(mxchatChat.ajax_url, {
1371 method: 'POST',
1372 headers: {
1373 'Content-Type': 'application/x-www-form-urlencoded',
1374 },
1375 body: new URLSearchParams({
1376 'action': 'mxchat_check_word_status',
1377 'session_id': sessionId,
1378 'nonce': mxchatChat.nonce
1379 })
1380 })
1381 .then(response => response.json())
1382 .then(data => {
1383 if (data.success && data.data.filename) {
1384 showActiveWord(data.data.filename);
1385 activeWordFile = data.data.filename;
1386 }
1387 })
1388 .catch(error => {
1389 console.error('Error checking Word document status:', error);
1390 });
1391 }
1392
1393 // Apply toolbar settings
1394 if (mxchatChat.chat_toolbar_toggle === 'on') {
1395 $('.chat-toolbar').show();
1396 } else {
1397 $('.chat-toolbar').hide();
1398 }
1399
1400 // Initialize on page load
1401 document.addEventListener('DOMContentLoaded', function() {
1402 checkInitialDocumentStatus();
1403 });
1404
1405 // Style all toolbar elements
1406 const toolbarElements = [
1407 '#mxchat-chatbot .toolbar-btn svg',
1408 '#mxchat-chatbot .active-pdf-name',
1409 '#mxchat-chatbot .active-word-name',
1410 '#mxchat-chatbot .remove-pdf-btn svg',
1411 '#mxchat-chatbot .remove-word-btn svg'
1412 ];
1413 $(toolbarElements.join(', ')).css({
1414 'fill': toolbarIconColor,
1415 'color': toolbarIconColor
1416 });
1417
1418
1419
1420 // Ensure essential elements are defined
1421 const emailForm = document.getElementById('email-collection-form');
1422 const emailBlocker = document.getElementById('email-blocker');
1423 const chatbotWrapper = document.getElementById('chat-container');
1424
1425 if (emailForm && emailBlocker && chatbotWrapper) {
1426 // Check if email exists for the current session
1427 function checkSessionAndEmail() {
1428 const sessionId = getChatSession();
1429 //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);
1430
1431 fetch(mxchatChat.ajax_url, {
1432 method: 'POST',
1433 headers: {
1434 'Content-Type': 'application/x-www-form-urlencoded',
1435 },
1436 body: new URLSearchParams({
1437 action: 'mxchat_check_email_provided',
1438 session_id: sessionId,
1439 nonce: mxchatChat.nonce,
1440 }),
1441 })
1442 .then((response) => response.json())
1443 .then((data) => {
1444 //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
1445
1446 if (data.success) {
1447 if (data.data.logged_in) {
1448 //console.log("[DEBUG JS] User is logged in. Hiding email form.");
1449 emailBlocker.style.display = 'none';
1450 chatbotWrapper.style.display = 'flex';
1451 } else if (data.data.email) {
1452 //console.log("[DEBUG JS] Email found for session. Hiding email form.");
1453 emailBlocker.style.display = 'none';
1454 chatbotWrapper.style.display = 'flex';
1455 } else {
1456 //console.log("[DEBUG JS] No email provided. Showing email form.");
1457 emailBlocker.style.display = 'flex';
1458 chatbotWrapper.style.display = 'none';
1459 }
1460 } else {
1461 //console.log("[DEBUG JS] Error or no data received. Showing email form.");
1462 emailBlocker.style.display = 'flex';
1463 chatbotWrapper.style.display = 'none';
1464 }
1465 })
1466 .catch((error) => {
1467 // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
1468 emailBlocker.style.display = 'flex';
1469 chatbotWrapper.style.display = 'none';
1470 });
1471 }
1472
1473
1474
1475 // Handle email form submission
1476 emailForm.addEventListener('submit', function (event) {
1477 event.preventDefault();
1478 const userEmail = document.getElementById('user-email').value;
1479 const sessionId = getChatSession();
1480
1481 if (userEmail) {
1482 fetch(mxchatChat.ajax_url, {
1483 method: 'POST',
1484 headers: {
1485 'Content-Type': 'application/x-www-form-urlencoded',
1486 },
1487 body: new URLSearchParams({
1488 action: 'mxchat_handle_save_email_and_response',
1489 email: userEmail,
1490 session_id: sessionId,
1491 nonce: mxchatChat.nonce,
1492 }),
1493 })
1494 .then((response) => response.json())
1495 .then((data) => {
1496 //console.log('Backend response:', data);
1497 if (data.success) {
1498 //console.log('Email saved successfully:', userEmail);
1499 emailBlocker.style.display = 'none';
1500 chatbotWrapper.style.display = 'flex';
1501
1502 // Optionally handle bot response
1503 if (data.message) {
1504 appendMessage('bot', data.message);
1505 scrollToBottom();
1506 }
1507 } else {
1508 console.error('Error saving email:', data.message || 'Unknown error');
1509 }
1510 })
1511 .catch((error) => {
1512 console.error('AJAX error:', error);
1513 });
1514 }
1515 });
1516
1517 // Check session and email status on page load
1518 checkSessionAndEmail();
1519 } else {
1520 console.error('Essential elements for email handling are missing.');
1521 }
1522
1523
1524 // Initialize when document is ready
1525 $(document).ready(function() {
1526 setFullHeight();
1527 initializeChatVisibility();
1528 loadChatHistory();
1529
1530 });
1531
1532 });
1533
1534 // Event listener for copy button
1535 document.addEventListener("click", (e) => {
1536 if (e.target.classList.contains("mxchat-copy-button")) {
1537 const copyButton = e.target;
1538 const codeBlock = copyButton
1539 .closest(".mxchat-code-block-container")
1540 .querySelector(".mxchat-code-block code");
1541
1542 if (codeBlock) {
1543 // Preserve formatting using innerText
1544 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1545 copyButton.textContent = "Copied!";
1546 copyButton.setAttribute("aria-label", "Copied to clipboard");
1547
1548 setTimeout(() => {
1549 copyButton.textContent = "Copy";
1550 copyButton.setAttribute("aria-label", "Copy to clipboard");
1551 }, 2000);
1552 });
1553 }
1554 }
1555 });
1556
1557
1558
1559