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

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

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