PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.5.1
MxChat – AI Chatbot & Content Generation for WordPress v1.5.1
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.1, at js/chat-script.js

1,126 lines 38.7 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 // Check for live agent response
286 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
287 updateChatModeIndicator('agent');
288 // Do not replace the thinking dots; just wait for the agent's actual response
289 return;
290 }
291
292 // Handle other responses
293 let responseText = response.text || '';
294 let responseHtml = response.html || '';
295 let responseMessage = response.message || '';
296
297 // Check for mode change in text response
298 if (responseText === 'You are now chatting with the AI chatbot.') {
299 updateChatModeIndicator('ai');
300 }
301
302 // For product card or chatbot responses with HTML
303 if (responseText && responseHtml) {
304 replaceLastMessage("bot", responseText, responseHtml);
305 }
306 // For regular chatbot responses with just text
307 else if (responseText) {
308 replaceLastMessage("bot", responseText);
309 }
310 // For responses with only HTML (like product cards)
311 else if (responseHtml) {
312 replaceLastMessage("bot", "", responseHtml);
313 }
314 // For legacy message format
315 else if (responseMessage) {
316 replaceLastMessage("bot", responseMessage);
317 }
318 // Fallback error case
319 else {
320 console.error("Unexpected response format:", response);
321 replaceLastMessage("bot", "I'm sorry, something went wrong.");
322 }
323
324 if (response.message_id) {
325 lastSeenMessageId = response.message_id;
326 }
327 },
328 error: function(xhr, status, error) {
329 //console.log("Error communicating with the server:", xhr.status, error);
330 replaceLastMessage("bot", "An unexpected error occurred.");
331 }
332 });
333 }
334
335 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
336 //console.log("Appending message. Sender:", sender, "Content:", messageText);
337
338 try {
339 // Determine styles based on sender type
340 let messageClass, bgColor, fontColor;
341
342 if (sender === "user") {
343 messageClass = "user-message";
344 bgColor = userMessageBgColor;
345 fontColor = userMessageFontColor;
346 } else if (sender === "agent") {
347 messageClass = "agent-message";
348 bgColor = liveAgentMessageBgColor;
349 fontColor = liveAgentMessageFontColor;
350 } else {
351 messageClass = "bot-message";
352 bgColor = botMessageBgColor;
353 fontColor = botMessageFontColor;
354 }
355
356 const messageDiv = $('<div>')
357 .addClass(messageClass)
358 .css({
359 'background': bgColor,
360 'color': fontColor,
361 });
362
363 // Format and process the message content
364 let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
365
366 // Add images if provided
367 if (images && images.length > 0) {
368 fullMessage += '<div class="image-gallery">';
369 images.forEach(img => {
370 fullMessage += `
371 <div style="margin-bottom: 10px;">
372 <strong>${img.title}</strong><br>
373 <a href="${img.image_url}" target="_blank">
374 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
375 </a>
376 </div>`;
377 });
378 fullMessage += '</div>';
379 }
380
381 // Append HTML content if provided
382 if (messageHtml) {
383 fullMessage += '<br><br>' + messageHtml;
384 }
385
386 messageDiv.html(fullMessage);
387
388 // Add a class for temporary messages if needed
389 if (isTemporary) {
390 messageDiv.addClass('temporary-message');
391 }
392
393 // Append the message to the chat box
394 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
395 if (sender === "bot") {
396 // After bot's message is displayed, scroll the last user message to the top
397 const lastUserMessage = $('#chat-box').find('.user-message').last();
398 if (lastUserMessage.length) {
399 scrollElementToTop(lastUserMessage);
400 }
401 }
402 });
403
404 // Update the last seen message ID if applicable
405 if (messageText.id) {
406 lastSeenMessageId = messageText.id;
407 }
408 } catch (error) {
409 console.error("Error rendering message with images:", error);
410 }
411 }
412
413
414 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
415 var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
416 var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();
417
418 // Determine styles
419 let bgColor, fontColor;
420 if (sender === "user") {
421 bgColor = userMessageBgColor;
422 fontColor = userMessageFontColor;
423 } else if (sender === "agent") {
424 bgColor = liveAgentMessageBgColor;
425 fontColor = liveAgentMessageFontColor;
426 } else {
427 bgColor = botMessageBgColor;
428 fontColor = botMessageFontColor;
429 }
430
431 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
432 if (responseHtml) {
433 fullMessage += '<br><br>' + responseHtml;
434 }
435
436 if (images.length > 0) {
437 fullMessage += '<div class="image-gallery">';
438 images.forEach(img => {
439 fullMessage += `
440 <div style="margin-bottom: 10px;">
441 <strong>${img.title}</strong><br>
442 <a href="${img.image_url}" target="_blank">
443 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
444 </a>
445 </div>`;
446 });
447 fullMessage += '</div>';
448 }
449
450 if (lastMessageDiv.length) {
451 lastMessageDiv.fadeOut(200, function() {
452 $(this)
453 .html(fullMessage)
454 .removeClass('bot-message user-message')
455 .addClass(messageClass)
456 .css({
457 'background-color': bgColor,
458 'color': fontColor,
459 })
460 .removeClass('temporary-message')
461 .fadeIn(200);
462 });
463 } else {
464 appendMessage(sender, responseText, responseHtml, images);
465 }
466 }
467
468
469
470 function startPolling() {
471 // Clear any existing interval first
472 stopPolling();
473 // Start new polling interval
474 pollingInterval = setInterval(checkForAgentMessages, 5000);
475 //console.log("Started agent message polling");
476 }
477
478 function stopPolling() {
479 if (pollingInterval) {
480 clearInterval(pollingInterval);
481 pollingInterval = null;
482 //console.log("Stopped agent message polling");
483 }
484 }
485
486
487 function checkForAgentMessages() {
488 const sessionId = getChatSession();
489
490 $.ajax({
491 url: mxchatChat.ajax_url,
492 type: 'POST',
493 dataType: 'json',
494 data: {
495 action: 'mxchat_fetch_new_messages',
496 session_id: sessionId,
497 last_seen_id: lastSeenMessageId,
498 nonce: mxchatChat.nonce
499 },
500 success: function (response) {
501 //console.log("Agent messages polling response:", response);
502 if (response.success && response.data?.new_messages) {
503 response.data.new_messages.forEach(function (message) {
504 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
505 replaceLastMessage("agent", message.content);
506 lastSeenMessageId = message.id;
507 processedMessageIds.add(message.id);
508 }
509 });
510 scrollToBottom(true);
511 }
512 },
513 error: function (xhr, status, error) {
514 console.error("Polling error:", xhr, status, error);
515 }
516 });
517 }
518 function loadChatHistory() {
519 var sessionId = getChatSession();
520 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
521
522 if (chatPersistenceEnabled && sessionId) {
523 $.ajax({
524 url: mxchatChat.ajax_url,
525 type: 'POST',
526 dataType: 'json',
527 data: {
528 action: 'mxchat_fetch_conversation_history',
529 session_id: sessionId
530 },
531 success: function(response) {
532 if (response.success && response.data && Array.isArray(response.data.conversation)) {
533
534
535 var $chatBox = $('#chat-box');
536 var $fragment = $(document.createDocumentFragment());
537 let highestMessageId = lastSeenMessageId;
538
539 if (response.data.chat_mode) {
540 updateChatModeIndicator(response.data.chat_mode);
541 }
542
543 $.each(response.data.conversation, function(index, message) {
544 // Skip agent messages if persistence is off
545 if (!chatPersistenceEnabled && message.role === 'agent') {
546 return;
547 }
548
549 var messageClass, messageBgColor, messageFontColor;
550
551 switch (message.role) {
552 case 'user':
553 messageClass = 'user-message';
554 messageBgColor = userMessageBgColor;
555 messageFontColor = userMessageFontColor;
556 break;
557 case 'agent':
558 messageClass = 'agent-message';
559 messageBgColor = liveAgentMessageBgColor;
560 messageFontColor = liveAgentMessageFontColor;
561 break;
562 default:
563 messageClass = 'bot-message';
564 messageBgColor = botMessageBgColor;
565 messageFontColor = botMessageFontColor;
566 break;
567 }
568
569 var messageElement = $('<div>').addClass(messageClass)
570 .css({
571 'background': messageBgColor,
572 'color': messageFontColor
573 });
574
575 var content = message.content;
576 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
577 content = decodeHTMLEntities(content);
578
579 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
580 messageElement.html(content);
581 } else {
582 var formattedContent = linkify(
583 formatBoldText(
584 convertNewlinesToBreaks(formatCodeBlocks(content))
585 )
586 );
587 messageElement.html(formattedContent);
588 }
589
590 $fragment.append(messageElement);
591
592 // In loadChatHistory, change this part:
593 if (message.id) {
594 highestMessageId = Math.max(highestMessageId, message.id);
595 processedMessageIds.add(message.id); // Add all message IDs to processed set
596 }
597 });
598
599 $chatBox.append($fragment);
600 scrollToBottom(true);
601
602 if (response.data.conversation.length > 0) {
603 $('#mxchat-popular-questions').hide();
604 }
605
606 // Update lastSeenMessageId after history loads
607 lastSeenMessageId = highestMessageId;
608
609 // Only update chat mode if persistence is enabled
610 if (chatPersistenceEnabled && response.data.conversation.length > 0) {
611 var lastMessage = response.data.conversation[response.data.conversation.length - 1];
612 if (lastMessage.role === 'agent') {
613 updateChatModeIndicator('agent');
614 }
615 }
616 } else {
617 console.warn("No conversation history found.");
618 }
619 },
620 error: function(xhr, status, error) {
621 console.error("Error loading chat history:", status, error);
622 appendMessage("bot", "Unable to load chat history.");
623 }
624 });
625 } else {
626 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
627 }
628 }
629
630 // Function to decode HTML entities
631 function decodeHTMLEntities(text) {
632 var textArea = document.createElement('textarea');
633 textArea.innerHTML = text;
634 return textArea.value;
635 }
636
637
638 function formatCodeBlocks(text) {
639 // Ensure the input is a string; otherwise, convert or return empty
640 if (typeof text !== 'string') {
641 console.error("formatCodeBlocks: Input is not a string:", text);
642 return typeof text === 'object' && text.text ? text.text : ""; // Use .text if available, else empty
643 }
644
645 const codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
646
647 return text.replace(codeBlockPattern, (_, language, codeContent) => {
648 language = language || 'plaintext';
649
650 return `
651 <div class="mxchat-code-block-container">
652 <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
653 <pre class="mxchat-code-block"><code class="mxchat-language-${language}">${escapeHtml(codeContent)}</code></pre>
654 </div>`;
655 });
656 }
657
658
659 // Utility function to escape HTML
660 function escapeHtml(unsafe) {
661 return unsafe
662 .replace(/&/g, "&amp;")
663 .replace(/</g, "&lt;")
664 .replace(/>/g, "&gt;")
665 .replace(/"/g, "&quot;")
666 .replace(/'/g, "&#039;");
667 }
668
669
670
671
672 // Function to convert newlines, skipping preformatted text
673 function convertNewlinesToBreaks(text) {
674 // Regex to exclude <pre> and <code> tags from adding <br> tags
675 return text.replace(/(^|[^>])\n/g, '$1<br>');
676 }
677
678
679
680 $(document).ready(function() {
681 loadChatHistory();
682 });
683
684
685
686 // Helper function to check if a string is an image HTML
687 function isImageHtml(str) {
688 return str.startsWith('<img') && str.endsWith('>');
689 }
690
691 // Function to remove thinking dots
692 function removeThinkingDots() {
693 $('.thinking-dots').closest('.temporary-message').remove();
694 }
695
696 function isMobile() {
697 // This can be a simple check, or more sophisticated detection of mobile devices
698 return window.innerWidth <= 768; // Example threshold for mobile devices
699 }
700
701 function disableScroll() {
702 if (isMobile()) {
703 $('body').css('overflow', 'hidden');
704 }
705 }
706
707 function enableScroll() {
708 if (isMobile()) {
709 $('body').css('overflow', '');
710 }
711 }
712
713 // Function to show the chatbot widget (moved outside the Complianz logic)
714 function showChatWidget() {
715 setTimeout(function() {
716 $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
717 }, 250);
718 }
719
720 // Function to hide the chatbot widget
721 function hideChatWidget() {
722 $('#floating-chatbot-button').css('display', 'none');
723 }
724
725 // Pre-chat dismissal check function (wrapped in a function for reuse)
726 function checkPreChatDismissal() {
727 $.ajax({
728 url: mxchatChat.ajax_url,
729 type: 'POST',
730 data: {
731 action: 'mxchat_check_pre_chat_message_status',
732 _ajax_nonce: mxchatChat.nonce
733 },
734 success: function(response) {
735 if (response.success && !response.data.dismissed) {
736 $('#pre-chat-message').fadeIn(250);
737 } else {
738 $('#pre-chat-message').hide();
739 }
740 },
741 error: function() {
742 console.error('Failed to check pre-chat message dismissal status.');
743 }
744 });
745 }
746
747 // Function to dismiss pre-chat message for 24 hours
748 function handlePreChatDismissal() {
749 $('#pre-chat-message').fadeOut(200);
750 $.ajax({
751 url: mxchatChat.ajax_url,
752 type: 'POST',
753 data: {
754 action: 'mxchat_dismiss_pre_chat_message',
755 _ajax_nonce: mxchatChat.nonce
756 },
757 success: function() {
758 $('#pre-chat-message').hide();
759 },
760 error: function() {
761 console.error('Failed to dismiss pre-chat message.');
762 }
763 });
764 }
765
766 // Handle pre-chat message dismissal on button click
767 $(document).on('click', '.close-pre-chat-message', function(e) {
768 e.stopPropagation();
769 handlePreChatDismissal();
770 });
771
772 // Function for Complianz logic
773 var applyComplianzLogic = mxchatChat.complianz_toggle;
774 if (applyComplianzLogic) {
775 function checkConsentAndShowChat() {
776 var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
777 var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
778
779 // Show the chatbot by default
780 showChatWidget();
781
782 if (consentType === 'optin' && !consentStatus) {
783 // For opt-in, hide only if user explicitly denies consent
784 hideChatWidget();
785 } else if (consentType === 'optout' && consentStatus === false) {
786 // For opt-out, hide only if user explicitly denies consent
787 hideChatWidget();
788 } else {
789 // Keep showing the chatbot
790 showChatWidget();
791 }
792
793 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
794 }
795
796 // Initial check when the page loads
797 checkConsentAndShowChat();
798
799 // Listen for changes in consent status
800 $(document).on('cmplz_status_change', function(event, category) {
801 if (category === 'marketing') {
802 checkConsentAndShowChat();
803 }
804 });
805 } else {
806 // If Complianz is not toggled on, always show the chatbot
807 showChatWidget();
808 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
809 }
810
811 // Toggle chatbot visibility on floating button click
812 $(document).on('click', '#floating-chatbot-button', function() {
813 var chatbot = $('#floating-chatbot');
814 if (chatbot.hasClass('hidden')) {
815 chatbot.removeClass('hidden').addClass('visible');
816 $(this).addClass('hidden');
817 disableScroll();
818 // Hide the pre-chat message without dismissing it
819 $('#pre-chat-message').fadeOut(250);
820 } else {
821 chatbot.removeClass('visible').addClass('hidden');
822 $(this).removeClass('hidden');
823 enableScroll();
824 // Show the pre-chat message again if it hasn't been dismissed
825 checkPreChatDismissal();
826 }
827 });
828
829 $(document).on('click', '#exit-chat-button', function() {
830 $('#floating-chatbot').addClass('hidden').removeClass('visible');
831 $('#floating-chatbot-button').removeClass('hidden');
832 enableScroll();
833 });
834
835 // Close pre-chat message on click
836 $(document).on('click', '.close-pre-chat-message', function(e) {
837 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
838 $('#pre-chat-message').fadeOut(200, function() {
839 $(this).remove();
840 });
841 });
842
843 // Open chatbot when pre-chat message is clicked
844 $(document).on('click', '#pre-chat-message', function() {
845 var chatbot = $('#floating-chatbot');
846 if (chatbot.hasClass('hidden')) {
847 chatbot.removeClass('hidden').addClass('visible');
848 $('#floating-chatbot-button').addClass('hidden');
849 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
850 disableScroll(); // Disable scroll when chatbot opens
851 }
852 });
853
854 // If the chatbot is initially hidden, ensure the button is visible
855 if ($('#floating-chatbot').hasClass('hidden')) {
856 $('#floating-chatbot-button').removeClass('hidden');
857 }
858
859 function setFullHeight() {
860 var vh = $(window).innerHeight() * 0.01;
861 $(':root').css('--vh', vh + 'px');
862 }
863
864 // Set the height when the page loads
865 $(document).ready(function() {
866 setFullHeight();
867 });
868
869 // Set the height on resize and orientation change events
870 $(window).on('resize orientationchange', function() {
871 setFullHeight();
872 });
873
874
875 // Now handle the close button to dismiss the pre-chat message for 24 hours
876 var closeButton = document.querySelector('.close-pre-chat-message');
877 if (closeButton) {
878 closeButton.addEventListener('click', function() {
879 $('#pre-chat-message').fadeOut(200); // Hide the message
880
881 // Send an AJAX request to set the transient flag for 24 hours
882 $.ajax({
883 url: mxchatChat.ajax_url,
884 type: 'POST',
885 data: {
886 action: 'mxchat_dismiss_pre_chat_message',
887 _ajax_nonce: mxchatChat.nonce
888 },
889 success: function() {
890 //console.log('Pre-chat message dismissed for 24 hours.');
891
892 // Ensure the message is hidden after dismissal
893 $('#pre-chat-message').hide();
894 },
895 error: function() {
896 //console.error('Failed to dismiss pre-chat message.');
897 }
898 });
899 });
900 }
901
902
903
904
905 // Event listener for Add to Cart button
906 $(document).on('click', '.mxchat-add-to-cart-button', function() {
907 var productId = $(this).data('product-id'); // Get product ID from data attribute
908
909 // Simulate user message first for proper ordering
910 appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
911
912
913 // Use existing function to send the "add to cart" command to the chatbot
914 sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
915 });
916
917
918 // PDF Upload button click handler
919 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
920 document.getElementById('pdf-upload').click();
921 });
922
923 // PDF file input change handler
924 document.getElementById('pdf-upload').addEventListener('change', async function(e) {
925 const file = e.target.files[0];
926
927 if (!file || file.type !== 'application/pdf') {
928 alert('Please select a valid PDF file.');
929 return;
930 }
931
932 if (!sessionId) {
933 console.error('No session ID found');
934 alert('Error: No session ID found');
935 return;
936 }
937
938 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
939 console.error('mxchatChat not properly configured:', mxchatChat);
940 alert('Error: Ajax configuration missing');
941 return;
942 }
943
944 // Disable buttons and show loading state
945 const uploadBtn = document.getElementById('pdf-upload-btn');
946 const sendBtn = document.getElementById('send-button');
947 const originalBtnContent = uploadBtn.innerHTML;
948
949 try {
950 const formData = new FormData();
951 formData.append('action', 'mxchat_upload_pdf');
952 formData.append('pdf_file', file);
953 formData.append('session_id', sessionId);
954 formData.append('nonce', mxchatChat.nonce);
955
956 uploadBtn.disabled = true;
957 sendBtn.disabled = true;
958 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
959 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
960 </svg>`;
961
962 const response = await fetch(mxchatChat.ajax_url, {
963 method: 'POST',
964 body: formData
965 });
966
967 const data = await response.json();
968
969 if (data.success) {
970 // Hide popular questions if they exist
971 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
972 if (popularQuestionsContainer) {
973 popularQuestionsContainer.style.display = 'none';
974 }
975
976 // Show the active PDF name
977 showActivePdf(data.data.filename);
978
979 appendMessage('bot', data.data.message);
980 scrollToBottom();
981 activePdfFile = data.data.filename;
982 } else {
983 console.error('Upload failed:', data.data);
984 alert('Failed to upload PDF. Please try again.');
985 }
986 } catch (error) {
987 console.error('Upload error:', error);
988 alert('Error uploading file. Please try again.');
989 } finally {
990 uploadBtn.disabled = false;
991 sendBtn.disabled = false;
992 uploadBtn.innerHTML = originalBtnContent;
993 this.value = ''; // Reset file input
994 }
995 });
996
997 // Function to show active PDF name in toolbar
998 function showActivePdf(filename) {
999 const container = document.getElementById('active-pdf-container');
1000 const nameElement = document.getElementById('active-pdf-name');
1001
1002 if (!container || !nameElement) {
1003 console.error('PDF container elements not found');
1004 return;
1005 }
1006
1007 nameElement.textContent = filename;
1008 container.style.display = 'flex';
1009 }
1010
1011 // Function to remove active PDF
1012 function removeActivePdf() {
1013 const container = document.getElementById('active-pdf-container');
1014 const nameElement = document.getElementById('active-pdf-name');
1015
1016 if (!container || !nameElement || !activePdfFile) return;
1017
1018 fetch(mxchatChat.ajax_url, {
1019 method: 'POST',
1020 headers: {
1021 'Content-Type': 'application/x-www-form-urlencoded',
1022 },
1023 body: new URLSearchParams({
1024 'action': 'mxchat_remove_pdf',
1025 'session_id': sessionId,
1026 'nonce': mxchatChat.nonce
1027 })
1028 })
1029 .then(response => response.json())
1030 .then(data => {
1031 if (data.success) {
1032 container.style.display = 'none';
1033 nameElement.textContent = '';
1034 activePdfFile = null;
1035 appendMessage('bot', 'PDF removed.');
1036 }
1037 })
1038 .catch(error => {
1039 console.error('Error removing PDF:', error);
1040 });
1041 }
1042
1043 // Add remove button click handler
1044 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1045 e.preventDefault();
1046 e.stopPropagation();
1047 removeActivePdf();
1048 });
1049
1050 // Check initial PDF status on load
1051 function checkInitialPdfStatus() {
1052 if (!sessionId) return;
1053
1054 fetch(mxchatChat.ajax_url, {
1055 method: 'POST',
1056 headers: {
1057 'Content-Type': 'application/x-www-form-urlencoded',
1058 },
1059 body: new URLSearchParams({
1060 'action': 'mxchat_check_pdf_status',
1061 'session_id': sessionId,
1062 'nonce': mxchatChat.nonce
1063 })
1064 })
1065 .then(response => response.json())
1066 .then(data => {
1067 if (data.success && data.data.filename) {
1068 showActivePdf(data.data.filename);
1069 activePdfFile = data.data.filename;
1070 }
1071 })
1072 .catch(error => {
1073 console.error('Error checking PDF status:', error);
1074 });
1075 }
1076
1077 // Apply toolbar settings
1078 if (mxchatChat.chat_toolbar_toggle === 'on') {
1079 $('.chat-toolbar').show();
1080 } else {
1081 $('.chat-toolbar').hide();
1082 }
1083
1084 // Initialize on page load
1085 document.addEventListener('DOMContentLoaded', function() {
1086 checkInitialPdfStatus();
1087 });
1088
1089 // Style all toolbar elements
1090 const toolbarElements = [
1091 '#mxchat-chatbot .toolbar-btn svg',
1092 '#mxchat-chatbot .active-pdf-name',
1093 '#mxchat-chatbot .remove-pdf-btn svg'
1094 ];
1095 $(toolbarElements.join(', ')).css({
1096 'fill': toolbarIconColor,
1097 'color': toolbarIconColor
1098 });
1099
1100
1101
1102 });
1103
1104 // Event listener for copy button
1105 document.addEventListener("click", (e) => {
1106 if (e.target.classList.contains("mxchat-copy-button")) {
1107 const copyButton = e.target;
1108 const codeBlock = copyButton
1109 .closest(".mxchat-code-block-container")
1110 .querySelector(".mxchat-code-block code");
1111
1112 if (codeBlock) {
1113 // Preserve formatting using innerText
1114 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1115 copyButton.textContent = "Copied!";
1116 copyButton.setAttribute("aria-label", "Copied to clipboard");
1117
1118 setTimeout(() => {
1119 copyButton.textContent = "Copy";
1120 copyButton.setAttribute("aria-label", "Copy to clipboard");
1121 }, 2000);
1122 });
1123 }
1124 }
1125 });
1126