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

1,614 lines 54.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 // 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 URLs, markdown, and phone numbers
270 function linkify(inputText) {
271 if (!inputText) return '';
272
273 // Process markdown headers
274 let processedText = formatMarkdownHeaders(inputText);
275
276 // Process markdown links
277 const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
278 processedText = processedText.replace(markdownLinkPattern, (match, text, url) => {
279 const safeUrl = encodeURI(url);
280 const safeText = sanitizeUserInput(text);
281 return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`;
282 });
283
284 // Process phone numbers (tel:)
285 const phonePattern = /\[([^\]]+)\]\((tel:[\d+]+)\)/g;
286 processedText = processedText.replace(phonePattern, (match, text, phone) => {
287 const safePhone = encodeURI(phone);
288 const safeText = sanitizeUserInput(text);
289 return `<a href="${safePhone}">${safeText}</a>`;
290 });
291
292 // Process standalone URLs
293 const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
294 processedText = processedText.replace(urlPattern, (match, prefix, url) => {
295 const safeUrl = encodeURI(url);
296 return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
297 });
298
299 // Process www. URLs
300 const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
301 processedText = processedText.replace(wwwPattern, (match, prefix, url) => {
302 const safeUrl = encodeURI(`http://${url}`);
303 return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
304 });
305
306 return processedText;
307 }
308
309 function scrollElementToTop(element) {
310 var chatBox = $('#chat-box');
311 var elementTop = element.position().top + chatBox.scrollTop();
312 chatBox.animate({ scrollTop: elementTop }, 500);
313 }
314
315
316 // Optimized scrollToBottom function for instant scrolling
317 function scrollToBottom(instant = false) {
318 var chatBox = $('#chat-box');
319 if (instant) {
320 // Instantly set the scroll position to the bottom
321 chatBox.scrollTop(chatBox.prop("scrollHeight"));
322 } else {
323 // Use requestAnimationFrame for smoother scrolling if needed
324 let start = null;
325 const scrollHeight = chatBox.prop("scrollHeight");
326 const initialScroll = chatBox.scrollTop();
327 const distance = scrollHeight - initialScroll;
328 const duration = 500; // Duration in ms
329
330 function smoothScroll(timestamp) {
331 if (!start) start = timestamp;
332 const progress = timestamp - start;
333 const currentScroll = initialScroll + (distance * (progress / duration));
334 chatBox.scrollTop(currentScroll);
335
336 if (progress < duration) {
337 requestAnimationFrame(smoothScroll);
338 } else {
339 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
340 }
341 }
342
343 requestAnimationFrame(smoothScroll);
344 }
345 }
346
347
348 // Function to format text with **bold** inside double asterisks
349 function formatBoldText(text) {
350 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
351 }
352
353 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
354 function convertNewlinesToBreaks(text) {
355 // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
356 const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
357
358 // Wrap each paragraph in <p> tags
359 return paragraphs
360 .map(para => `<p>${para.trim()}</p>`)
361 .join('');
362 }
363 // Copy to clipboard function
364 // Function to copy text to clipboard
365 function copyToClipboard(text) {
366 var tempInput = $('<input>');
367 $('body').append(tempInput);
368 tempInput.val(text).select();
369 document.execCommand('copy');
370 tempInput.remove();
371 }
372
373
374 function updateChatModeIndicator(mode) {
375 const indicator = document.getElementById('chat-mode-indicator');
376 if (indicator) {
377 indicator.textContent = mode === 'agent' ? 'Live Agent' : 'AI Agent';
378 }
379
380 // Start or stop polling based on mode
381 if (mode === 'agent') {
382 startPolling();
383 } else {
384 stopPolling();
385 }
386 }
387
388 function callMxChat(message, callback) {
389 $.ajax({
390 url: mxchatChat.ajax_url,
391 type: 'POST',
392 dataType: 'json',
393 data: {
394 action: 'mxchat_handle_chat_request',
395 message: message,
396 session_id: getChatSession(),
397 nonce: mxchatChat.nonce
398 },
399 success: function(response) {
400 // Existing chat mode check
401 if (response.chat_mode) {
402 updateChatModeIndicator(response.chat_mode);
403 }
404 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
405 updateChatModeIndicator(response.fallbackResponse.chat_mode);
406 }
407
408 // Add PDF filename handling
409 if (response.data && response.data.filename) {
410 showActivePdf(response.data.filename);
411 activePdfFile = response.data.filename;
412 }
413
414 // Add redirect check here
415 if (response.redirect_url) {
416 let responseText = response.text || '';
417 if (responseText) {
418 replaceLastMessage("bot", responseText);
419 }
420 setTimeout(() => {
421 window.location.href = response.redirect_url;
422 }, 1500);
423 return;
424 }
425
426
427 // Check for live agent response
428 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
429 updateChatModeIndicator('agent');
430 return;
431 }
432
433 // Handle other responses
434 let responseText = response.text || '';
435 let responseHtml = response.html || '';
436 let responseMessage = response.message || '';
437
438 if (responseText === 'You are now chatting with the AI chatbot.') {
439 updateChatModeIndicator('ai');
440 }
441
442 // Handle the message and show notification if chat is hidden
443 if (responseText || responseHtml || responseMessage) {
444 // Update the messages as before
445 if (responseText && responseHtml) {
446 replaceLastMessage("bot", responseText, responseHtml);
447 } else if (responseText) {
448 replaceLastMessage("bot", responseText);
449 } else if (responseHtml) {
450 replaceLastMessage("bot", "", responseHtml);
451 } else if (responseMessage) {
452 replaceLastMessage("bot", responseMessage);
453 }
454
455 // Check if chat is hidden and show notification
456 if ($('#floating-chatbot').hasClass('hidden')) {
457 const badge = $('#chat-notification-badge');
458 if (badge.length) {
459 badge.show();
460 }
461 }
462 } else {
463 console.error("Unexpected response format:", response);
464 replaceLastMessage("bot", "I'm sorry, something went wrong.");
465 }
466
467 if (response.message_id) {
468 lastSeenMessageId = response.message_id;
469 }
470 },
471 error: function(xhr, status, error) {
472 replaceLastMessage("bot", "An unexpected error occurred.");
473 }
474 });
475 }
476
477 // Sanitize only user input
478 function sanitizeUserInput(text) {
479 const div = document.createElement('div');
480 div.textContent = text;
481 return div.innerHTML;
482 }
483
484 // Modified appendMessage function that only sanitizes user content
485 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
486 try {
487 // Determine styles based on sender type
488 let messageClass, bgColor, fontColor;
489
490 if (sender === "user") {
491 messageClass = "user-message";
492 bgColor = userMessageBgColor;
493 fontColor = userMessageFontColor;
494 // Only sanitize user input
495 messageText = sanitizeUserInput(messageText);
496 } else if (sender === "agent") {
497 messageClass = "agent-message";
498 bgColor = liveAgentMessageBgColor;
499 fontColor = liveAgentMessageFontColor;
500 } else {
501 messageClass = "bot-message";
502 bgColor = botMessageBgColor;
503 fontColor = botMessageFontColor;
504 }
505
506 const messageDiv = $('<div>')
507 .addClass(messageClass)
508 .css({
509 'background': bgColor,
510 'color': fontColor,
511 'margin-bottom': '1em'
512 });
513
514 // Process the message content based on sender
515 let fullMessage;
516 if (sender === "user") {
517 // For user messages, apply linkify after sanitization
518 fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
519 } else {
520 // For bot/agent messages, preserve HTML
521 fullMessage = messageText;
522 }
523
524 // Add images if provided
525 if (images && images.length > 0) {
526 fullMessage += '<div class="image-gallery">';
527 images.forEach(img => {
528 // Ensure image URLs and titles are properly escaped
529 const safeTitle = sanitizeUserInput(img.title);
530 const safeUrl = encodeURI(img.image_url);
531 const safeThumbnail = encodeURI(img.thumbnail_url);
532
533 fullMessage += `
534 <div style="margin-bottom: 10px;">
535 <strong>${safeTitle}</strong><br>
536 <a href="${safeUrl}" target="_blank">
537 <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" />
538 </a>
539 </div>`;
540 });
541 fullMessage += '</div>';
542 }
543
544 // Append HTML content if provided
545 if (messageHtml && sender !== "user") {
546 fullMessage += '<br><br>' + messageHtml;
547 }
548
549 messageDiv.html(fullMessage);
550
551 if (isTemporary) {
552 messageDiv.addClass('temporary-message');
553 }
554
555 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
556 if (sender === "bot") {
557 const lastUserMessage = $('#chat-box').find('.user-message').last();
558 if (lastUserMessage.length) {
559 scrollElementToTop(lastUserMessage);
560 }
561 }
562 });
563
564 if (messageText.id) {
565 lastSeenMessageId = messageText.id;
566 hideNotification();
567 }
568 } catch (error) {
569 console.error("Error rendering message:", error);
570 }
571 }
572
573
574 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
575 var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
576 var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();
577
578 // Determine styles
579 let bgColor, fontColor;
580 if (sender === "user") {
581 bgColor = userMessageBgColor;
582 fontColor = userMessageFontColor;
583 } else if (sender === "agent") {
584 bgColor = liveAgentMessageBgColor;
585 fontColor = liveAgentMessageFontColor;
586 } else {
587 bgColor = botMessageBgColor;
588 fontColor = botMessageFontColor;
589 }
590
591 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
592 if (responseHtml) {
593 fullMessage += '<br><br>' + responseHtml;
594 }
595
596 if (images.length > 0) {
597 fullMessage += '<div class="image-gallery">';
598 images.forEach(img => {
599 fullMessage += `
600 <div style="margin-bottom: 10px;">
601 <strong>${img.title}</strong><br>
602 <a href="${img.image_url}" target="_blank">
603 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
604 </a>
605 </div>`;
606 });
607 fullMessage += '</div>';
608 }
609
610 if (lastMessageDiv.length) {
611 lastMessageDiv.fadeOut(200, function() {
612 $(this)
613 .html(fullMessage)
614 .removeClass('bot-message user-message')
615 .addClass(messageClass)
616 .css({
617 'background-color': bgColor,
618 'color': fontColor,
619 })
620 .removeClass('temporary-message')
621 .fadeIn(200, function() {
622 if (sender === "bot" || sender === "agent") {
623 const lastUserMessage = $('#chat-box').find('.user-message').last();
624 if (lastUserMessage.length) {
625 scrollElementToTop(lastUserMessage);
626 }
627 // Show notification if chat is hidden
628 if ($('#floating-chatbot').hasClass('hidden')) {
629 showNotification();
630 }
631 }
632 });
633 });
634 } else {
635 appendMessage(sender, responseText, responseHtml, images);
636 }
637 }
638
639
640 function startPolling() {
641 // Clear any existing interval first
642 stopPolling();
643 // Start new polling interval
644 pollingInterval = setInterval(checkForAgentMessages, 5000);
645 //console.log("Started agent message polling");
646 }
647
648 function stopPolling() {
649 if (pollingInterval) {
650 clearInterval(pollingInterval);
651 pollingInterval = null;
652 //console.log("Stopped agent message polling");
653 }
654 }
655
656
657 // Update your checkForAgentMessages function
658 function checkForAgentMessages() {
659 const sessionId = getChatSession();
660 $.ajax({
661 url: mxchatChat.ajax_url,
662 type: 'POST',
663 dataType: 'json',
664 data: {
665 action: 'mxchat_fetch_new_messages',
666 session_id: sessionId,
667 last_seen_id: lastSeenMessageId,
668 nonce: mxchatChat.nonce
669 },
670 success: function (response) {
671 if (response.success && response.data?.new_messages) {
672 let hasNewMessage = false;
673
674 response.data.new_messages.forEach(function (message) {
675 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
676 hasNewMessage = true;
677 replaceLastMessage("agent", message.content);
678 lastSeenMessageId = message.id;
679 processedMessageIds.add(message.id);
680 }
681 });
682
683 if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
684 showNotification();
685 }
686
687 scrollToBottom(true);
688 }
689 },
690 error: function (xhr, status, error) {
691 console.error("Polling error:", xhr, status, error);
692 }
693 });
694 }
695 function loadChatHistory() {
696 var sessionId = getChatSession();
697 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
698
699 if (chatPersistenceEnabled && sessionId) {
700 $.ajax({
701 url: mxchatChat.ajax_url,
702 type: 'POST',
703 dataType: 'json',
704 data: {
705 action: 'mxchat_fetch_conversation_history',
706 session_id: sessionId
707 },
708 success: function(response) {
709 if (response.success && response.data && Array.isArray(response.data.conversation)) {
710
711
712 var $chatBox = $('#chat-box');
713 var $fragment = $(document.createDocumentFragment());
714 let highestMessageId = lastSeenMessageId;
715
716 if (response.data.chat_mode) {
717 updateChatModeIndicator(response.data.chat_mode);
718 }
719
720 $.each(response.data.conversation, function(index, message) {
721 // Skip agent messages if persistence is off
722 if (!chatPersistenceEnabled && message.role === 'agent') {
723 return;
724 }
725
726 var messageClass, messageBgColor, messageFontColor;
727
728 switch (message.role) {
729 case 'user':
730 messageClass = 'user-message';
731 messageBgColor = userMessageBgColor;
732 messageFontColor = userMessageFontColor;
733 break;
734 case 'agent':
735 messageClass = 'agent-message';
736 messageBgColor = liveAgentMessageBgColor;
737 messageFontColor = liveAgentMessageFontColor;
738 break;
739 default:
740 messageClass = 'bot-message';
741 messageBgColor = botMessageBgColor;
742 messageFontColor = botMessageFontColor;
743 break;
744 }
745
746 var messageElement = $('<div>').addClass(messageClass)
747 .css({
748 'background': messageBgColor,
749 'color': messageFontColor
750 });
751
752 var content = message.content;
753 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
754 content = decodeHTMLEntities(content);
755
756 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
757 messageElement.html(content);
758 } else {
759 var formattedContent = linkify(
760 formatBoldText(
761 convertNewlinesToBreaks(formatCodeBlocks(content))
762 )
763 );
764 messageElement.html(formattedContent);
765 }
766
767 $fragment.append(messageElement);
768
769 // In loadChatHistory, change this part:
770 if (message.id) {
771 highestMessageId = Math.max(highestMessageId, message.id);
772 processedMessageIds.add(message.id); // Add all message IDs to processed set
773 }
774 });
775
776 $chatBox.append($fragment);
777 scrollToBottom(true);
778
779 if (response.data.conversation.length > 0) {
780 $('#mxchat-popular-questions').hide();
781 }
782
783 // Update lastSeenMessageId after history loads
784 lastSeenMessageId = highestMessageId;
785
786 // Only update chat mode if persistence is enabled
787 if (chatPersistenceEnabled && response.data.conversation.length > 0) {
788 var lastMessage = response.data.conversation[response.data.conversation.length - 1];
789 if (lastMessage.role === 'agent') {
790 updateChatModeIndicator('agent');
791 }
792 }
793 } else {
794 console.warn("No conversation history found.");
795 }
796 },
797 error: function(xhr, status, error) {
798 console.error("Error loading chat history:", status, error);
799 appendMessage("bot", "Unable to load chat history.");
800 }
801 });
802 } else {
803 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
804 }
805 }
806
807 // Function to decode HTML entities
808 function decodeHTMLEntities(text) {
809 var textArea = document.createElement('textarea');
810 textArea.innerHTML = text;
811 return textArea.value;
812 }
813
814
815 // Update formatCodeBlocks function
816 function formatCodeBlocks(text) {
817 // First handle raw PHP tags
818 text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
819 return `<pre><code class="language-php">${escapeHtml(match)}</code></pre>`;
820 });
821
822 // Then handle code blocks with backticks
823 text = text.replace(/```php5?\n([\s\S]+?)```/gi, (match, code) => {
824 return `<pre><code class="language-php">${escapeHtml(code)}</code></pre>`;
825 });
826
827 return text;
828 }
829
830 // Update escapeHtml function to preserve existing code blocks
831 function escapeHtml(unsafe) {
832 // First check if it's already a code block
833 if (unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
834 return unsafe;
835 }
836
837 return unsafe
838 .replace(/&/g, "&amp;")
839 .replace(/</g, "&lt;")
840 .replace(/>/g, "&gt;")
841 .replace(/"/g, "&quot;")
842 .replace(/'/g, "&#039;");
843 }
844 // Utility function to escape HTML
845 function escapeHtml(unsafe) {
846 return unsafe
847 .replace(/&/g, "&amp;")
848 .replace(/</g, "&lt;")
849 .replace(/>/g, "&gt;")
850 .replace(/"/g, "&quot;")
851 .replace(/'/g, "&#039;");
852 }
853
854
855
856
857 // Function to convert newlines, skipping preformatted text
858 function convertNewlinesToBreaks(text) {
859 // Split while preserving code blocks
860 return text.split(/(<pre\b[^>]*>[\s\S]*?<\/pre>)/g).map(part => {
861 if (part.startsWith('<pre')) return part;
862 return part.replace(/(^|[^>])\n/g, '$1<br>');
863 }).join('');
864 }
865
866
867
868
869 // Helper function to check if a string is an image HTML
870 function isImageHtml(str) {
871 return str.startsWith('<img') && str.endsWith('>');
872 }
873
874 // Function to remove thinking dots
875 function removeThinkingDots() {
876 $('.thinking-dots').closest('.temporary-message').remove();
877 }
878
879 function isMobile() {
880 // This can be a simple check, or more sophisticated detection of mobile devices
881 return window.innerWidth <= 768; // Example threshold for mobile devices
882 }
883
884 function disableScroll() {
885 if (isMobile()) {
886 $('body').css('overflow', 'hidden');
887 }
888 }
889
890 function enableScroll() {
891 if (isMobile()) {
892 $('body').css('overflow', '');
893 }
894 }
895
896 // Pre-chat dismissal check function (wrapped in a function for reuse)
897 function checkPreChatDismissal() {
898 $.ajax({
899 url: mxchatChat.ajax_url,
900 type: 'POST',
901 data: {
902 action: 'mxchat_check_pre_chat_message_status',
903 _ajax_nonce: mxchatChat.nonce
904 },
905 success: function(response) {
906 if (response.success && !response.data.dismissed) {
907 $('#pre-chat-message').fadeIn(250);
908 } else {
909 $('#pre-chat-message').hide();
910 }
911 },
912 error: function() {
913 console.error('Failed to check pre-chat message dismissal status.');
914 }
915 });
916 }
917
918 // Function to show the chatbot widget
919 function showChatWidget() {
920 // First ensure display is set
921 $('#floating-chatbot-button').css('display', 'flex');
922 // Then handle the fade
923 $('#floating-chatbot-button').fadeTo(500, 1);
924 // Force visibility
925 $('#floating-chatbot-button').removeClass('hidden');
926 //console.log('Showing widget');
927 }
928
929 // Function to hide the chatbot widget
930 function hideChatWidget() {
931 $('#floating-chatbot-button').css('display', 'none');
932 $('#floating-chatbot-button').addClass('hidden');
933 //console.log('Hiding widget');
934 }
935
936 function initializeChatVisibility() {
937 //console.log('Initializing chat visibility');
938 const complianzEnabled = mxchatChat.complianz_toggle === 'on' ||
939 mxchatChat.complianz_toggle === '1' ||
940 mxchatChat.complianz_toggle === 1;
941
942 if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
943 // Initial check
944 checkConsentAndShowChat();
945
946 // Listen for consent changes
947 $(document).on('cmplz_status_change', function(event) {
948 //console.log('Status change detected');
949 checkConsentAndShowChat();
950 });
951 } else {
952 // If Complianz is not enabled, always show
953 $('#floating-chatbot-button')
954 .css('display', 'flex')
955 .removeClass('hidden no-consent')
956 .fadeTo(500, 1);
957
958 // Also check pre-chat message when Complianz is not enabled
959 checkPreChatDismissal();
960 }
961 }
962
963
964
965 function checkConsentAndShowChat() {
966 var consentStatus = cmplz_has_consent('marketing');
967 var consentType = complianz.consenttype;
968
969 //console.log('Checking consent:', {status: consentStatus,type: consentType});
970
971 let $widget = $('#floating-chatbot-button');
972 let $chatbot = $('#floating-chatbot');
973 let $preChat = $('#pre-chat-message');
974
975 if (consentStatus === true) {
976 //console.log('Consent granted - showing widget');
977 $widget
978 .removeClass('no-consent')
979 .css('display', 'flex')
980 .removeClass('hidden')
981 .fadeTo(500, 1);
982 $chatbot.removeClass('no-consent');
983
984 // Show pre-chat message if not dismissed
985 checkPreChatDismissal();
986 } else {
987 //console.log('No consent - hiding widget');
988 $widget
989 .addClass('no-consent')
990 .fadeTo(500, 0, function() {
991 $(this)
992 .css('display', 'none')
993 .addClass('hidden');
994 });
995 $chatbot.addClass('no-consent');
996
997 // Hide pre-chat message when no consent
998 $preChat.hide();
999 }
1000 }
1001
1002 // Function to dismiss pre-chat message for 24 hours
1003 function handlePreChatDismissal() {
1004 $('#pre-chat-message').fadeOut(200);
1005 $.ajax({
1006 url: mxchatChat.ajax_url,
1007 type: 'POST',
1008 data: {
1009 action: 'mxchat_dismiss_pre_chat_message',
1010 _ajax_nonce: mxchatChat.nonce
1011 },
1012 success: function() {
1013 $('#pre-chat-message').hide();
1014 },
1015 error: function() {
1016 console.error('Failed to dismiss pre-chat message.');
1017 }
1018 });
1019 }
1020
1021 // Handle pre-chat message dismissal on button click
1022 $(document).on('click', '.close-pre-chat-message', function(e) {
1023 e.stopPropagation();
1024 handlePreChatDismissal();
1025 });
1026
1027 // Toggle chatbot visibility on floating button click
1028 $(document).on('click', '#floating-chatbot-button', function() {
1029 var chatbot = $('#floating-chatbot');
1030 if (chatbot.hasClass('hidden')) {
1031 chatbot.removeClass('hidden').addClass('visible');
1032 $(this).addClass('hidden');
1033 $('#chat-notification-badge').hide(); // Hide notification when opening chat
1034 disableScroll();
1035 $('#pre-chat-message').fadeOut(250);
1036 } else {
1037 chatbot.removeClass('visible').addClass('hidden');
1038 $(this).removeClass('hidden');
1039 enableScroll();
1040 checkPreChatDismissal();
1041 }
1042 });
1043
1044 $(document).on('click', '#exit-chat-button', function() {
1045 $('#floating-chatbot').addClass('hidden').removeClass('visible');
1046 $('#floating-chatbot-button').removeClass('hidden');
1047 enableScroll();
1048 });
1049
1050 // Close pre-chat message on click
1051 $(document).on('click', '.close-pre-chat-message', function(e) {
1052 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
1053 $('#pre-chat-message').fadeOut(200, function() {
1054 $(this).remove();
1055 });
1056 });
1057
1058 // Open chatbot when pre-chat message is clicked
1059 $(document).on('click', '#pre-chat-message', function() {
1060 var chatbot = $('#floating-chatbot');
1061 if (chatbot.hasClass('hidden')) {
1062 chatbot.removeClass('hidden').addClass('visible');
1063 $('#floating-chatbot-button').addClass('hidden');
1064 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
1065 disableScroll(); // Disable scroll when chatbot opens
1066 }
1067 });
1068
1069 // If the chatbot is initially hidden, ensure the button is visible
1070 if ($('#floating-chatbot').hasClass('hidden')) {
1071 $('#floating-chatbot-button').removeClass('hidden');
1072 }
1073
1074 function setFullHeight() {
1075 var vh = $(window).innerHeight() * 0.01;
1076 $(':root').css('--vh', vh + 'px');
1077 }
1078
1079 // Set the height when the page loads
1080
1081
1082 // Set the height on resize and orientation change events
1083 $(window).on('resize orientationchange', function() {
1084 setFullHeight();
1085 });
1086
1087
1088 // Now handle the close button to dismiss the pre-chat message for 24 hours
1089 var closeButton = document.querySelector('.close-pre-chat-message');
1090 if (closeButton) {
1091 closeButton.addEventListener('click', function() {
1092 $('#pre-chat-message').fadeOut(200); // Hide the message
1093
1094 // Send an AJAX request to set the transient flag for 24 hours
1095 $.ajax({
1096 url: mxchatChat.ajax_url,
1097 type: 'POST',
1098 data: {
1099 action: 'mxchat_dismiss_pre_chat_message',
1100 _ajax_nonce: mxchatChat.nonce
1101 },
1102 success: function() {
1103 //console.log('Pre-chat message dismissed for 24 hours.');
1104
1105 // Ensure the message is hidden after dismissal
1106 $('#pre-chat-message').hide();
1107 },
1108 error: function() {
1109 //console.error('Failed to dismiss pre-chat message.');
1110 }
1111 });
1112 });
1113 }
1114
1115
1116
1117
1118 // Event listener for Add to Cart button
1119 $(document).on('click', '.mxchat-add-to-cart-button', function() {
1120 var productId = $(this).data('product-id');
1121 // Add a special prefix to indicate this is from button
1122 appendMessage("user", "add to cart");
1123 sendMessageToChatbot("!addtocart"); // Special command to indicate button click
1124 });
1125
1126
1127 if (document.getElementById('pdf-upload-btn')) {
1128 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
1129 document.getElementById('pdf-upload').click();
1130 });
1131 }
1132
1133 if (document.getElementById('word-upload-btn')) {
1134 document.getElementById('word-upload-btn').addEventListener('click', function() {
1135 document.getElementById('word-upload').click();
1136 });
1137 }
1138
1139 function addSafeEventListener(elementId, eventType, handler) {
1140 const element = document.getElementById(elementId);
1141 if (element) {
1142 element.addEventListener(eventType, handler);
1143 }
1144 }
1145
1146
1147 // PDF file input change handler
1148 addSafeEventListener('pdf-upload', 'change', async function(e) {
1149 const file = e.target.files[0];
1150
1151 if (!file || file.type !== 'application/pdf') {
1152 alert('Please select a valid PDF file.');
1153 return;
1154 }
1155
1156 if (!sessionId) {
1157 console.error('No session ID found');
1158 alert('Error: No session ID found');
1159 return;
1160 }
1161
1162 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
1163 console.error('mxchatChat not properly configured:', mxchatChat);
1164 alert('Error: Ajax configuration missing');
1165 return;
1166 }
1167
1168 // Disable buttons and show loading state
1169 const uploadBtn = document.getElementById('pdf-upload-btn');
1170 const sendBtn = document.getElementById('send-button');
1171 const originalBtnContent = uploadBtn.innerHTML;
1172
1173 try {
1174 const formData = new FormData();
1175 formData.append('action', 'mxchat_upload_pdf');
1176 formData.append('pdf_file', file);
1177 formData.append('session_id', sessionId);
1178 formData.append('nonce', mxchatChat.nonce);
1179
1180 uploadBtn.disabled = true;
1181 sendBtn.disabled = true;
1182 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1183 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1184 </svg>`;
1185
1186 const response = await fetch(mxchatChat.ajax_url, {
1187 method: 'POST',
1188 body: formData
1189 });
1190
1191 const data = await response.json();
1192
1193 if (data.success) {
1194 // Hide popular questions if they exist
1195 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1196 if (popularQuestionsContainer) {
1197 popularQuestionsContainer.style.display = 'none';
1198 }
1199
1200 // Show the active PDF name
1201 showActivePdf(data.data.filename);
1202
1203 appendMessage('bot', data.data.message);
1204 scrollToBottom();
1205 activePdfFile = data.data.filename;
1206 } else {
1207 console.error('Upload failed:', data.data);
1208 alert('Failed to upload PDF. Please try again.');
1209 }
1210 } catch (error) {
1211 console.error('Upload error:', error);
1212 alert('Error uploading file. Please try again.');
1213 } finally {
1214 uploadBtn.disabled = false;
1215 sendBtn.disabled = false;
1216 uploadBtn.innerHTML = originalBtnContent;
1217 this.value = ''; // Reset file input
1218 }
1219 });
1220
1221 // Word file input change handler
1222 addSafeEventListener('word-upload', 'change', async function(e) {
1223 const file = e.target.files[0];
1224
1225 if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1226 alert('Please select a valid Word document (.docx).');
1227 return;
1228 }
1229
1230 if (!sessionId) {
1231 console.error('No session ID found');
1232 alert('Error: No session ID found');
1233 return;
1234 }
1235
1236 // Disable buttons and show loading state
1237 const uploadBtn = document.getElementById('word-upload-btn');
1238 const sendBtn = document.getElementById('send-button');
1239 const originalBtnContent = uploadBtn.innerHTML;
1240
1241 try {
1242 const formData = new FormData();
1243 formData.append('action', 'mxchat_upload_word');
1244 formData.append('word_file', file);
1245 formData.append('session_id', sessionId);
1246 formData.append('nonce', mxchatChat.nonce);
1247
1248 uploadBtn.disabled = true;
1249 sendBtn.disabled = true;
1250 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1251 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1252 </svg>`;
1253
1254 const response = await fetch(mxchatChat.ajax_url, {
1255 method: 'POST',
1256 body: formData
1257 });
1258
1259 const data = await response.json();
1260
1261 if (data.success) {
1262 // Hide popular questions if they exist
1263 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1264 if (popularQuestionsContainer) {
1265 popularQuestionsContainer.style.display = 'none';
1266 }
1267
1268 // Show the active Word document name
1269 showActiveWord(data.data.filename);
1270
1271 appendMessage('bot', data.data.message);
1272 scrollToBottom();
1273 activeWordFile = data.data.filename;
1274 } else {
1275 console.error('Upload failed:', data.data);
1276 alert('Failed to upload Word document. Please try again.');
1277 }
1278 } catch (error) {
1279 console.error('Upload error:', error);
1280 alert('Error uploading file. Please try again.');
1281 } finally {
1282 uploadBtn.disabled = false;
1283 sendBtn.disabled = false;
1284 uploadBtn.innerHTML = originalBtnContent;
1285 this.value = ''; // Reset file input
1286 }
1287 });
1288
1289 // Function to show active PDF name in toolbar
1290 function showActivePdf(filename) {
1291 const container = document.getElementById('active-pdf-container');
1292 const nameElement = document.getElementById('active-pdf-name');
1293
1294 if (!container || !nameElement) {
1295 console.error('PDF container elements not found');
1296 return;
1297 }
1298
1299 nameElement.textContent = filename;
1300 container.style.display = 'flex';
1301 }
1302
1303 // Function to show active Word document name in toolbar
1304 function showActiveWord(filename) {
1305 const container = document.getElementById('active-word-container');
1306 const nameElement = document.getElementById('active-word-name');
1307
1308 if (!container || !nameElement) {
1309 console.error('Word document container elements not found');
1310 return;
1311 }
1312
1313 nameElement.textContent = filename;
1314 container.style.display = 'flex';
1315 }
1316
1317 // Function to remove active PDF
1318 function removeActivePdf() {
1319 const container = document.getElementById('active-pdf-container');
1320 const nameElement = document.getElementById('active-pdf-name');
1321
1322 if (!container || !nameElement || !activePdfFile) return;
1323
1324 fetch(mxchatChat.ajax_url, {
1325 method: 'POST',
1326 headers: {
1327 'Content-Type': 'application/x-www-form-urlencoded',
1328 },
1329 body: new URLSearchParams({
1330 'action': 'mxchat_remove_pdf',
1331 'session_id': sessionId,
1332 'nonce': mxchatChat.nonce
1333 })
1334 })
1335 .then(response => response.json())
1336 .then(data => {
1337 if (data.success) {
1338 container.style.display = 'none';
1339 nameElement.textContent = '';
1340 activePdfFile = null;
1341 appendMessage('bot', 'PDF removed.');
1342 }
1343 })
1344 .catch(error => {
1345 console.error('Error removing PDF:', error);
1346 });
1347 }
1348
1349 // Function to remove active Word document
1350 function removeActiveWord() {
1351 const container = document.getElementById('active-word-container');
1352 const nameElement = document.getElementById('active-word-name');
1353
1354 if (!container || !nameElement || !activeWordFile) return;
1355
1356 fetch(mxchatChat.ajax_url, {
1357 method: 'POST',
1358 headers: {
1359 'Content-Type': 'application/x-www-form-urlencoded',
1360 },
1361 body: new URLSearchParams({
1362 'action': 'mxchat_remove_word',
1363 'session_id': sessionId,
1364 'nonce': mxchatChat.nonce
1365 })
1366 })
1367 .then(response => response.json())
1368 .then(data => {
1369 if (data.success) {
1370 container.style.display = 'none';
1371 nameElement.textContent = '';
1372 activeWordFile = null;
1373 appendMessage('bot', 'Word document removed.');
1374 }
1375 })
1376 .catch(error => {
1377 console.error('Error removing Word document:', error);
1378 });
1379 }
1380
1381 // Add remove button click handlers
1382 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1383 e.preventDefault();
1384 e.stopPropagation();
1385 removeActivePdf();
1386 });
1387
1388 document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
1389 e.preventDefault();
1390 e.stopPropagation();
1391 removeActiveWord();
1392 });
1393
1394 // Check initial document status
1395 function checkInitialDocumentStatus() {
1396 if (!sessionId) return;
1397
1398 // Check PDF status
1399 fetch(mxchatChat.ajax_url, {
1400 method: 'POST',
1401 headers: {
1402 'Content-Type': 'application/x-www-form-urlencoded',
1403 },
1404 body: new URLSearchParams({
1405 'action': 'mxchat_check_pdf_status',
1406 'session_id': sessionId,
1407 'nonce': mxchatChat.nonce
1408 })
1409 })
1410 .then(response => response.json())
1411 .then(data => {
1412 if (data.success && data.data.filename) {
1413 showActivePdf(data.data.filename);
1414 activePdfFile = data.data.filename;
1415 }
1416 })
1417 .catch(error => {
1418 console.error('Error checking PDF status:', error);
1419 });
1420
1421 // Check Word document status
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_word_status',
1429 'session_id': sessionId,
1430 'nonce': mxchatChat.nonce
1431 })
1432 })
1433 .then(response => response.json())
1434 .then(data => {
1435 if (data.success && data.data.filename) {
1436 showActiveWord(data.data.filename);
1437 activeWordFile = data.data.filename;
1438 }
1439 })
1440 .catch(error => {
1441 console.error('Error checking Word document status:', error);
1442 });
1443 }
1444
1445 // Apply toolbar settings
1446 if (mxchatChat.chat_toolbar_toggle === 'on') {
1447 $('.chat-toolbar').show();
1448 } else {
1449 $('.chat-toolbar').hide();
1450 }
1451
1452 // Initialize on page load
1453 document.addEventListener('DOMContentLoaded', function() {
1454 checkInitialDocumentStatus();
1455 });
1456
1457 const toolbarElements = [
1458 '#mxchat-chatbot .toolbar-btn svg',
1459 '#mxchat-chatbot .active-pdf-name',
1460 '#mxchat-chatbot .active-word-name',
1461 '#mxchat-chatbot .remove-pdf-btn svg',
1462 '#mxchat-chatbot .remove-word-btn svg',
1463 '#mxchat-chatbot .toolbar-perplexity svg'
1464 ];
1465
1466 toolbarElements.forEach(selector => {
1467 $(selector).css({
1468 'fill': toolbarIconColor,
1469 'stroke': toolbarIconColor,
1470 'color': toolbarIconColor
1471 });
1472 });
1473
1474
1475 // Ensure essential elements are defined
1476 const emailForm = document.getElementById('email-collection-form');
1477 const emailBlocker = document.getElementById('email-blocker');
1478 const chatbotWrapper = document.getElementById('chat-container');
1479
1480 if (emailForm && emailBlocker && chatbotWrapper) {
1481 // Check if email exists for the current session
1482 function checkSessionAndEmail() {
1483 const sessionId = getChatSession();
1484 //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);
1485
1486 fetch(mxchatChat.ajax_url, {
1487 method: 'POST',
1488 headers: {
1489 'Content-Type': 'application/x-www-form-urlencoded',
1490 },
1491 body: new URLSearchParams({
1492 action: 'mxchat_check_email_provided',
1493 session_id: sessionId,
1494 nonce: mxchatChat.nonce,
1495 }),
1496 })
1497 .then((response) => response.json())
1498 .then((data) => {
1499 //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
1500
1501 if (data.success) {
1502 if (data.data.logged_in) {
1503 //console.log("[DEBUG JS] User is logged in. Hiding email form.");
1504 emailBlocker.style.display = 'none';
1505 chatbotWrapper.style.display = 'flex';
1506 } else if (data.data.email) {
1507 //console.log("[DEBUG JS] Email found for session. Hiding email form.");
1508 emailBlocker.style.display = 'none';
1509 chatbotWrapper.style.display = 'flex';
1510 } else {
1511 //console.log("[DEBUG JS] No email provided. Showing email form.");
1512 emailBlocker.style.display = 'flex';
1513 chatbotWrapper.style.display = 'none';
1514 }
1515 } else {
1516 //console.log("[DEBUG JS] Error or no data received. Showing email form.");
1517 emailBlocker.style.display = 'flex';
1518 chatbotWrapper.style.display = 'none';
1519 }
1520 })
1521 .catch((error) => {
1522 // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
1523 emailBlocker.style.display = 'flex';
1524 chatbotWrapper.style.display = 'none';
1525 });
1526 }
1527
1528
1529
1530 // Handle email form submission
1531 emailForm.addEventListener('submit', function (event) {
1532 event.preventDefault();
1533 const userEmail = document.getElementById('user-email').value;
1534 const sessionId = getChatSession();
1535
1536 if (userEmail) {
1537 fetch(mxchatChat.ajax_url, {
1538 method: 'POST',
1539 headers: {
1540 'Content-Type': 'application/x-www-form-urlencoded',
1541 },
1542 body: new URLSearchParams({
1543 action: 'mxchat_handle_save_email_and_response',
1544 email: userEmail,
1545 session_id: sessionId,
1546 nonce: mxchatChat.nonce,
1547 }),
1548 })
1549 .then((response) => response.json())
1550 .then((data) => {
1551 //console.log('Backend response:', data);
1552 if (data.success) {
1553 //console.log('Email saved successfully:', userEmail);
1554 emailBlocker.style.display = 'none';
1555 chatbotWrapper.style.display = 'flex';
1556
1557 // Optionally handle bot response
1558 if (data.message) {
1559 appendMessage('bot', data.message);
1560 scrollToBottom();
1561 }
1562 } else {
1563 console.error('Error saving email:', data.message || 'Unknown error');
1564 }
1565 })
1566 .catch((error) => {
1567 console.error('AJAX error:', error);
1568 });
1569 }
1570 });
1571
1572 // Check session and email status on page load
1573 checkSessionAndEmail();
1574 } else {
1575 console.error('Essential elements for email handling are missing.');
1576 }
1577
1578
1579 // Initialize when document is ready
1580 $(document).ready(function() {
1581 setFullHeight();
1582 initializeChatVisibility();
1583 loadChatHistory();
1584
1585 });
1586
1587 });
1588
1589 // Event listener for copy button
1590 document.addEventListener("click", (e) => {
1591 if (e.target.classList.contains("mxchat-copy-button")) {
1592 const copyButton = e.target;
1593 const codeBlock = copyButton
1594 .closest(".mxchat-code-block-container")
1595 .querySelector(".mxchat-code-block code");
1596
1597 if (codeBlock) {
1598 // Preserve formatting using innerText
1599 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1600 copyButton.textContent = "Copied!";
1601 copyButton.setAttribute("aria-label", "Copied to clipboard");
1602
1603 setTimeout(() => {
1604 copyButton.textContent = "Copy";
1605 copyButton.setAttribute("aria-label", "Copy to clipboard");
1606 }, 2000);
1607 });
1608 }
1609 }
1610 });
1611
1612
1613
1614