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

701 lines 25.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
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 if (consentType === 'optin' && !consentStatus) {
574 hideChatWidget();
575 } else if (consentType === 'optout' && !consentStatus) {
576 hideChatWidget();
577 } else {
578 showChatWidget();
579 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
580 }
581 }
582
583 checkConsentAndShowChat();
584 $(document).on('cmplz_status_change', function(event, category) {
585 checkConsentAndShowChat();
586 });
587 } else {
588 showChatWidget();
589 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
590 }
591
592 // Toggle chatbot visibility on floating button click
593 $(document).on('click', '#floating-chatbot-button', function() {
594 var chatbot = $('#floating-chatbot');
595 if (chatbot.hasClass('hidden')) {
596 chatbot.removeClass('hidden').addClass('visible');
597 $(this).addClass('hidden');
598 disableScroll();
599 // Hide the pre-chat message without dismissing it
600 $('#pre-chat-message').fadeOut(250);
601 } else {
602 chatbot.removeClass('visible').addClass('hidden');
603 $(this).removeClass('hidden');
604 enableScroll();
605 // Show the pre-chat message again if it hasn't been dismissed
606 checkPreChatDismissal();
607 }
608 });
609
610 $(document).on('click', '#exit-chat-button', function() {
611 $('#floating-chatbot').addClass('hidden').removeClass('visible');
612 $('#floating-chatbot-button').removeClass('hidden');
613 enableScroll();
614 });
615
616 // Close pre-chat message on click
617 $(document).on('click', '.close-pre-chat-message', function(e) {
618 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
619 $('#pre-chat-message').fadeOut(200, function() {
620 $(this).remove();
621 });
622 });
623
624 // Open chatbot when pre-chat message is clicked
625 $(document).on('click', '#pre-chat-message', function() {
626 var chatbot = $('#floating-chatbot');
627 if (chatbot.hasClass('hidden')) {
628 chatbot.removeClass('hidden').addClass('visible');
629 $('#floating-chatbot-button').addClass('hidden');
630 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
631 disableScroll(); // Disable scroll when chatbot opens
632 }
633 });
634
635 // If the chatbot is initially hidden, ensure the button is visible
636 if ($('#floating-chatbot').hasClass('hidden')) {
637 $('#floating-chatbot-button').removeClass('hidden');
638 }
639
640 function setFullHeight() {
641 var vh = $(window).innerHeight() * 0.01;
642 $(':root').css('--vh', vh + 'px');
643 }
644
645 // Set the height when the page loads
646 $(document).ready(function() {
647 setFullHeight();
648 });
649
650 // Set the height on resize and orientation change events
651 $(window).on('resize orientationchange', function() {
652 setFullHeight();
653 });
654
655
656 // Now handle the close button to dismiss the pre-chat message for 24 hours
657 var closeButton = document.querySelector('.close-pre-chat-message');
658 if (closeButton) {
659 closeButton.addEventListener('click', function() {
660 $('#pre-chat-message').fadeOut(200); // Hide the message
661
662 // Send an AJAX request to set the transient flag for 24 hours
663 $.ajax({
664 url: mxchatChat.ajax_url,
665 type: 'POST',
666 data: {
667 action: 'mxchat_dismiss_pre_chat_message',
668 _ajax_nonce: mxchatChat.nonce
669 },
670 success: function() {
671 //console.log('Pre-chat message dismissed for 24 hours.');
672
673 // Ensure the message is hidden after dismissal
674 $('#pre-chat-message').hide();
675 },
676 error: function() {
677 //console.error('Failed to dismiss pre-chat message.');
678 }
679 });
680 });
681 }
682
683
684
685
686 // Event listener for Add to Cart button
687 $(document).on('click', '.add-to-cart-button', function() {
688 var productId = $(this).data('product-id'); // Get product ID from data attribute
689
690 // Simulate user message first for proper ordering
691 appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
692
693
694 // Use existing function to send the "add to cart" command to the chatbot
695 sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
696 });
697
698
699
700 });
701