PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.6.4
MxChat – AI Chatbot & Content Generation for WordPress v1.6.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 1.6.4, at js/chat-script.js

1,550 lines 52.5 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 // Use the linkTarget in your linkify function
261 function linkify(inputText) {
262 // Check for already linked URLs and skip them
263 // We use negative lookaheads to skip anything already in an <a> tag
264 var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
265 var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
266
267 // Replace standalone URLs not already in an <a> tag
268 var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
269 replacedText = replacedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
270
271 // Replace "www." prefixed URLs not already in an <a> tag
272 var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
273 replacedText = replacedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
274
275 return replacedText;
276 }
277
278
279 function scrollElementToTop(element) {
280 var chatBox = $('#chat-box');
281 var elementTop = element.position().top + chatBox.scrollTop();
282 chatBox.animate({ scrollTop: elementTop }, 500);
283 }
284
285
286 // Optimized scrollToBottom function for instant scrolling
287 function scrollToBottom(instant = false) {
288 var chatBox = $('#chat-box');
289 if (instant) {
290 // Instantly set the scroll position to the bottom
291 chatBox.scrollTop(chatBox.prop("scrollHeight"));
292 } else {
293 // Use requestAnimationFrame for smoother scrolling if needed
294 let start = null;
295 const scrollHeight = chatBox.prop("scrollHeight");
296 const initialScroll = chatBox.scrollTop();
297 const distance = scrollHeight - initialScroll;
298 const duration = 500; // Duration in ms
299
300 function smoothScroll(timestamp) {
301 if (!start) start = timestamp;
302 const progress = timestamp - start;
303 const currentScroll = initialScroll + (distance * (progress / duration));
304 chatBox.scrollTop(currentScroll);
305
306 if (progress < duration) {
307 requestAnimationFrame(smoothScroll);
308 } else {
309 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
310 }
311 }
312
313 requestAnimationFrame(smoothScroll);
314 }
315 }
316
317
318 // Function to format text with **bold** inside double asterisks
319 function formatBoldText(text) {
320 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
321 }
322
323 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
324 function convertNewlinesToBreaks(text) {
325 // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
326 const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
327
328 // Wrap each paragraph in <p> tags
329 return paragraphs
330 .map(para => `<p>${para.trim()}</p>`)
331 .join('');
332 }
333 // Copy to clipboard function
334 // Function to copy text to clipboard
335 function copyToClipboard(text) {
336 var tempInput = $('<input>');
337 $('body').append(tempInput);
338 tempInput.val(text).select();
339 document.execCommand('copy');
340 tempInput.remove();
341 }
342
343
344 function updateChatModeIndicator(mode) {
345 const indicator = document.getElementById('chat-mode-indicator');
346 if (indicator) {
347 indicator.textContent = mode === 'agent' ? 'Live Agent' : 'AI Agent';
348 }
349
350 // Start or stop polling based on mode
351 if (mode === 'agent') {
352 startPolling();
353 } else {
354 stopPolling();
355 }
356 }
357
358 function callMxChat(message, callback) {
359 $.ajax({
360 url: mxchatChat.ajax_url,
361 type: 'POST',
362 dataType: 'json',
363 data: {
364 action: 'mxchat_handle_chat_request',
365 message: message,
366 session_id: getChatSession(),
367 nonce: mxchatChat.nonce
368 },
369 success: function(response) {
370 // Existing chat mode check
371 if (response.chat_mode) {
372 updateChatModeIndicator(response.chat_mode);
373 }
374 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
375 updateChatModeIndicator(response.fallbackResponse.chat_mode);
376 }
377
378 // Add PDF filename handling
379 if (response.data && response.data.filename) {
380 showActivePdf(response.data.filename);
381 activePdfFile = response.data.filename;
382 }
383
384 // Add redirect check here
385 if (response.redirect_url) {
386 let responseText = response.text || '';
387 if (responseText) {
388 replaceLastMessage("bot", responseText);
389 }
390 setTimeout(() => {
391 window.location.href = response.redirect_url;
392 }, 1500);
393 return;
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 function formatCodeBlocks(text) {
772 // Ensure the input is a string; otherwise, convert or return empty
773 if (typeof text !== 'string') {
774 console.error("formatCodeBlocks: Input is not a string:", text);
775 return typeof text === 'object' && text.text ? text.text : ""; // Use .text if available, else empty
776 }
777
778 const codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
779
780 return text.replace(codeBlockPattern, (_, language, codeContent) => {
781 language = language || 'plaintext';
782
783 return `
784 <div class="mxchat-code-block-container">
785 <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
786 <pre class="mxchat-code-block"><code class="mxchat-language-${language}">${escapeHtml(codeContent)}</code></pre>
787 </div>`;
788 });
789 }
790
791
792 // Utility function to escape HTML
793 function escapeHtml(unsafe) {
794 return unsafe
795 .replace(/&/g, "&amp;")
796 .replace(/</g, "&lt;")
797 .replace(/>/g, "&gt;")
798 .replace(/"/g, "&quot;")
799 .replace(/'/g, "&#039;");
800 }
801
802
803
804
805 // Function to convert newlines, skipping preformatted text
806 function convertNewlinesToBreaks(text) {
807 // Regex to exclude <pre> and <code> tags from adding <br> tags
808 return text.replace(/(^|[^>])\n/g, '$1<br>');
809 }
810
811
812
813
814
815
816 // Helper function to check if a string is an image HTML
817 function isImageHtml(str) {
818 return str.startsWith('<img') && str.endsWith('>');
819 }
820
821 // Function to remove thinking dots
822 function removeThinkingDots() {
823 $('.thinking-dots').closest('.temporary-message').remove();
824 }
825
826 function isMobile() {
827 // This can be a simple check, or more sophisticated detection of mobile devices
828 return window.innerWidth <= 768; // Example threshold for mobile devices
829 }
830
831 function disableScroll() {
832 if (isMobile()) {
833 $('body').css('overflow', 'hidden');
834 }
835 }
836
837 function enableScroll() {
838 if (isMobile()) {
839 $('body').css('overflow', '');
840 }
841 }
842
843 // Pre-chat dismissal check function (wrapped in a function for reuse)
844 function checkPreChatDismissal() {
845 $.ajax({
846 url: mxchatChat.ajax_url,
847 type: 'POST',
848 data: {
849 action: 'mxchat_check_pre_chat_message_status',
850 _ajax_nonce: mxchatChat.nonce
851 },
852 success: function(response) {
853 if (response.success && !response.data.dismissed) {
854 $('#pre-chat-message').fadeIn(250);
855 } else {
856 $('#pre-chat-message').hide();
857 }
858 },
859 error: function() {
860 console.error('Failed to check pre-chat message dismissal status.');
861 }
862 });
863 }
864
865 // Function to show the chatbot widget
866 function showChatWidget() {
867 // First ensure display is set
868 $('#floating-chatbot-button').css('display', 'flex');
869 // Then handle the fade
870 $('#floating-chatbot-button').fadeTo(500, 1);
871 // Force visibility
872 $('#floating-chatbot-button').removeClass('hidden');
873 //console.log('Showing widget');
874 }
875
876 // Function to hide the chatbot widget
877 function hideChatWidget() {
878 $('#floating-chatbot-button').css('display', 'none');
879 $('#floating-chatbot-button').addClass('hidden');
880 //console.log('Hiding widget');
881 }
882
883 function initializeChatVisibility() {
884 //console.log('Initializing chat visibility');
885 const complianzEnabled = mxchatChat.complianz_toggle === 'on' ||
886 mxchatChat.complianz_toggle === '1' ||
887 mxchatChat.complianz_toggle === 1;
888
889 if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
890 // Initial check
891 checkConsentAndShowChat();
892
893 // Listen for consent changes
894 $(document).on('cmplz_status_change', function(event) {
895 //console.log('Status change detected');
896 checkConsentAndShowChat();
897 });
898 } else {
899 // If Complianz is not enabled, always show
900 $('#floating-chatbot-button')
901 .css('display', 'flex')
902 .removeClass('hidden no-consent')
903 .fadeTo(500, 1);
904
905 // Also check pre-chat message when Complianz is not enabled
906 checkPreChatDismissal();
907 }
908 }
909
910
911
912 function checkConsentAndShowChat() {
913 var consentStatus = cmplz_has_consent('marketing');
914 var consentType = complianz.consenttype;
915
916 //console.log('Checking consent:', {status: consentStatus,type: consentType});
917
918 let $widget = $('#floating-chatbot-button');
919 let $chatbot = $('#floating-chatbot');
920 let $preChat = $('#pre-chat-message');
921
922 if (consentStatus === true) {
923 //console.log('Consent granted - showing widget');
924 $widget
925 .removeClass('no-consent')
926 .css('display', 'flex')
927 .removeClass('hidden')
928 .fadeTo(500, 1);
929 $chatbot.removeClass('no-consent');
930
931 // Show pre-chat message if not dismissed
932 checkPreChatDismissal();
933 } else {
934 //console.log('No consent - hiding widget');
935 $widget
936 .addClass('no-consent')
937 .fadeTo(500, 0, function() {
938 $(this)
939 .css('display', 'none')
940 .addClass('hidden');
941 });
942 $chatbot.addClass('no-consent');
943
944 // Hide pre-chat message when no consent
945 $preChat.hide();
946 }
947 }
948
949 // Function to dismiss pre-chat message for 24 hours
950 function handlePreChatDismissal() {
951 $('#pre-chat-message').fadeOut(200);
952 $.ajax({
953 url: mxchatChat.ajax_url,
954 type: 'POST',
955 data: {
956 action: 'mxchat_dismiss_pre_chat_message',
957 _ajax_nonce: mxchatChat.nonce
958 },
959 success: function() {
960 $('#pre-chat-message').hide();
961 },
962 error: function() {
963 console.error('Failed to dismiss pre-chat message.');
964 }
965 });
966 }
967
968 // Handle pre-chat message dismissal on button click
969 $(document).on('click', '.close-pre-chat-message', function(e) {
970 e.stopPropagation();
971 handlePreChatDismissal();
972 });
973
974 // Toggle chatbot visibility on floating button click
975 $(document).on('click', '#floating-chatbot-button', function() {
976 var chatbot = $('#floating-chatbot');
977 if (chatbot.hasClass('hidden')) {
978 chatbot.removeClass('hidden').addClass('visible');
979 $(this).addClass('hidden');
980 $('#chat-notification-badge').hide(); // Hide notification when opening chat
981 disableScroll();
982 $('#pre-chat-message').fadeOut(250);
983 } else {
984 chatbot.removeClass('visible').addClass('hidden');
985 $(this).removeClass('hidden');
986 enableScroll();
987 checkPreChatDismissal();
988 }
989 });
990
991 $(document).on('click', '#exit-chat-button', function() {
992 $('#floating-chatbot').addClass('hidden').removeClass('visible');
993 $('#floating-chatbot-button').removeClass('hidden');
994 enableScroll();
995 });
996
997 // Close pre-chat message on click
998 $(document).on('click', '.close-pre-chat-message', function(e) {
999 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
1000 $('#pre-chat-message').fadeOut(200, function() {
1001 $(this).remove();
1002 });
1003 });
1004
1005 // Open chatbot when pre-chat message is clicked
1006 $(document).on('click', '#pre-chat-message', function() {
1007 var chatbot = $('#floating-chatbot');
1008 if (chatbot.hasClass('hidden')) {
1009 chatbot.removeClass('hidden').addClass('visible');
1010 $('#floating-chatbot-button').addClass('hidden');
1011 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
1012 disableScroll(); // Disable scroll when chatbot opens
1013 }
1014 });
1015
1016 // If the chatbot is initially hidden, ensure the button is visible
1017 if ($('#floating-chatbot').hasClass('hidden')) {
1018 $('#floating-chatbot-button').removeClass('hidden');
1019 }
1020
1021 function setFullHeight() {
1022 var vh = $(window).innerHeight() * 0.01;
1023 $(':root').css('--vh', vh + 'px');
1024 }
1025
1026 // Set the height when the page loads
1027
1028
1029 // Set the height on resize and orientation change events
1030 $(window).on('resize orientationchange', function() {
1031 setFullHeight();
1032 });
1033
1034
1035 // Now handle the close button to dismiss the pre-chat message for 24 hours
1036 var closeButton = document.querySelector('.close-pre-chat-message');
1037 if (closeButton) {
1038 closeButton.addEventListener('click', function() {
1039 $('#pre-chat-message').fadeOut(200); // Hide the message
1040
1041 // Send an AJAX request to set the transient flag for 24 hours
1042 $.ajax({
1043 url: mxchatChat.ajax_url,
1044 type: 'POST',
1045 data: {
1046 action: 'mxchat_dismiss_pre_chat_message',
1047 _ajax_nonce: mxchatChat.nonce
1048 },
1049 success: function() {
1050 //console.log('Pre-chat message dismissed for 24 hours.');
1051
1052 // Ensure the message is hidden after dismissal
1053 $('#pre-chat-message').hide();
1054 },
1055 error: function() {
1056 //console.error('Failed to dismiss pre-chat message.');
1057 }
1058 });
1059 });
1060 }
1061
1062
1063
1064
1065 // Event listener for Add to Cart button
1066 $(document).on('click', '.mxchat-add-to-cart-button', function() {
1067 var productId = $(this).data('product-id');
1068 // Add a special prefix to indicate this is from button
1069 appendMessage("user", "add to cart");
1070 sendMessageToChatbot("!addtocart"); // Special command to indicate button click
1071 });
1072
1073
1074 if (document.getElementById('pdf-upload-btn')) {
1075 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
1076 document.getElementById('pdf-upload').click();
1077 });
1078 }
1079
1080 if (document.getElementById('word-upload-btn')) {
1081 document.getElementById('word-upload-btn').addEventListener('click', function() {
1082 document.getElementById('word-upload').click();
1083 });
1084 }
1085
1086 // PDF file input change handler
1087 document.getElementById('pdf-upload').addEventListener('change', async function(e) {
1088 const file = e.target.files[0];
1089
1090 if (!file || file.type !== 'application/pdf') {
1091 alert('Please select a valid PDF file.');
1092 return;
1093 }
1094
1095 if (!sessionId) {
1096 console.error('No session ID found');
1097 alert('Error: No session ID found');
1098 return;
1099 }
1100
1101 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
1102 console.error('mxchatChat not properly configured:', mxchatChat);
1103 alert('Error: Ajax configuration missing');
1104 return;
1105 }
1106
1107 // Disable buttons and show loading state
1108 const uploadBtn = document.getElementById('pdf-upload-btn');
1109 const sendBtn = document.getElementById('send-button');
1110 const originalBtnContent = uploadBtn.innerHTML;
1111
1112 try {
1113 const formData = new FormData();
1114 formData.append('action', 'mxchat_upload_pdf');
1115 formData.append('pdf_file', file);
1116 formData.append('session_id', sessionId);
1117 formData.append('nonce', mxchatChat.nonce);
1118
1119 uploadBtn.disabled = true;
1120 sendBtn.disabled = true;
1121 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1122 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1123 </svg>`;
1124
1125 const response = await fetch(mxchatChat.ajax_url, {
1126 method: 'POST',
1127 body: formData
1128 });
1129
1130 const data = await response.json();
1131
1132 if (data.success) {
1133 // Hide popular questions if they exist
1134 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1135 if (popularQuestionsContainer) {
1136 popularQuestionsContainer.style.display = 'none';
1137 }
1138
1139 // Show the active PDF name
1140 showActivePdf(data.data.filename);
1141
1142 appendMessage('bot', data.data.message);
1143 scrollToBottom();
1144 activePdfFile = data.data.filename;
1145 } else {
1146 console.error('Upload failed:', data.data);
1147 alert('Failed to upload PDF. Please try again.');
1148 }
1149 } catch (error) {
1150 console.error('Upload error:', error);
1151 alert('Error uploading file. Please try again.');
1152 } finally {
1153 uploadBtn.disabled = false;
1154 sendBtn.disabled = false;
1155 uploadBtn.innerHTML = originalBtnContent;
1156 this.value = ''; // Reset file input
1157 }
1158 });
1159
1160 // Word file input change handler
1161 document.getElementById('word-upload').addEventListener('change', async function(e) {
1162 const file = e.target.files[0];
1163
1164 if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1165 alert('Please select a valid Word document (.docx).');
1166 return;
1167 }
1168
1169 if (!sessionId) {
1170 console.error('No session ID found');
1171 alert('Error: No session ID found');
1172 return;
1173 }
1174
1175 // Disable buttons and show loading state
1176 const uploadBtn = document.getElementById('word-upload-btn');
1177 const sendBtn = document.getElementById('send-button');
1178 const originalBtnContent = uploadBtn.innerHTML;
1179
1180 try {
1181 const formData = new FormData();
1182 formData.append('action', 'mxchat_upload_word');
1183 formData.append('word_file', file);
1184 formData.append('session_id', sessionId);
1185 formData.append('nonce', mxchatChat.nonce);
1186
1187 uploadBtn.disabled = true;
1188 sendBtn.disabled = true;
1189 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1190 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1191 </svg>`;
1192
1193 const response = await fetch(mxchatChat.ajax_url, {
1194 method: 'POST',
1195 body: formData
1196 });
1197
1198 const data = await response.json();
1199
1200 if (data.success) {
1201 // Hide popular questions if they exist
1202 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1203 if (popularQuestionsContainer) {
1204 popularQuestionsContainer.style.display = 'none';
1205 }
1206
1207 // Show the active Word document name
1208 showActiveWord(data.data.filename);
1209
1210 appendMessage('bot', data.data.message);
1211 scrollToBottom();
1212 activeWordFile = data.data.filename;
1213 } else {
1214 console.error('Upload failed:', data.data);
1215 alert('Failed to upload Word document. Please try again.');
1216 }
1217 } catch (error) {
1218 console.error('Upload error:', error);
1219 alert('Error uploading file. Please try again.');
1220 } finally {
1221 uploadBtn.disabled = false;
1222 sendBtn.disabled = false;
1223 uploadBtn.innerHTML = originalBtnContent;
1224 this.value = ''; // Reset file input
1225 }
1226 });
1227
1228 // Function to show active PDF name in toolbar
1229 function showActivePdf(filename) {
1230 const container = document.getElementById('active-pdf-container');
1231 const nameElement = document.getElementById('active-pdf-name');
1232
1233 if (!container || !nameElement) {
1234 console.error('PDF container elements not found');
1235 return;
1236 }
1237
1238 nameElement.textContent = filename;
1239 container.style.display = 'flex';
1240 }
1241
1242 // Function to show active Word document name in toolbar
1243 function showActiveWord(filename) {
1244 const container = document.getElementById('active-word-container');
1245 const nameElement = document.getElementById('active-word-name');
1246
1247 if (!container || !nameElement) {
1248 console.error('Word document container elements not found');
1249 return;
1250 }
1251
1252 nameElement.textContent = filename;
1253 container.style.display = 'flex';
1254 }
1255
1256 // Function to remove active PDF
1257 function removeActivePdf() {
1258 const container = document.getElementById('active-pdf-container');
1259 const nameElement = document.getElementById('active-pdf-name');
1260
1261 if (!container || !nameElement || !activePdfFile) return;
1262
1263 fetch(mxchatChat.ajax_url, {
1264 method: 'POST',
1265 headers: {
1266 'Content-Type': 'application/x-www-form-urlencoded',
1267 },
1268 body: new URLSearchParams({
1269 'action': 'mxchat_remove_pdf',
1270 'session_id': sessionId,
1271 'nonce': mxchatChat.nonce
1272 })
1273 })
1274 .then(response => response.json())
1275 .then(data => {
1276 if (data.success) {
1277 container.style.display = 'none';
1278 nameElement.textContent = '';
1279 activePdfFile = null;
1280 appendMessage('bot', 'PDF removed.');
1281 }
1282 })
1283 .catch(error => {
1284 console.error('Error removing PDF:', error);
1285 });
1286 }
1287
1288 // Function to remove active Word document
1289 function removeActiveWord() {
1290 const container = document.getElementById('active-word-container');
1291 const nameElement = document.getElementById('active-word-name');
1292
1293 if (!container || !nameElement || !activeWordFile) return;
1294
1295 fetch(mxchatChat.ajax_url, {
1296 method: 'POST',
1297 headers: {
1298 'Content-Type': 'application/x-www-form-urlencoded',
1299 },
1300 body: new URLSearchParams({
1301 'action': 'mxchat_remove_word',
1302 'session_id': sessionId,
1303 'nonce': mxchatChat.nonce
1304 })
1305 })
1306 .then(response => response.json())
1307 .then(data => {
1308 if (data.success) {
1309 container.style.display = 'none';
1310 nameElement.textContent = '';
1311 activeWordFile = null;
1312 appendMessage('bot', 'Word document removed.');
1313 }
1314 })
1315 .catch(error => {
1316 console.error('Error removing Word document:', error);
1317 });
1318 }
1319
1320 // Add remove button click handlers
1321 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1322 e.preventDefault();
1323 e.stopPropagation();
1324 removeActivePdf();
1325 });
1326
1327 document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
1328 e.preventDefault();
1329 e.stopPropagation();
1330 removeActiveWord();
1331 });
1332
1333 // Check initial document status
1334 function checkInitialDocumentStatus() {
1335 if (!sessionId) return;
1336
1337 // Check PDF status
1338 fetch(mxchatChat.ajax_url, {
1339 method: 'POST',
1340 headers: {
1341 'Content-Type': 'application/x-www-form-urlencoded',
1342 },
1343 body: new URLSearchParams({
1344 'action': 'mxchat_check_pdf_status',
1345 'session_id': sessionId,
1346 'nonce': mxchatChat.nonce
1347 })
1348 })
1349 .then(response => response.json())
1350 .then(data => {
1351 if (data.success && data.data.filename) {
1352 showActivePdf(data.data.filename);
1353 activePdfFile = data.data.filename;
1354 }
1355 })
1356 .catch(error => {
1357 console.error('Error checking PDF status:', error);
1358 });
1359
1360 // Check Word document status
1361 fetch(mxchatChat.ajax_url, {
1362 method: 'POST',
1363 headers: {
1364 'Content-Type': 'application/x-www-form-urlencoded',
1365 },
1366 body: new URLSearchParams({
1367 'action': 'mxchat_check_word_status',
1368 'session_id': sessionId,
1369 'nonce': mxchatChat.nonce
1370 })
1371 })
1372 .then(response => response.json())
1373 .then(data => {
1374 if (data.success && data.data.filename) {
1375 showActiveWord(data.data.filename);
1376 activeWordFile = data.data.filename;
1377 }
1378 })
1379 .catch(error => {
1380 console.error('Error checking Word document status:', error);
1381 });
1382 }
1383
1384 // Apply toolbar settings
1385 if (mxchatChat.chat_toolbar_toggle === 'on') {
1386 $('.chat-toolbar').show();
1387 } else {
1388 $('.chat-toolbar').hide();
1389 }
1390
1391 // Initialize on page load
1392 document.addEventListener('DOMContentLoaded', function() {
1393 checkInitialDocumentStatus();
1394 });
1395
1396 // Style all toolbar elements
1397 const toolbarElements = [
1398 '#mxchat-chatbot .toolbar-btn svg',
1399 '#mxchat-chatbot .active-pdf-name',
1400 '#mxchat-chatbot .active-word-name',
1401 '#mxchat-chatbot .remove-pdf-btn svg',
1402 '#mxchat-chatbot .remove-word-btn svg'
1403 ];
1404 $(toolbarElements.join(', ')).css({
1405 'fill': toolbarIconColor,
1406 'color': toolbarIconColor
1407 });
1408
1409
1410
1411 // Ensure essential elements are defined
1412 const emailForm = document.getElementById('email-collection-form');
1413 const emailBlocker = document.getElementById('email-blocker');
1414 const chatbotWrapper = document.getElementById('chat-container');
1415
1416 if (emailForm && emailBlocker && chatbotWrapper) {
1417 // Check if email exists for the current session
1418 function checkSessionAndEmail() {
1419 const sessionId = getChatSession();
1420 //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);
1421
1422 fetch(mxchatChat.ajax_url, {
1423 method: 'POST',
1424 headers: {
1425 'Content-Type': 'application/x-www-form-urlencoded',
1426 },
1427 body: new URLSearchParams({
1428 action: 'mxchat_check_email_provided',
1429 session_id: sessionId,
1430 nonce: mxchatChat.nonce,
1431 }),
1432 })
1433 .then((response) => response.json())
1434 .then((data) => {
1435 //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
1436
1437 if (data.success) {
1438 if (data.data.logged_in) {
1439 //console.log("[DEBUG JS] User is logged in. Hiding email form.");
1440 emailBlocker.style.display = 'none';
1441 chatbotWrapper.style.display = 'flex';
1442 } else if (data.data.email) {
1443 //console.log("[DEBUG JS] Email found for session. Hiding email form.");
1444 emailBlocker.style.display = 'none';
1445 chatbotWrapper.style.display = 'flex';
1446 } else {
1447 //console.log("[DEBUG JS] No email provided. Showing email form.");
1448 emailBlocker.style.display = 'flex';
1449 chatbotWrapper.style.display = 'none';
1450 }
1451 } else {
1452 //console.log("[DEBUG JS] Error or no data received. Showing email form.");
1453 emailBlocker.style.display = 'flex';
1454 chatbotWrapper.style.display = 'none';
1455 }
1456 })
1457 .catch((error) => {
1458 // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
1459 emailBlocker.style.display = 'flex';
1460 chatbotWrapper.style.display = 'none';
1461 });
1462 }
1463
1464
1465
1466 // Handle email form submission
1467 emailForm.addEventListener('submit', function (event) {
1468 event.preventDefault();
1469 const userEmail = document.getElementById('user-email').value;
1470 const sessionId = getChatSession();
1471
1472 if (userEmail) {
1473 fetch(mxchatChat.ajax_url, {
1474 method: 'POST',
1475 headers: {
1476 'Content-Type': 'application/x-www-form-urlencoded',
1477 },
1478 body: new URLSearchParams({
1479 action: 'mxchat_handle_save_email_and_response',
1480 email: userEmail,
1481 session_id: sessionId,
1482 nonce: mxchatChat.nonce,
1483 }),
1484 })
1485 .then((response) => response.json())
1486 .then((data) => {
1487 //console.log('Backend response:', data);
1488 if (data.success) {
1489 //console.log('Email saved successfully:', userEmail);
1490 emailBlocker.style.display = 'none';
1491 chatbotWrapper.style.display = 'flex';
1492
1493 // Optionally handle bot response
1494 if (data.message) {
1495 appendMessage('bot', data.message);
1496 scrollToBottom();
1497 }
1498 } else {
1499 console.error('Error saving email:', data.message || 'Unknown error');
1500 }
1501 })
1502 .catch((error) => {
1503 console.error('AJAX error:', error);
1504 });
1505 }
1506 });
1507
1508 // Check session and email status on page load
1509 checkSessionAndEmail();
1510 } else {
1511 console.error('Essential elements for email handling are missing.');
1512 }
1513
1514
1515 // Initialize when document is ready
1516 $(document).ready(function() {
1517 setFullHeight();
1518 initializeChatVisibility();
1519 loadChatHistory();
1520
1521 });
1522
1523 });
1524
1525 // Event listener for copy button
1526 document.addEventListener("click", (e) => {
1527 if (e.target.classList.contains("mxchat-copy-button")) {
1528 const copyButton = e.target;
1529 const codeBlock = copyButton
1530 .closest(".mxchat-code-block-container")
1531 .querySelector(".mxchat-code-block code");
1532
1533 if (codeBlock) {
1534 // Preserve formatting using innerText
1535 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1536 copyButton.textContent = "Copied!";
1537 copyButton.setAttribute("aria-label", "Copied to clipboard");
1538
1539 setTimeout(() => {
1540 copyButton.textContent = "Copy";
1541 copyButton.setAttribute("aria-label", "Copy to clipboard");
1542 }, 2000);
1543 });
1544 }
1545 }
1546 });
1547
1548
1549
1550