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

683 lines 24.5 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 // Optimized scrollToBottom function for instant scrolling
160 function scrollToBottom(instant = false) {
161 var chatBox = $('#chat-box');
162 if (instant) {
163 // Instantly set the scroll position to the bottom
164 chatBox.scrollTop(chatBox.prop("scrollHeight"));
165 } else {
166 // Use requestAnimationFrame for smoother scrolling if needed
167 let start = null;
168 const scrollHeight = chatBox.prop("scrollHeight");
169 const initialScroll = chatBox.scrollTop();
170 const distance = scrollHeight - initialScroll;
171 const duration = 500; // Duration in ms
172
173 function smoothScroll(timestamp) {
174 if (!start) start = timestamp;
175 const progress = timestamp - start;
176 const currentScroll = initialScroll + (distance * (progress / duration));
177 chatBox.scrollTop(currentScroll);
178
179 if (progress < duration) {
180 requestAnimationFrame(smoothScroll);
181 } else {
182 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
183 }
184 }
185
186 requestAnimationFrame(smoothScroll);
187 }
188 }
189
190
191 // Function to format text with **bold** inside double asterisks
192 function formatBoldText(text) {
193 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
194 }
195
196 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
197 function convertNewlinesToBreaks(text) {
198 var lines = text.split('\n');
199 var formattedText = '';
200
201 for (var i = 0; i < lines.length; i++) {
202 formattedText += lines[i] + '<br>';
203 }
204
205 return formattedText;
206 }
207
208 // Copy to clipboard function
209 // Function to copy text to clipboard
210 function copyToClipboard(text) {
211 var tempInput = $('<input>');
212 $('body').append(tempInput);
213 tempInput.val(text).select();
214 document.execCommand('copy');
215 tempInput.remove();
216 }
217
218
219 // Initialize session ID
220 var sessionId = getChatSession();
221
222 function callMxChat(message, callback) {
223 var sessionId = getChatSession();
224 $.ajax({
225 url: mxchatChat.ajax_url,
226 type: 'POST',
227 dataType: 'json',
228 data: {
229 action: 'mxchat_handle_chat_request',
230 message: message,
231 session_id: sessionId,
232 nonce: mxchatChat.nonce
233 },
234 success: function(response) {
235 //console.log("API Response:", response); // Debug log
236
237 // Check for the presence of 'message' in response as a fallback for 'text'
238 var responseText = response.text || response.message || '';
239 var responseHtml = response.html || ''; // Capture product card HTML
240 var images = response.images || [];
241
242 // Display the responseText if available, or responseHtml if included
243 if (responseText || responseHtml || images.length > 0) {
244 replaceLastMessage("bot", responseText, responseHtml, images);
245 } else {
246 appendMessage("bot", "I'm sorry, something went wrong.");
247 }
248
249 // Handle any redirection for checkout if available
250 if (response.redirect_url) {
251 setTimeout(function() {
252 window.location.href = response.redirect_url;
253 }, 2000);
254 }
255 },
256 error: function(xhr, status, error) {
257 console.error("Error communicating with the server:", error);
258 appendMessage("bot", "Error communicating with the server.");
259 }
260 });
261 }
262
263 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
264 try {
265 var messageClass = sender === "user" ? "user-message" : "bot-message";
266 var bgColor = sender === "user" ? userMessageBgColor : botMessageBgColor;
267 var fontColor = sender === "user" ? userMessageFontColor : botMessageFontColor;
268
269 var messageDiv = $('<div>').addClass(messageClass).css({
270 'background': bgColor,
271 'color': fontColor
272 });
273
274 // Format the message text, including code blocks
275 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
276
277 if (images && images.length > 0) {
278 fullMessage += '<div class="image-gallery">';
279 images.forEach(img => {
280 fullMessage += `
281 <div style="margin-bottom: 10px;">
282 <strong>${img.title}</strong><br>
283 <a href="${img.image_url}" target="_blank">
284 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
285 </a>
286 </div>`;
287 });
288 fullMessage += '</div>';
289 }
290
291 if (messageHtml) {
292 fullMessage += '<br><br>' + messageHtml;
293 }
294
295 messageDiv.html(fullMessage);
296
297 if (isTemporary) {
298 messageDiv.addClass('temporary-message');
299 }
300
301 messageDiv.hide().appendTo('#chat-box').fadeIn(300);
302 scrollToBottom();
303 } catch (error) {
304 console.error("Error rendering message with images:", error);
305 }
306 }
307
308
309 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
310 var messageClass = sender === "user" ? "user-message" : "bot-message";
311 var lastMessageDiv = $('#chat-box').find('.' + messageClass + '.temporary-message').last();
312
313 // Format response text, including code blocks
314 var fullMessage = Array.isArray(responseText) ?
315 responseText.map(item => linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(item))))).join("<br>") :
316 linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
317
318 if (responseHtml) {
319 fullMessage += '<br><br>' + responseHtml;
320 }
321
322 if (images.length > 0) {
323 fullMessage += '<div class="image-gallery">';
324 images.forEach(img => {
325 fullMessage += `
326 <div style="margin-bottom: 10px;">
327 <strong>${img.title}</strong><br>
328 <a href="${img.image_url}" target="_blank">
329 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
330 </a>
331 </div>`;
332 });
333 fullMessage += '</div>';
334 }
335
336 if (lastMessageDiv.length) {
337 lastMessageDiv.fadeOut(200, function() {
338 $(this).html(fullMessage).removeClass('temporary-message').fadeIn(200);
339 });
340 } else {
341 appendMessage(sender, responseText, responseHtml, images);
342 }
343
344 scrollToBottom();
345 }
346
347
348 function loadChatHistory() {
349 var sessionId = getChatSession();
350 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
351
352 if (chatPersistenceEnabled && sessionId) {
353 $.ajax({
354 url: mxchatChat.ajax_url,
355 type: 'POST',
356 dataType: 'json',
357 data: {
358 action: 'mxchat_fetch_conversation_history',
359 session_id: sessionId
360 },
361 success: function(response) {
362 if (response.success && response.data && Array.isArray(response.data.conversation)) {
363 var $chatBox = $('#chat-box');
364 var $fragment = $(document.createDocumentFragment());
365
366 $.each(response.data.conversation, function(index, message) {
367 var messageElement = $('<div>').addClass(message.role === 'user' ? 'user-message' : 'bot-message')
368 .css({
369 'background': message.role === 'user' ? userMessageBgColor : botMessageBgColor,
370 'color': message.role === 'user' ? userMessageFontColor : botMessageFontColor
371 });
372
373 var content = message.content;
374
375 // Decode any escaped characters
376 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
377 content = decodeHTMLEntities(content);
378
379 // Detect if the content is HTML by checking for specific classes
380 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
381 // Append HTML content directly
382 messageElement.html(content);
383 } else {
384 // Format plain text content with code block support
385 var formattedContent = linkify(
386 formatBoldText(
387 convertNewlinesToBreaks(formatCodeBlocks(content))
388 )
389 );
390 messageElement.html(formattedContent);
391 }
392
393 $fragment.append(messageElement);
394 });
395
396 $chatBox.append($fragment);
397 scrollToBottom(true);
398
399 if (response.data.conversation.length > 0) {
400 $('#mxchat-popular-questions').hide();
401 }
402 } else {
403 console.warn("No conversation history found.");
404 }
405 },
406 error: function(xhr, status, error) {
407 console.error("Error loading chat history:", status, error);
408 appendMessage("bot", "Unable to load chat history.");
409 }
410 });
411 } else {
412 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
413 }
414 }
415
416
417 // Function to decode HTML entities
418 function decodeHTMLEntities(text) {
419 var textArea = document.createElement('textarea');
420 textArea.innerHTML = text;
421 return textArea.value;
422 }
423
424
425 function formatCodeBlocks(text) {
426 // Regex to match triple backticks and capture content between them
427 var codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
428
429 return text.replace(codeBlockPattern, function(_, language, codeContent) {
430 language = language || 'plaintext';
431
432 // Wrap code content in <pre> and <code> tags
433 return `<div class="code-block-container">
434 <pre class="code-block"><code class="language-${language}">${escapeHtml(codeContent)}</code></pre>
435 </div>`;
436 });
437 }
438
439 function escapeHtml(unsafe) {
440 return unsafe
441 .replace(/&/g, "&amp;")
442 .replace(/</g, "&lt;")
443 .replace(/>/g, "&gt;")
444 .replace(/"/g, "&quot;")
445 .replace(/'/g, "&#039;");
446 }
447
448 // Function to convert newlines, skipping preformatted text
449 function convertNewlinesToBreaks(text) {
450 // Regex to exclude <pre> and <code> tags from adding <br> tags
451 return text.replace(/(^|[^>])\n/g, '$1<br>');
452 }
453
454
455
456 $(document).ready(function() {
457 loadChatHistory();
458 });
459
460
461
462 // Helper function to check if a string is an image HTML
463 function isImageHtml(str) {
464 return str.startsWith('<img') && str.endsWith('>');
465 }
466
467 // Function to remove thinking dots
468 function removeThinkingDots() {
469 $('.thinking-dots').closest('.temporary-message').remove();
470 }
471
472 function isMobile() {
473 // This can be a simple check, or more sophisticated detection of mobile devices
474 return window.innerWidth <= 768; // Example threshold for mobile devices
475 }
476
477 function disableScroll() {
478 if (isMobile()) {
479 $('body').css('overflow', 'hidden');
480 }
481 }
482
483 function enableScroll() {
484 if (isMobile()) {
485 $('body').css('overflow', '');
486 }
487 }
488
489 // Function to show the chatbot widget (moved outside the Complianz logic)
490 function showChatWidget() {
491 setTimeout(function() {
492 $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
493 }, 250);
494 }
495
496 // Function to hide the chatbot widget
497 function hideChatWidget() {
498 $('#floating-chatbot-button').css('display', 'none');
499 }
500
501 // Pre-chat dismissal check function (wrapped in a function for reuse)
502 function checkPreChatDismissal() {
503 $.ajax({
504 url: mxchatChat.ajax_url,
505 type: 'POST',
506 data: {
507 action: 'mxchat_check_pre_chat_message_status',
508 _ajax_nonce: mxchatChat.nonce
509 },
510 success: function(response) {
511 if (response.success && !response.data.dismissed) {
512 $('#pre-chat-message').fadeIn(250);
513 } else {
514 $('#pre-chat-message').hide();
515 }
516 },
517 error: function() {
518 console.error('Failed to check pre-chat message dismissal status.');
519 }
520 });
521 }
522
523 // Function to dismiss pre-chat message for 24 hours
524 function handlePreChatDismissal() {
525 $('#pre-chat-message').fadeOut(200);
526 $.ajax({
527 url: mxchatChat.ajax_url,
528 type: 'POST',
529 data: {
530 action: 'mxchat_dismiss_pre_chat_message',
531 _ajax_nonce: mxchatChat.nonce
532 },
533 success: function() {
534 $('#pre-chat-message').hide();
535 },
536 error: function() {
537 console.error('Failed to dismiss pre-chat message.');
538 }
539 });
540 }
541
542 // Handle pre-chat message dismissal on button click
543 $(document).on('click', '.close-pre-chat-message', function(e) {
544 e.stopPropagation();
545 handlePreChatDismissal();
546 });
547
548 // Function for Complianz logic
549 var applyComplianzLogic = mxchatChat.complianz_toggle;
550 if (applyComplianzLogic) {
551 function checkConsentAndShowChat() {
552 var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
553 var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
554
555 if (consentType === 'optin' && !consentStatus) {
556 hideChatWidget();
557 } else if (consentType === 'optout' && !consentStatus) {
558 hideChatWidget();
559 } else {
560 showChatWidget();
561 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
562 }
563 }
564
565 checkConsentAndShowChat();
566 $(document).on('cmplz_status_change', function(event, category) {
567 checkConsentAndShowChat();
568 });
569 } else {
570 showChatWidget();
571 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
572 }
573
574 // Toggle chatbot visibility on floating button click
575 $(document).on('click', '#floating-chatbot-button', function() {
576 var chatbot = $('#floating-chatbot');
577 if (chatbot.hasClass('hidden')) {
578 chatbot.removeClass('hidden').addClass('visible');
579 $(this).addClass('hidden');
580 disableScroll();
581 // Hide the pre-chat message without dismissing it
582 $('#pre-chat-message').fadeOut(250);
583 } else {
584 chatbot.removeClass('visible').addClass('hidden');
585 $(this).removeClass('hidden');
586 enableScroll();
587 // Show the pre-chat message again if it hasn't been dismissed
588 checkPreChatDismissal();
589 }
590 });
591
592 $(document).on('click', '#exit-chat-button', function() {
593 $('#floating-chatbot').addClass('hidden').removeClass('visible');
594 $('#floating-chatbot-button').removeClass('hidden');
595 enableScroll();
596 });
597
598 // Close pre-chat message on click
599 $(document).on('click', '.close-pre-chat-message', function(e) {
600 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
601 $('#pre-chat-message').fadeOut(200, function() {
602 $(this).remove();
603 });
604 });
605
606 // Open chatbot when pre-chat message is clicked
607 $(document).on('click', '#pre-chat-message', function() {
608 var chatbot = $('#floating-chatbot');
609 if (chatbot.hasClass('hidden')) {
610 chatbot.removeClass('hidden').addClass('visible');
611 $('#floating-chatbot-button').addClass('hidden');
612 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
613 disableScroll(); // Disable scroll when chatbot opens
614 }
615 });
616
617 // If the chatbot is initially hidden, ensure the button is visible
618 if ($('#floating-chatbot').hasClass('hidden')) {
619 $('#floating-chatbot-button').removeClass('hidden');
620 }
621
622 function setFullHeight() {
623 var vh = $(window).innerHeight() * 0.01;
624 $(':root').css('--vh', vh + 'px');
625 }
626
627 // Set the height when the page loads
628 $(document).ready(function() {
629 setFullHeight();
630 });
631
632 // Set the height on resize and orientation change events
633 $(window).on('resize orientationchange', function() {
634 setFullHeight();
635 });
636
637
638 // Now handle the close button to dismiss the pre-chat message for 24 hours
639 var closeButton = document.querySelector('.close-pre-chat-message');
640 if (closeButton) {
641 closeButton.addEventListener('click', function() {
642 $('#pre-chat-message').fadeOut(200); // Hide the message
643
644 // Send an AJAX request to set the transient flag for 24 hours
645 $.ajax({
646 url: mxchatChat.ajax_url,
647 type: 'POST',
648 data: {
649 action: 'mxchat_dismiss_pre_chat_message',
650 _ajax_nonce: mxchatChat.nonce
651 },
652 success: function() {
653 //console.log('Pre-chat message dismissed for 24 hours.');
654
655 // Ensure the message is hidden after dismissal
656 $('#pre-chat-message').hide();
657 },
658 error: function() {
659 //console.error('Failed to dismiss pre-chat message.');
660 }
661 });
662 });
663 }
664
665
666
667
668 // Event listener for Add to Cart button
669 $(document).on('click', '.add-to-cart-button', function() {
670 var productId = $(this).data('product-id'); // Get product ID from data attribute
671
672 // Simulate user message first for proper ordering
673 appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
674
675
676 // Use existing function to send the "add to cart" command to the chatbot
677 sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
678 });
679
680
681
682 });
683