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

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