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

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

1,140 lines 39.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
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 var lastSeenMessageId = '';
19 // Initialize session ID
20 var sessionId = getChatSession();
21
22 let pollingInterval; // Variable to store the interval ID
23 let processedMessageIds = new Set(); // Add this at the top with your other variables
24 //console.log('Live Agent BG Color:', liveAgentMessageBgColor);
25 //console.log('Live Agent Font Color:', liveAgentMessageFontColor);
26 let activePdfFile = null;
27
28
29
30
31 function getChatSession() {
32 var sessionId = getCookie('mxchat_session_id');
33 //console.log("Session ID retrieved from cookie: ", sessionId);
34
35 if (!sessionId) {
36 sessionId = generateSessionId();
37 //console.log("Generated new session ID: ", sessionId);
38 setChatSession(sessionId);
39 }
40
41 //console.log("Final session ID: ", sessionId);
42 return sessionId;
43 }
44
45 function setChatSession(sessionId) {
46 // Set the cookie with a 24-hour expiration (86400 seconds)
47 document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
48 }
49
50 // Get cookie value by name
51 function getCookie(name) {
52 let value = "; " + document.cookie;
53 let parts = value.split("; " + name + "=");
54 if (parts.length == 2) return parts.pop().split(";").shift();
55 }
56
57 // Generate a new session ID
58 function generateSessionId() {
59 return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
60 }
61
62 // Function to send the message to the chatbot (backend)
63 function sendMessageToChatbot(message) {
64 var sessionId = getChatSession(); // Reuse the session ID logic
65
66 // Hide the popular questions section
67 $('#mxchat-popular-questions').hide();
68
69 // Show thinking indicator (no need to append the user's message again)
70 appendThinkingMessage();
71 scrollToBottom();
72
73 //console.log("Sending message to chatbot:", message); // Log the message
74 //console.log("Session ID:", sessionId); // Log the session ID
75
76 // Call the chatbot using the same call logic as sendMessage
77 callMxChat(message, function(response) {
78 // ** Ensure temporary thinking message is removed before adding new response **
79 $('.temporary-message').remove();
80
81 // Replace thinking indicator with actual response
82 replaceLastMessage("bot", response);
83 });
84 }
85
86
87
88
89 function sendMessage() {
90 var message = $('#chat-input').val(); // Get value from textarea
91 if (message) {
92 appendMessage("user", message); // Append user's message
93 $('#chat-input').val(''); // Clear the textarea
94 $('#chat-input').css('height', 'auto'); // Reset height after clearing content
95
96 // Hide the popular questions section
97 $('#mxchat-popular-questions').hide();
98
99 // Show typing indicator
100 appendThinkingMessage();
101 scrollToBottom();
102
103 callMxChat(message, function(response) {
104 // Replace typing indicator with actual response
105 replaceLastMessage("bot", response);
106 });
107 }
108 }
109
110
111
112 // Function to append a thinking message with animation
113 function appendThinkingMessage() {
114 // Remove any existing thinking dots first
115 $('.thinking-dots').remove();
116
117 // Retrieve the bot message font color and background color
118 var botMessageFontColor = mxchatChat.bot_message_font_color;
119 var botMessageBgColor = mxchatChat.bot_message_bg_color;
120
121
122 var thinkingHtml = '<div class="thinking-dots-container">' +
123 '<div class="thinking-dots">' +
124 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
125 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
126 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
127 '</div>' +
128 '</div>';
129
130 // Append the thinking dots to the chat container (or within the temporary message div)
131 $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
132 scrollToBottom();
133 }
134
135 // Trigger send button click when "Enter" key is pressed in the textarea
136 $('#chat-input').keypress(function(e) {
137 if (e.which == 13 && !e.shiftKey) { // Check if "Enter" is pressed without Shift
138 e.preventDefault(); // Prevent default "Enter" behavior
139 $('#send-button').click(); // Trigger send button click
140 }
141 });
142
143 // Handle send button click
144 $('#send-button').click(function() {
145 sendMessage();
146 });
147
148 // Handle click on popular questions
149 $('.mxchat-popular-question').on('click', function () {
150 var question = $(this).text(); // Get the text of the clicked question
151
152 // Append the question as if the user typed it
153 appendMessage("user", question);
154
155 // Send the question to the server (backend)
156 sendMessageToChatbot(question);
157 });
158
159
160 // Use the linkTarget in your linkify function
161 function linkify(inputText) {
162 // Check for already linked URLs and skip them
163 // We use negative lookaheads to skip anything already in an <a> tag
164 var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
165 var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
166
167 // Replace standalone URLs not already in an <a> tag
168 var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
169 replacedText = replacedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
170
171 // Replace "www." prefixed URLs not already in an <a> tag
172 var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
173 replacedText = replacedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
174
175 return replacedText;
176 }
177
178
179 function scrollElementToTop(element) {
180 var chatBox = $('#chat-box');
181 var elementTop = element.position().top + chatBox.scrollTop();
182 chatBox.animate({ scrollTop: elementTop }, 500);
183 }
184
185
186 // Optimized scrollToBottom function for instant scrolling
187 function scrollToBottom(instant = false) {
188 var chatBox = $('#chat-box');
189 if (instant) {
190 // Instantly set the scroll position to the bottom
191 chatBox.scrollTop(chatBox.prop("scrollHeight"));
192 } else {
193 // Use requestAnimationFrame for smoother scrolling if needed
194 let start = null;
195 const scrollHeight = chatBox.prop("scrollHeight");
196 const initialScroll = chatBox.scrollTop();
197 const distance = scrollHeight - initialScroll;
198 const duration = 500; // Duration in ms
199
200 function smoothScroll(timestamp) {
201 if (!start) start = timestamp;
202 const progress = timestamp - start;
203 const currentScroll = initialScroll + (distance * (progress / duration));
204 chatBox.scrollTop(currentScroll);
205
206 if (progress < duration) {
207 requestAnimationFrame(smoothScroll);
208 } else {
209 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
210 }
211 }
212
213 requestAnimationFrame(smoothScroll);
214 }
215 }
216
217
218 // Function to format text with **bold** inside double asterisks
219 function formatBoldText(text) {
220 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
221 }
222
223 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
224 function convertNewlinesToBreaks(text) {
225 var lines = text.split('\n');
226 var formattedText = '';
227
228 for (var i = 0; i < lines.length; i++) {
229 formattedText += lines[i] + '<br>';
230 }
231
232 return formattedText;
233 }
234
235 // Copy to clipboard function
236 // Function to copy text to clipboard
237 function copyToClipboard(text) {
238 var tempInput = $('<input>');
239 $('body').append(tempInput);
240 tempInput.val(text).select();
241 document.execCommand('copy');
242 tempInput.remove();
243 }
244
245
246 function updateChatModeIndicator(mode) {
247 const indicator = document.getElementById('chat-mode-indicator');
248 if (indicator) {
249 indicator.textContent = mode === 'agent' ? 'Live Agent' : 'AI Agent';
250 }
251
252 // Start or stop polling based on mode
253 if (mode === 'agent') {
254 startPolling();
255 } else {
256 stopPolling();
257 }
258 }
259
260 function callMxChat(message, callback) {
261 //console.log("Sending message to chatbot:", message);
262
263 $.ajax({
264 url: mxchatChat.ajax_url,
265 type: 'POST',
266 dataType: 'json',
267 data: {
268 action: 'mxchat_handle_chat_request',
269 message: message,
270 session_id: getChatSession(),
271 nonce: mxchatChat.nonce
272 },
273 success: function(response) {
274 //console.log("callMxChat response:", response);
275
276 // Check for chat_mode in the response
277 if (response.chat_mode) {
278 updateChatModeIndicator(response.chat_mode);
279 }
280 // Also check in fallbackResponse if exists
281 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
282 updateChatModeIndicator(response.fallbackResponse.chat_mode);
283 }
284
285 // Add redirect check here
286 if (response.redirect_url) {
287 // Show the message first
288 let responseText = response.text || '';
289 if (responseText) {
290 replaceLastMessage("bot", responseText);
291 }
292 // Then redirect after a short delay
293 setTimeout(() => {
294 window.location.href = response.redirect_url;
295 }, 1500);
296 return; // Exit early since we're redirecting
297 }
298
299 // Check for live agent response
300 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
301 updateChatModeIndicator('agent');
302 // Do not replace the thinking dots; just wait for the agent's actual response
303 return;
304 }
305
306 // Handle other responses
307 let responseText = response.text || '';
308 let responseHtml = response.html || '';
309 let responseMessage = response.message || '';
310
311 // Check for mode change in text response
312 if (responseText === 'You are now chatting with the AI chatbot.') {
313 updateChatModeIndicator('ai');
314 }
315
316 // For product card or chatbot responses with HTML
317 if (responseText && responseHtml) {
318 replaceLastMessage("bot", responseText, responseHtml);
319 }
320 // For regular chatbot responses with just text
321 else if (responseText) {
322 replaceLastMessage("bot", responseText);
323 }
324 // For responses with only HTML (like product cards)
325 else if (responseHtml) {
326 replaceLastMessage("bot", "", responseHtml);
327 }
328 // For legacy message format
329 else if (responseMessage) {
330 replaceLastMessage("bot", responseMessage);
331 }
332 // Fallback error case
333 else {
334 console.error("Unexpected response format:", response);
335 replaceLastMessage("bot", "I'm sorry, something went wrong.");
336 }
337
338 if (response.message_id) {
339 lastSeenMessageId = response.message_id;
340 }
341 },
342 error: function(xhr, status, error) {
343 //console.log("Error communicating with the server:", xhr.status, error);
344 replaceLastMessage("bot", "An unexpected error occurred.");
345 }
346 });
347 }
348
349 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
350 //console.log("Appending message. Sender:", sender, "Content:", messageText);
351
352 try {
353 // Determine styles based on sender type
354 let messageClass, bgColor, fontColor;
355
356 if (sender === "user") {
357 messageClass = "user-message";
358 bgColor = userMessageBgColor;
359 fontColor = userMessageFontColor;
360 } else if (sender === "agent") {
361 messageClass = "agent-message";
362 bgColor = liveAgentMessageBgColor;
363 fontColor = liveAgentMessageFontColor;
364 } else {
365 messageClass = "bot-message";
366 bgColor = botMessageBgColor;
367 fontColor = botMessageFontColor;
368 }
369
370 const messageDiv = $('<div>')
371 .addClass(messageClass)
372 .css({
373 'background': bgColor,
374 'color': fontColor,
375 });
376
377 // Format and process the message content
378 let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
379
380 // Add images if provided
381 if (images && images.length > 0) {
382 fullMessage += '<div class="image-gallery">';
383 images.forEach(img => {
384 fullMessage += `
385 <div style="margin-bottom: 10px;">
386 <strong>${img.title}</strong><br>
387 <a href="${img.image_url}" target="_blank">
388 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
389 </a>
390 </div>`;
391 });
392 fullMessage += '</div>';
393 }
394
395 // Append HTML content if provided
396 if (messageHtml) {
397 fullMessage += '<br><br>' + messageHtml;
398 }
399
400 messageDiv.html(fullMessage);
401
402 // Add a class for temporary messages if needed
403 if (isTemporary) {
404 messageDiv.addClass('temporary-message');
405 }
406
407 // Append the message to the chat box
408 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
409 if (sender === "bot") {
410 // After bot's message is displayed, scroll the last user message to the top
411 const lastUserMessage = $('#chat-box').find('.user-message').last();
412 if (lastUserMessage.length) {
413 scrollElementToTop(lastUserMessage);
414 }
415 }
416 });
417
418 // Update the last seen message ID if applicable
419 if (messageText.id) {
420 lastSeenMessageId = messageText.id;
421 }
422 } catch (error) {
423 console.error("Error rendering message with images:", error);
424 }
425 }
426
427
428 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
429 var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
430 var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();
431
432 // Determine styles
433 let bgColor, fontColor;
434 if (sender === "user") {
435 bgColor = userMessageBgColor;
436 fontColor = userMessageFontColor;
437 } else if (sender === "agent") {
438 bgColor = liveAgentMessageBgColor;
439 fontColor = liveAgentMessageFontColor;
440 } else {
441 bgColor = botMessageBgColor;
442 fontColor = botMessageFontColor;
443 }
444
445 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
446 if (responseHtml) {
447 fullMessage += '<br><br>' + responseHtml;
448 }
449
450 if (images.length > 0) {
451 fullMessage += '<div class="image-gallery">';
452 images.forEach(img => {
453 fullMessage += `
454 <div style="margin-bottom: 10px;">
455 <strong>${img.title}</strong><br>
456 <a href="${img.image_url}" target="_blank">
457 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
458 </a>
459 </div>`;
460 });
461 fullMessage += '</div>';
462 }
463
464 if (lastMessageDiv.length) {
465 lastMessageDiv.fadeOut(200, function() {
466 $(this)
467 .html(fullMessage)
468 .removeClass('bot-message user-message')
469 .addClass(messageClass)
470 .css({
471 'background-color': bgColor,
472 'color': fontColor,
473 })
474 .removeClass('temporary-message')
475 .fadeIn(200);
476 });
477 } else {
478 appendMessage(sender, responseText, responseHtml, images);
479 }
480 }
481
482
483
484 function startPolling() {
485 // Clear any existing interval first
486 stopPolling();
487 // Start new polling interval
488 pollingInterval = setInterval(checkForAgentMessages, 5000);
489 //console.log("Started agent message polling");
490 }
491
492 function stopPolling() {
493 if (pollingInterval) {
494 clearInterval(pollingInterval);
495 pollingInterval = null;
496 //console.log("Stopped agent message polling");
497 }
498 }
499
500
501 function checkForAgentMessages() {
502 const sessionId = getChatSession();
503
504 $.ajax({
505 url: mxchatChat.ajax_url,
506 type: 'POST',
507 dataType: 'json',
508 data: {
509 action: 'mxchat_fetch_new_messages',
510 session_id: sessionId,
511 last_seen_id: lastSeenMessageId,
512 nonce: mxchatChat.nonce
513 },
514 success: function (response) {
515 //console.log("Agent messages polling response:", response);
516 if (response.success && response.data?.new_messages) {
517 response.data.new_messages.forEach(function (message) {
518 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
519 replaceLastMessage("agent", message.content);
520 lastSeenMessageId = message.id;
521 processedMessageIds.add(message.id);
522 }
523 });
524 scrollToBottom(true);
525 }
526 },
527 error: function (xhr, status, error) {
528 console.error("Polling error:", xhr, status, error);
529 }
530 });
531 }
532 function loadChatHistory() {
533 var sessionId = getChatSession();
534 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
535
536 if (chatPersistenceEnabled && sessionId) {
537 $.ajax({
538 url: mxchatChat.ajax_url,
539 type: 'POST',
540 dataType: 'json',
541 data: {
542 action: 'mxchat_fetch_conversation_history',
543 session_id: sessionId
544 },
545 success: function(response) {
546 if (response.success && response.data && Array.isArray(response.data.conversation)) {
547
548
549 var $chatBox = $('#chat-box');
550 var $fragment = $(document.createDocumentFragment());
551 let highestMessageId = lastSeenMessageId;
552
553 if (response.data.chat_mode) {
554 updateChatModeIndicator(response.data.chat_mode);
555 }
556
557 $.each(response.data.conversation, function(index, message) {
558 // Skip agent messages if persistence is off
559 if (!chatPersistenceEnabled && message.role === 'agent') {
560 return;
561 }
562
563 var messageClass, messageBgColor, messageFontColor;
564
565 switch (message.role) {
566 case 'user':
567 messageClass = 'user-message';
568 messageBgColor = userMessageBgColor;
569 messageFontColor = userMessageFontColor;
570 break;
571 case 'agent':
572 messageClass = 'agent-message';
573 messageBgColor = liveAgentMessageBgColor;
574 messageFontColor = liveAgentMessageFontColor;
575 break;
576 default:
577 messageClass = 'bot-message';
578 messageBgColor = botMessageBgColor;
579 messageFontColor = botMessageFontColor;
580 break;
581 }
582
583 var messageElement = $('<div>').addClass(messageClass)
584 .css({
585 'background': messageBgColor,
586 'color': messageFontColor
587 });
588
589 var content = message.content;
590 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
591 content = decodeHTMLEntities(content);
592
593 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
594 messageElement.html(content);
595 } else {
596 var formattedContent = linkify(
597 formatBoldText(
598 convertNewlinesToBreaks(formatCodeBlocks(content))
599 )
600 );
601 messageElement.html(formattedContent);
602 }
603
604 $fragment.append(messageElement);
605
606 // In loadChatHistory, change this part:
607 if (message.id) {
608 highestMessageId = Math.max(highestMessageId, message.id);
609 processedMessageIds.add(message.id); // Add all message IDs to processed set
610 }
611 });
612
613 $chatBox.append($fragment);
614 scrollToBottom(true);
615
616 if (response.data.conversation.length > 0) {
617 $('#mxchat-popular-questions').hide();
618 }
619
620 // Update lastSeenMessageId after history loads
621 lastSeenMessageId = highestMessageId;
622
623 // Only update chat mode if persistence is enabled
624 if (chatPersistenceEnabled && response.data.conversation.length > 0) {
625 var lastMessage = response.data.conversation[response.data.conversation.length - 1];
626 if (lastMessage.role === 'agent') {
627 updateChatModeIndicator('agent');
628 }
629 }
630 } else {
631 console.warn("No conversation history found.");
632 }
633 },
634 error: function(xhr, status, error) {
635 console.error("Error loading chat history:", status, error);
636 appendMessage("bot", "Unable to load chat history.");
637 }
638 });
639 } else {
640 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
641 }
642 }
643
644 // Function to decode HTML entities
645 function decodeHTMLEntities(text) {
646 var textArea = document.createElement('textarea');
647 textArea.innerHTML = text;
648 return textArea.value;
649 }
650
651
652 function formatCodeBlocks(text) {
653 // Ensure the input is a string; otherwise, convert or return empty
654 if (typeof text !== 'string') {
655 console.error("formatCodeBlocks: Input is not a string:", text);
656 return typeof text === 'object' && text.text ? text.text : ""; // Use .text if available, else empty
657 }
658
659 const codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
660
661 return text.replace(codeBlockPattern, (_, language, codeContent) => {
662 language = language || 'plaintext';
663
664 return `
665 <div class="mxchat-code-block-container">
666 <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
667 <pre class="mxchat-code-block"><code class="mxchat-language-${language}">${escapeHtml(codeContent)}</code></pre>
668 </div>`;
669 });
670 }
671
672
673 // Utility function to escape HTML
674 function escapeHtml(unsafe) {
675 return unsafe
676 .replace(/&/g, "&amp;")
677 .replace(/</g, "&lt;")
678 .replace(/>/g, "&gt;")
679 .replace(/"/g, "&quot;")
680 .replace(/'/g, "&#039;");
681 }
682
683
684
685
686 // Function to convert newlines, skipping preformatted text
687 function convertNewlinesToBreaks(text) {
688 // Regex to exclude <pre> and <code> tags from adding <br> tags
689 return text.replace(/(^|[^>])\n/g, '$1<br>');
690 }
691
692
693
694 $(document).ready(function() {
695 loadChatHistory();
696 });
697
698
699
700 // Helper function to check if a string is an image HTML
701 function isImageHtml(str) {
702 return str.startsWith('<img') && str.endsWith('>');
703 }
704
705 // Function to remove thinking dots
706 function removeThinkingDots() {
707 $('.thinking-dots').closest('.temporary-message').remove();
708 }
709
710 function isMobile() {
711 // This can be a simple check, or more sophisticated detection of mobile devices
712 return window.innerWidth <= 768; // Example threshold for mobile devices
713 }
714
715 function disableScroll() {
716 if (isMobile()) {
717 $('body').css('overflow', 'hidden');
718 }
719 }
720
721 function enableScroll() {
722 if (isMobile()) {
723 $('body').css('overflow', '');
724 }
725 }
726
727 // Function to show the chatbot widget (moved outside the Complianz logic)
728 function showChatWidget() {
729 setTimeout(function() {
730 $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
731 }, 250);
732 }
733
734 // Function to hide the chatbot widget
735 function hideChatWidget() {
736 $('#floating-chatbot-button').css('display', 'none');
737 }
738
739 // Pre-chat dismissal check function (wrapped in a function for reuse)
740 function checkPreChatDismissal() {
741 $.ajax({
742 url: mxchatChat.ajax_url,
743 type: 'POST',
744 data: {
745 action: 'mxchat_check_pre_chat_message_status',
746 _ajax_nonce: mxchatChat.nonce
747 },
748 success: function(response) {
749 if (response.success && !response.data.dismissed) {
750 $('#pre-chat-message').fadeIn(250);
751 } else {
752 $('#pre-chat-message').hide();
753 }
754 },
755 error: function() {
756 console.error('Failed to check pre-chat message dismissal status.');
757 }
758 });
759 }
760
761 // Function to dismiss pre-chat message for 24 hours
762 function handlePreChatDismissal() {
763 $('#pre-chat-message').fadeOut(200);
764 $.ajax({
765 url: mxchatChat.ajax_url,
766 type: 'POST',
767 data: {
768 action: 'mxchat_dismiss_pre_chat_message',
769 _ajax_nonce: mxchatChat.nonce
770 },
771 success: function() {
772 $('#pre-chat-message').hide();
773 },
774 error: function() {
775 console.error('Failed to dismiss pre-chat message.');
776 }
777 });
778 }
779
780 // Handle pre-chat message dismissal on button click
781 $(document).on('click', '.close-pre-chat-message', function(e) {
782 e.stopPropagation();
783 handlePreChatDismissal();
784 });
785
786 // Function for Complianz logic
787 var applyComplianzLogic = mxchatChat.complianz_toggle;
788 if (applyComplianzLogic) {
789 function checkConsentAndShowChat() {
790 var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
791 var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
792
793 // Show the chatbot by default
794 showChatWidget();
795
796 if (consentType === 'optin' && !consentStatus) {
797 // For opt-in, hide only if user explicitly denies consent
798 hideChatWidget();
799 } else if (consentType === 'optout' && consentStatus === false) {
800 // For opt-out, hide only if user explicitly denies consent
801 hideChatWidget();
802 } else {
803 // Keep showing the chatbot
804 showChatWidget();
805 }
806
807 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
808 }
809
810 // Initial check when the page loads
811 checkConsentAndShowChat();
812
813 // Listen for changes in consent status
814 $(document).on('cmplz_status_change', function(event, category) {
815 if (category === 'marketing') {
816 checkConsentAndShowChat();
817 }
818 });
819 } else {
820 // If Complianz is not toggled on, always show the chatbot
821 showChatWidget();
822 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
823 }
824
825 // Toggle chatbot visibility on floating button click
826 $(document).on('click', '#floating-chatbot-button', function() {
827 var chatbot = $('#floating-chatbot');
828 if (chatbot.hasClass('hidden')) {
829 chatbot.removeClass('hidden').addClass('visible');
830 $(this).addClass('hidden');
831 disableScroll();
832 // Hide the pre-chat message without dismissing it
833 $('#pre-chat-message').fadeOut(250);
834 } else {
835 chatbot.removeClass('visible').addClass('hidden');
836 $(this).removeClass('hidden');
837 enableScroll();
838 // Show the pre-chat message again if it hasn't been dismissed
839 checkPreChatDismissal();
840 }
841 });
842
843 $(document).on('click', '#exit-chat-button', function() {
844 $('#floating-chatbot').addClass('hidden').removeClass('visible');
845 $('#floating-chatbot-button').removeClass('hidden');
846 enableScroll();
847 });
848
849 // Close pre-chat message on click
850 $(document).on('click', '.close-pre-chat-message', function(e) {
851 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
852 $('#pre-chat-message').fadeOut(200, function() {
853 $(this).remove();
854 });
855 });
856
857 // Open chatbot when pre-chat message is clicked
858 $(document).on('click', '#pre-chat-message', function() {
859 var chatbot = $('#floating-chatbot');
860 if (chatbot.hasClass('hidden')) {
861 chatbot.removeClass('hidden').addClass('visible');
862 $('#floating-chatbot-button').addClass('hidden');
863 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
864 disableScroll(); // Disable scroll when chatbot opens
865 }
866 });
867
868 // If the chatbot is initially hidden, ensure the button is visible
869 if ($('#floating-chatbot').hasClass('hidden')) {
870 $('#floating-chatbot-button').removeClass('hidden');
871 }
872
873 function setFullHeight() {
874 var vh = $(window).innerHeight() * 0.01;
875 $(':root').css('--vh', vh + 'px');
876 }
877
878 // Set the height when the page loads
879 $(document).ready(function() {
880 setFullHeight();
881 });
882
883 // Set the height on resize and orientation change events
884 $(window).on('resize orientationchange', function() {
885 setFullHeight();
886 });
887
888
889 // Now handle the close button to dismiss the pre-chat message for 24 hours
890 var closeButton = document.querySelector('.close-pre-chat-message');
891 if (closeButton) {
892 closeButton.addEventListener('click', function() {
893 $('#pre-chat-message').fadeOut(200); // Hide the message
894
895 // Send an AJAX request to set the transient flag for 24 hours
896 $.ajax({
897 url: mxchatChat.ajax_url,
898 type: 'POST',
899 data: {
900 action: 'mxchat_dismiss_pre_chat_message',
901 _ajax_nonce: mxchatChat.nonce
902 },
903 success: function() {
904 //console.log('Pre-chat message dismissed for 24 hours.');
905
906 // Ensure the message is hidden after dismissal
907 $('#pre-chat-message').hide();
908 },
909 error: function() {
910 //console.error('Failed to dismiss pre-chat message.');
911 }
912 });
913 });
914 }
915
916
917
918
919 // Event listener for Add to Cart button
920 $(document).on('click', '.mxchat-add-to-cart-button', function() {
921 var productId = $(this).data('product-id'); // Get product ID from data attribute
922
923 // Simulate user message first for proper ordering
924 appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
925
926
927 // Use existing function to send the "add to cart" command to the chatbot
928 sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
929 });
930
931
932 // PDF Upload button click handler
933 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
934 document.getElementById('pdf-upload').click();
935 });
936
937 // PDF file input change handler
938 document.getElementById('pdf-upload').addEventListener('change', async function(e) {
939 const file = e.target.files[0];
940
941 if (!file || file.type !== 'application/pdf') {
942 alert('Please select a valid PDF file.');
943 return;
944 }
945
946 if (!sessionId) {
947 console.error('No session ID found');
948 alert('Error: No session ID found');
949 return;
950 }
951
952 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
953 console.error('mxchatChat not properly configured:', mxchatChat);
954 alert('Error: Ajax configuration missing');
955 return;
956 }
957
958 // Disable buttons and show loading state
959 const uploadBtn = document.getElementById('pdf-upload-btn');
960 const sendBtn = document.getElementById('send-button');
961 const originalBtnContent = uploadBtn.innerHTML;
962
963 try {
964 const formData = new FormData();
965 formData.append('action', 'mxchat_upload_pdf');
966 formData.append('pdf_file', file);
967 formData.append('session_id', sessionId);
968 formData.append('nonce', mxchatChat.nonce);
969
970 uploadBtn.disabled = true;
971 sendBtn.disabled = true;
972 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
973 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
974 </svg>`;
975
976 const response = await fetch(mxchatChat.ajax_url, {
977 method: 'POST',
978 body: formData
979 });
980
981 const data = await response.json();
982
983 if (data.success) {
984 // Hide popular questions if they exist
985 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
986 if (popularQuestionsContainer) {
987 popularQuestionsContainer.style.display = 'none';
988 }
989
990 // Show the active PDF name
991 showActivePdf(data.data.filename);
992
993 appendMessage('bot', data.data.message);
994 scrollToBottom();
995 activePdfFile = data.data.filename;
996 } else {
997 console.error('Upload failed:', data.data);
998 alert('Failed to upload PDF. Please try again.');
999 }
1000 } catch (error) {
1001 console.error('Upload error:', error);
1002 alert('Error uploading file. Please try again.');
1003 } finally {
1004 uploadBtn.disabled = false;
1005 sendBtn.disabled = false;
1006 uploadBtn.innerHTML = originalBtnContent;
1007 this.value = ''; // Reset file input
1008 }
1009 });
1010
1011 // Function to show active PDF name in toolbar
1012 function showActivePdf(filename) {
1013 const container = document.getElementById('active-pdf-container');
1014 const nameElement = document.getElementById('active-pdf-name');
1015
1016 if (!container || !nameElement) {
1017 console.error('PDF container elements not found');
1018 return;
1019 }
1020
1021 nameElement.textContent = filename;
1022 container.style.display = 'flex';
1023 }
1024
1025 // Function to remove active PDF
1026 function removeActivePdf() {
1027 const container = document.getElementById('active-pdf-container');
1028 const nameElement = document.getElementById('active-pdf-name');
1029
1030 if (!container || !nameElement || !activePdfFile) return;
1031
1032 fetch(mxchatChat.ajax_url, {
1033 method: 'POST',
1034 headers: {
1035 'Content-Type': 'application/x-www-form-urlencoded',
1036 },
1037 body: new URLSearchParams({
1038 'action': 'mxchat_remove_pdf',
1039 'session_id': sessionId,
1040 'nonce': mxchatChat.nonce
1041 })
1042 })
1043 .then(response => response.json())
1044 .then(data => {
1045 if (data.success) {
1046 container.style.display = 'none';
1047 nameElement.textContent = '';
1048 activePdfFile = null;
1049 appendMessage('bot', 'PDF removed.');
1050 }
1051 })
1052 .catch(error => {
1053 console.error('Error removing PDF:', error);
1054 });
1055 }
1056
1057 // Add remove button click handler
1058 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1059 e.preventDefault();
1060 e.stopPropagation();
1061 removeActivePdf();
1062 });
1063
1064 // Check initial PDF status on load
1065 function checkInitialPdfStatus() {
1066 if (!sessionId) return;
1067
1068 fetch(mxchatChat.ajax_url, {
1069 method: 'POST',
1070 headers: {
1071 'Content-Type': 'application/x-www-form-urlencoded',
1072 },
1073 body: new URLSearchParams({
1074 'action': 'mxchat_check_pdf_status',
1075 'session_id': sessionId,
1076 'nonce': mxchatChat.nonce
1077 })
1078 })
1079 .then(response => response.json())
1080 .then(data => {
1081 if (data.success && data.data.filename) {
1082 showActivePdf(data.data.filename);
1083 activePdfFile = data.data.filename;
1084 }
1085 })
1086 .catch(error => {
1087 console.error('Error checking PDF status:', error);
1088 });
1089 }
1090
1091 // Apply toolbar settings
1092 if (mxchatChat.chat_toolbar_toggle === 'on') {
1093 $('.chat-toolbar').show();
1094 } else {
1095 $('.chat-toolbar').hide();
1096 }
1097
1098 // Initialize on page load
1099 document.addEventListener('DOMContentLoaded', function() {
1100 checkInitialPdfStatus();
1101 });
1102
1103 // Style all toolbar elements
1104 const toolbarElements = [
1105 '#mxchat-chatbot .toolbar-btn svg',
1106 '#mxchat-chatbot .active-pdf-name',
1107 '#mxchat-chatbot .remove-pdf-btn svg'
1108 ];
1109 $(toolbarElements.join(', ')).css({
1110 'fill': toolbarIconColor,
1111 'color': toolbarIconColor
1112 });
1113
1114
1115
1116 });
1117
1118 // Event listener for copy button
1119 document.addEventListener("click", (e) => {
1120 if (e.target.classList.contains("mxchat-copy-button")) {
1121 const copyButton = e.target;
1122 const codeBlock = copyButton
1123 .closest(".mxchat-code-block-container")
1124 .querySelector(".mxchat-code-block code");
1125
1126 if (codeBlock) {
1127 // Preserve formatting using innerText
1128 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1129 copyButton.textContent = "Copied!";
1130 copyButton.setAttribute("aria-label", "Copied to clipboard");
1131
1132 setTimeout(() => {
1133 copyButton.textContent = "Copy";
1134 copyButton.setAttribute("aria-label", "Copy to clipboard");
1135 }, 2000);
1136 });
1137 }
1138 }
1139 });
1140