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

714 lines 25.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
4 // Initialize color settings
5 var userMessageBgColor = mxchatChat.user_message_bg_color;
6 var userMessageFontColor = mxchatChat.user_message_font_color;
7 var botMessageBgColor = mxchatChat.bot_message_bg_color;
8 var botMessageFontColor = mxchatChat.bot_message_font_color;
9 var linkTarget = mxchatChat.link_target === 'on' ? '_blank' : '_self';
10
11
12
13 function getChatSession() {
14 var sessionId = getCookie('mxchat_session_id');
15 //console.log("Session ID retrieved from cookie: ", sessionId);
16
17 if (!sessionId) {
18 sessionId = generateSessionId();
19 //console.log("Generated new session ID: ", sessionId);
20 setChatSession(sessionId);
21 }
22
23 //console.log("Final session ID: ", sessionId);
24 return sessionId;
25 }
26
27 function setChatSession(sessionId) {
28 // Set the cookie with a 24-hour expiration (86400 seconds)
29 document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
30 }
31
32 // Get cookie value by name
33 function getCookie(name) {
34 let value = "; " + document.cookie;
35 let parts = value.split("; " + name + "=");
36 if (parts.length == 2) return parts.pop().split(";").shift();
37 }
38
39 // Generate a new session ID
40 function generateSessionId() {
41 return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
42 }
43
44 // Function to send the message to the chatbot (backend)
45 function sendMessageToChatbot(message) {
46 var sessionId = getChatSession(); // Reuse the session ID logic
47
48 // Hide the popular questions section
49 $('#mxchat-popular-questions').hide();
50
51 // Show thinking indicator (no need to append the user's message again)
52 appendThinkingMessage();
53 scrollToBottom();
54
55 //console.log("Sending message to chatbot:", message); // Log the message
56 //console.log("Session ID:", sessionId); // Log the session ID
57
58 // Call the chatbot using the same call logic as sendMessage
59 callMxChat(message, function(response) {
60 // ** Ensure temporary thinking message is removed before adding new response **
61 $('.temporary-message').remove();
62
63 // Replace thinking indicator with actual response
64 replaceLastMessage("bot", response);
65 });
66 }
67
68
69
70
71
72 function sendMessage() {
73 var message = $('#chat-input').val();
74 if (message) {
75 appendMessage("user", message);
76 $('#chat-input').val('');
77
78 // Hide the popular questions section
79 $('#mxchat-popular-questions').hide();
80
81 // Show typing indicator
82 appendThinkingMessage();
83 scrollToBottom();
84
85 callMxChat(message, function(response) {
86 // Replace typing indicator with actual response
87 replaceLastMessage("bot", response);
88 });
89 }
90 }
91
92
93 // Function to append a thinking message with animation
94 function appendThinkingMessage() {
95 // Remove any existing thinking dots first
96 $('.thinking-dots').remove();
97
98 // Retrieve the bot message font color and background color
99 var botMessageFontColor = mxchatChat.bot_message_font_color;
100 var botMessageBgColor = mxchatChat.bot_message_bg_color;
101
102 var thinkingHtml = '<div class="thinking-dots-container">' +
103 '<div class="thinking-dots">' +
104 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
105 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
106 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
107 '</div>' +
108 '</div>';
109
110 // Append the thinking dots to the chat container (or within the temporary message div)
111 $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
112 scrollToBottom();
113 }
114
115 // Trigger send button click when "Enter" key is pressed in the input field
116 $('#chat-input').keypress(function(e) {
117 if (e.which == 13) {
118 e.preventDefault();
119 $('#send-button').click();
120 }
121 });
122
123 // Handle send button click
124 $('#send-button').click(function() {
125 sendMessage();
126 });
127
128 // Handle click on popular questions
129 $('.mxchat-popular-question').on('click', function () {
130 var question = $(this).text(); // Get the text of the clicked question
131
132 // Append the question as if the user typed it
133 appendMessage("user", question);
134
135 // Send the question to the server (backend)
136 sendMessageToChatbot(question);
137 });
138
139
140 // Use the linkTarget in your linkify function
141 function linkify(inputText) {
142 // Check for already linked URLs and skip them
143 // We use negative lookaheads to skip anything already in an <a> tag
144 var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
145 var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
146
147 // Replace standalone URLs not already in an <a> tag
148 var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
149 replacedText = replacedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
150
151 // Replace "www." prefixed URLs not already in an <a> tag
152 var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
153 replacedText = replacedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
154
155 return replacedText;
156 }
157
158
159 function scrollElementToTop(element) {
160 var chatBox = $('#chat-box');
161 var elementTop = element.position().top + chatBox.scrollTop();
162 chatBox.animate({ scrollTop: elementTop }, 500);
163 }
164
165
166 // Optimized scrollToBottom function for instant scrolling
167 function scrollToBottom(instant = false) {
168 var chatBox = $('#chat-box');
169 if (instant) {
170 // Instantly set the scroll position to the bottom
171 chatBox.scrollTop(chatBox.prop("scrollHeight"));
172 } else {
173 // Use requestAnimationFrame for smoother scrolling if needed
174 let start = null;
175 const scrollHeight = chatBox.prop("scrollHeight");
176 const initialScroll = chatBox.scrollTop();
177 const distance = scrollHeight - initialScroll;
178 const duration = 500; // Duration in ms
179
180 function smoothScroll(timestamp) {
181 if (!start) start = timestamp;
182 const progress = timestamp - start;
183 const currentScroll = initialScroll + (distance * (progress / duration));
184 chatBox.scrollTop(currentScroll);
185
186 if (progress < duration) {
187 requestAnimationFrame(smoothScroll);
188 } else {
189 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
190 }
191 }
192
193 requestAnimationFrame(smoothScroll);
194 }
195 }
196
197
198 // Function to format text with **bold** inside double asterisks
199 function formatBoldText(text) {
200 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
201 }
202
203 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
204 function convertNewlinesToBreaks(text) {
205 var lines = text.split('\n');
206 var formattedText = '';
207
208 for (var i = 0; i < lines.length; i++) {
209 formattedText += lines[i] + '<br>';
210 }
211
212 return formattedText;
213 }
214
215 // Copy to clipboard function
216 // Function to copy text to clipboard
217 function copyToClipboard(text) {
218 var tempInput = $('<input>');
219 $('body').append(tempInput);
220 tempInput.val(text).select();
221 document.execCommand('copy');
222 tempInput.remove();
223 }
224
225
226 // Initialize session ID
227 var sessionId = getChatSession();
228
229 function callMxChat(message, callback) {
230 var sessionId = getChatSession();
231 $.ajax({
232 url: mxchatChat.ajax_url,
233 type: 'POST',
234 dataType: 'json',
235 data: {
236 action: 'mxchat_handle_chat_request',
237 message: message,
238 session_id: sessionId,
239 nonce: mxchatChat.nonce
240 },
241 success: function(response) {
242 //console.log("API Response:", response); // Debug log
243
244 // Check for the presence of 'message' in response as a fallback for 'text'
245 var responseText = response.text || response.message || '';
246 var responseHtml = response.html || ''; // Capture product card HTML
247 var images = response.images || [];
248
249 // Display the responseText if available, or responseHtml if included
250 if (responseText || responseHtml || images.length > 0) {
251 replaceLastMessage("bot", responseText, responseHtml, images);
252 } else {
253 appendMessage("bot", "I'm sorry, something went wrong.");
254 }
255
256 // Handle any redirection for checkout if available
257 if (response.redirect_url) {
258 setTimeout(function() {
259 window.location.href = response.redirect_url;
260 }, 2000);
261 }
262 },
263 error: function(xhr, status, error) {
264 console.error("Error communicating with the server:", error);
265 appendMessage("bot", "Error communicating with the server.");
266 }
267 });
268 }
269
270 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
271 try {
272 var messageClass = sender === "user" ? "user-message" : "bot-message";
273 var bgColor = sender === "user" ? userMessageBgColor : botMessageBgColor;
274 var fontColor = sender === "user" ? userMessageFontColor : botMessageFontColor;
275
276 var messageDiv = $('<div>').addClass(messageClass).css({
277 'background': bgColor,
278 'color': fontColor
279 });
280
281 // Format the message text, including code blocks
282 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
283
284 if (images && images.length > 0) {
285 fullMessage += '<div class="image-gallery">';
286 images.forEach(img => {
287 fullMessage += `
288 <div style="margin-bottom: 10px;">
289 <strong>${img.title}</strong><br>
290 <a href="${img.image_url}" target="_blank">
291 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
292 </a>
293 </div>`;
294 });
295 fullMessage += '</div>';
296 }
297
298 if (messageHtml) {
299 fullMessage += '<br><br>' + messageHtml;
300 }
301
302 messageDiv.html(fullMessage);
303
304 if (isTemporary) {
305 messageDiv.addClass('temporary-message');
306 }
307
308 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
309 if (sender === "bot") {
310 // After bot's message is displayed, scroll last user message to top
311 var lastUserMessage = $('#chat-box').find('.user-message').last();
312 if (lastUserMessage.length) {
313 scrollElementToTop(lastUserMessage);
314 }
315 }
316 });
317 } catch (error) {
318 console.error("Error rendering message with images:", error);
319 }
320 }
321
322
323 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
324 var messageClass = sender === "user" ? "user-message" : "bot-message";
325 var lastMessageDiv = $('#chat-box').find('.' + messageClass + '.temporary-message').last();
326
327 // Format response text, including code blocks
328 var fullMessage = Array.isArray(responseText) ?
329 responseText.map(item => linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(item))))).join("<br>") :
330 linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
331
332 if (responseHtml) {
333 fullMessage += '<br><br>' + responseHtml;
334 }
335
336 if (images.length > 0) {
337 fullMessage += '<div class="image-gallery">';
338 images.forEach(img => {
339 fullMessage += `
340 <div style="margin-bottom: 10px;">
341 <strong>${img.title}</strong><br>
342 <a href="${img.image_url}" target="_blank">
343 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
344 </a>
345 </div>`;
346 });
347 fullMessage += '</div>';
348 }
349
350 if (lastMessageDiv.length) {
351 lastMessageDiv.fadeOut(200, function() {
352 $(this).html(fullMessage).removeClass('temporary-message').fadeIn(200, function() {
353 // After bot's message is displayed, scroll last user message to top
354 var lastUserMessage = $('#chat-box').find('.user-message').last();
355 if (lastUserMessage.length) {
356 scrollElementToTop(lastUserMessage);
357 }
358 });
359 });
360 } else {
361 appendMessage(sender, responseText, responseHtml, images);
362 }
363 }
364
365
366 function loadChatHistory() {
367 var sessionId = getChatSession();
368 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
369
370 if (chatPersistenceEnabled && sessionId) {
371 $.ajax({
372 url: mxchatChat.ajax_url,
373 type: 'POST',
374 dataType: 'json',
375 data: {
376 action: 'mxchat_fetch_conversation_history',
377 session_id: sessionId
378 },
379 success: function(response) {
380 if (response.success && response.data && Array.isArray(response.data.conversation)) {
381 var $chatBox = $('#chat-box');
382 var $fragment = $(document.createDocumentFragment());
383
384 $.each(response.data.conversation, function(index, message) {
385 var messageElement = $('<div>').addClass(message.role === 'user' ? 'user-message' : 'bot-message')
386 .css({
387 'background': message.role === 'user' ? userMessageBgColor : botMessageBgColor,
388 'color': message.role === 'user' ? userMessageFontColor : botMessageFontColor
389 });
390
391 var content = message.content;
392
393 // Decode any escaped characters
394 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
395 content = decodeHTMLEntities(content);
396
397 // Detect if the content is HTML by checking for specific classes
398 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
399 // Append HTML content directly
400 messageElement.html(content);
401 } else {
402 // Format plain text content with code block support
403 var formattedContent = linkify(
404 formatBoldText(
405 convertNewlinesToBreaks(formatCodeBlocks(content))
406 )
407 );
408 messageElement.html(formattedContent);
409 }
410
411 $fragment.append(messageElement);
412 });
413
414 $chatBox.append($fragment);
415 scrollToBottom(true);
416
417 if (response.data.conversation.length > 0) {
418 $('#mxchat-popular-questions').hide();
419 }
420 } else {
421 console.warn("No conversation history found.");
422 }
423 },
424 error: function(xhr, status, error) {
425 console.error("Error loading chat history:", status, error);
426 appendMessage("bot", "Unable to load chat history.");
427 }
428 });
429 } else {
430 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
431 }
432 }
433
434
435 // Function to decode HTML entities
436 function decodeHTMLEntities(text) {
437 var textArea = document.createElement('textarea');
438 textArea.innerHTML = text;
439 return textArea.value;
440 }
441
442
443 function formatCodeBlocks(text) {
444 // Regex to match triple backticks and capture content between them
445 var codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
446
447 return text.replace(codeBlockPattern, function(_, language, codeContent) {
448 language = language || 'plaintext';
449
450 // Wrap code content in <pre> and <code> tags
451 return `<div class="code-block-container">
452 <pre class="code-block"><code class="language-${language}">${escapeHtml(codeContent)}</code></pre>
453 </div>`;
454 });
455 }
456
457 function escapeHtml(unsafe) {
458 return unsafe
459 .replace(/&/g, "&amp;")
460 .replace(/</g, "&lt;")
461 .replace(/>/g, "&gt;")
462 .replace(/"/g, "&quot;")
463 .replace(/'/g, "&#039;");
464 }
465
466 // Function to convert newlines, skipping preformatted text
467 function convertNewlinesToBreaks(text) {
468 // Regex to exclude <pre> and <code> tags from adding <br> tags
469 return text.replace(/(^|[^>])\n/g, '$1<br>');
470 }
471
472
473
474 $(document).ready(function() {
475 loadChatHistory();
476 });
477
478
479
480 // Helper function to check if a string is an image HTML
481 function isImageHtml(str) {
482 return str.startsWith('<img') && str.endsWith('>');
483 }
484
485 // Function to remove thinking dots
486 function removeThinkingDots() {
487 $('.thinking-dots').closest('.temporary-message').remove();
488 }
489
490 function isMobile() {
491 // This can be a simple check, or more sophisticated detection of mobile devices
492 return window.innerWidth <= 768; // Example threshold for mobile devices
493 }
494
495 function disableScroll() {
496 if (isMobile()) {
497 $('body').css('overflow', 'hidden');
498 }
499 }
500
501 function enableScroll() {
502 if (isMobile()) {
503 $('body').css('overflow', '');
504 }
505 }
506
507 // Function to show the chatbot widget (moved outside the Complianz logic)
508 function showChatWidget() {
509 setTimeout(function() {
510 $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
511 }, 250);
512 }
513
514 // Function to hide the chatbot widget
515 function hideChatWidget() {
516 $('#floating-chatbot-button').css('display', 'none');
517 }
518
519 // Pre-chat dismissal check function (wrapped in a function for reuse)
520 function checkPreChatDismissal() {
521 $.ajax({
522 url: mxchatChat.ajax_url,
523 type: 'POST',
524 data: {
525 action: 'mxchat_check_pre_chat_message_status',
526 _ajax_nonce: mxchatChat.nonce
527 },
528 success: function(response) {
529 if (response.success && !response.data.dismissed) {
530 $('#pre-chat-message').fadeIn(250);
531 } else {
532 $('#pre-chat-message').hide();
533 }
534 },
535 error: function() {
536 console.error('Failed to check pre-chat message dismissal status.');
537 }
538 });
539 }
540
541 // Function to dismiss pre-chat message for 24 hours
542 function handlePreChatDismissal() {
543 $('#pre-chat-message').fadeOut(200);
544 $.ajax({
545 url: mxchatChat.ajax_url,
546 type: 'POST',
547 data: {
548 action: 'mxchat_dismiss_pre_chat_message',
549 _ajax_nonce: mxchatChat.nonce
550 },
551 success: function() {
552 $('#pre-chat-message').hide();
553 },
554 error: function() {
555 console.error('Failed to dismiss pre-chat message.');
556 }
557 });
558 }
559
560 // Handle pre-chat message dismissal on button click
561 $(document).on('click', '.close-pre-chat-message', function(e) {
562 e.stopPropagation();
563 handlePreChatDismissal();
564 });
565
566 // Function for Complianz logic
567 var applyComplianzLogic = mxchatChat.complianz_toggle;
568 if (applyComplianzLogic) {
569 function checkConsentAndShowChat() {
570 var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
571 var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
572
573 // Show the chatbot by default
574 showChatWidget();
575
576 if (consentType === 'optin' && !consentStatus) {
577 // For opt-in, hide only if user explicitly denies consent
578 hideChatWidget();
579 } else if (consentType === 'optout' && consentStatus === false) {
580 // For opt-out, hide only if user explicitly denies consent
581 hideChatWidget();
582 } else {
583 // Keep showing the chatbot
584 showChatWidget();
585 }
586
587 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
588 }
589
590 // Initial check when the page loads
591 checkConsentAndShowChat();
592
593 // Listen for changes in consent status
594 $(document).on('cmplz_status_change', function(event, category) {
595 if (category === 'marketing') {
596 checkConsentAndShowChat();
597 }
598 });
599 } else {
600 // If Complianz is not toggled on, always show the chatbot
601 showChatWidget();
602 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
603 }
604
605 // Toggle chatbot visibility on floating button click
606 $(document).on('click', '#floating-chatbot-button', function() {
607 var chatbot = $('#floating-chatbot');
608 if (chatbot.hasClass('hidden')) {
609 chatbot.removeClass('hidden').addClass('visible');
610 $(this).addClass('hidden');
611 disableScroll();
612 // Hide the pre-chat message without dismissing it
613 $('#pre-chat-message').fadeOut(250);
614 } else {
615 chatbot.removeClass('visible').addClass('hidden');
616 $(this).removeClass('hidden');
617 enableScroll();
618 // Show the pre-chat message again if it hasn't been dismissed
619 checkPreChatDismissal();
620 }
621 });
622
623 $(document).on('click', '#exit-chat-button', function() {
624 $('#floating-chatbot').addClass('hidden').removeClass('visible');
625 $('#floating-chatbot-button').removeClass('hidden');
626 enableScroll();
627 });
628
629 // Close pre-chat message on click
630 $(document).on('click', '.close-pre-chat-message', function(e) {
631 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
632 $('#pre-chat-message').fadeOut(200, function() {
633 $(this).remove();
634 });
635 });
636
637 // Open chatbot when pre-chat message is clicked
638 $(document).on('click', '#pre-chat-message', function() {
639 var chatbot = $('#floating-chatbot');
640 if (chatbot.hasClass('hidden')) {
641 chatbot.removeClass('hidden').addClass('visible');
642 $('#floating-chatbot-button').addClass('hidden');
643 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
644 disableScroll(); // Disable scroll when chatbot opens
645 }
646 });
647
648 // If the chatbot is initially hidden, ensure the button is visible
649 if ($('#floating-chatbot').hasClass('hidden')) {
650 $('#floating-chatbot-button').removeClass('hidden');
651 }
652
653 function setFullHeight() {
654 var vh = $(window).innerHeight() * 0.01;
655 $(':root').css('--vh', vh + 'px');
656 }
657
658 // Set the height when the page loads
659 $(document).ready(function() {
660 setFullHeight();
661 });
662
663 // Set the height on resize and orientation change events
664 $(window).on('resize orientationchange', function() {
665 setFullHeight();
666 });
667
668
669 // Now handle the close button to dismiss the pre-chat message for 24 hours
670 var closeButton = document.querySelector('.close-pre-chat-message');
671 if (closeButton) {
672 closeButton.addEventListener('click', function() {
673 $('#pre-chat-message').fadeOut(200); // Hide the message
674
675 // Send an AJAX request to set the transient flag for 24 hours
676 $.ajax({
677 url: mxchatChat.ajax_url,
678 type: 'POST',
679 data: {
680 action: 'mxchat_dismiss_pre_chat_message',
681 _ajax_nonce: mxchatChat.nonce
682 },
683 success: function() {
684 //console.log('Pre-chat message dismissed for 24 hours.');
685
686 // Ensure the message is hidden after dismissal
687 $('#pre-chat-message').hide();
688 },
689 error: function() {
690 //console.error('Failed to dismiss pre-chat message.');
691 }
692 });
693 });
694 }
695
696
697
698
699 // Event listener for Add to Cart button
700 $(document).on('click', '.add-to-cart-button', function() {
701 var productId = $(this).data('product-id'); // Get product ID from data attribute
702
703 // Simulate user message first for proper ordering
704 appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
705
706
707 // Use existing function to send the "add to cart" command to the chatbot
708 sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
709 });
710
711
712
713 });
714