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

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

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