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

525 lines 19.1 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
45
46 // Function to handle sending a message
47 function sendMessage() {
48 var message = $('#chat-input').val();
49 if (message) {
50 appendMessage("user", message);
51 $('#chat-input').val('');
52
53 // Show typing indicator
54 appendThinkingMessage(); // Use this instead of appendMessage for the typing indicator
55 scrollToBottom(); // Add this line
56
57 callMxChat(message, function(response) {
58 // Replace typing indicator with actual response
59 replaceLastMessage("bot", response);
60 });
61 }
62 }
63
64 // Function to append a thinking message with animation
65 function appendThinkingMessage() {
66 // Remove any existing thinking dots first
67 $('.thinking-dots').remove();
68
69 // Retrieve the bot message font color and background color
70 var botMessageFontColor = mxchatChat.bot_message_font_color;
71 var botMessageBgColor = mxchatChat.bot_message_bg_color;
72
73 var thinkingHtml = '<div class="thinking-dots-container">' +
74 '<div class="thinking-dots">' +
75 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
76 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
77 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
78 '</div>' +
79 '</div>';
80
81 // Append the thinking dots to the chat container (or within the temporary message div)
82 $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
83 scrollToBottom();
84 }
85
86 // Trigger send button click when "Enter" key is pressed in the input field
87 $('#chat-input').keypress(function(e) {
88 if (e.which == 13) {
89 e.preventDefault();
90 $('#send-button').click();
91 }
92 });
93
94 // Handle send button click
95 $('#send-button').click(function() {
96 sendMessage();
97 });
98
99 // Use the linkTarget in your linkify function
100 function linkify(inputText) {
101 // Convert Markdown-style links to HTML links first
102 var markdownLinkPattern = /\[([^\]]+)\]\(([^)]+)\)/g;
103 var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
104
105 // URLs starting with http://, https://, or ftp://, but not already inside an <a> tag
106 var urlPattern = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[--A-Z0-9+&@#\/%=~_|])(?![^<]*<\/a>)/gim;
107 replacedText = replacedText.replace(urlPattern, '<a href="$1" target="' + linkTarget + '">$1</a>');
108
109 // URLs starting with "www." not already inside an <a> tag
110 var wwwPattern = /(^|[^\/])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
111 replacedText = replacedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
112
113 return replacedText;
114 }
115
116
117
118 // Updated appendMessage function with debugging
119 function appendMessage(sender, message, isTemporary = false) {
120 var messageClass = sender === "user" ? "user-message" : "bot-message";
121 var bgColor = sender === "user" ? userMessageBgColor : botMessageBgColor;
122 var fontColor = sender === "user" ? userMessageFontColor : botMessageFontColor;
123
124 var messageDiv = $('<div>').addClass(messageClass).css({
125 'background': bgColor,
126 'color': fontColor
127 }).html(linkify(formatBoldText(convertNewlinesToBreaks(message))));
128
129 if (isTemporary) {
130 messageDiv.addClass('temporary-message');
131 }
132
133 messageDiv.hide().appendTo('#chat-box').fadeIn(300);
134 scrollToBottom();
135 }
136
137
138 // Function to replace the last message in the chat
139 // Function to replace the last message in the chat
140 function replaceLastMessage(sender, newMessage) {
141 var messageClass = sender === "user" ? "user-message" : "bot-message";
142 var lastMessageDiv = $('#chat-box').find('.' + messageClass + '.temporary-message').last();
143
144 // Apply linkify, formatBoldText, and convertNewlinesToBreaks to the new message
145 var formattedMessage = linkify(formatBoldText(convertNewlinesToBreaks(newMessage)));
146
147 // Check if the new message is the rate limit message before replacing
148 if (newMessage === mxchatChat.rate_limit_message) {
149 // Append rate limit message without replacing anything
150 appendMessage("bot", formattedMessage);
151 return; // Exit the function to prevent replacing the rate limit message
152 }
153 if (lastMessageDiv.length) {
154 lastMessageDiv.fadeOut(200, function() {
155 // Replace the content with the formatted message
156 $(this).html(formattedMessage).removeClass('temporary-message').fadeIn(200);
157 });
158 } else {
159 appendMessage(sender, formattedMessage);
160 }
161 scrollToBottom();
162 }
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
230 function callMxChat(message, callback) {
231 var sessionId = getChatSession();
232 $.ajax({
233 url: mxchatChat.ajax_url,
234 type: 'POST',
235 dataType: 'json',
236 data: {
237 action: 'mxchat_handle_chat_request',
238 message: message,
239 session_id: sessionId,
240 nonce: mxchatChat.nonce
241 },
242 success: function(response) {
243 // Check for redirect_url in the response and redirect
244 if (response.redirect_url) {
245 window.location.href = response.redirect_url;
246 } else if (response.message) {
247 callback(response.message);
248 } else if (response.completion && response.completion.text) {
249 // Handle Claude's response format
250 callback(response.completion.text);
251 } else {
252 // Handle unknown response format
253 appendMessage("bot", "Sorry, I couldn't process the response.");
254 }
255 },
256 error: function(xhr, status, error) {
257 appendMessage("bot", "Error communicating with the server.");
258 }
259 });
260 }
261
262
263 function loadChatHistory() {
264 var sessionId = getChatSession();
265
266 // Manually enable persistence for testing
267 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on'; // Reading from localized script
268
269 //console.log("Chat persistence enabled: ", chatPersistenceEnabled);
270 //console.log("Session ID for history loading: ", sessionId);
271
272 if (chatPersistenceEnabled && sessionId) {
273 //console.log("Loading chat history for session ID:", sessionId);
274 $.ajax({
275 url: mxchatChat.ajax_url,
276 type: 'POST',
277 dataType: 'json',
278 data: {
279 action: 'mxchat_fetch_conversation_history',
280 session_id: sessionId
281 },
282 success: function(response) {
283 //console.log("Chat history response:", response);
284 if (response.success && response.data && Array.isArray(response.data.conversation)) {
285 var $chatBox = $('#chat-box');
286 var $fragment = $(document.createDocumentFragment());
287
288 $.each(response.data.conversation, function(index, message) {
289 var messageElement = $('<div>').addClass(message.role === 'user' ? 'user-message' : 'bot-message')
290 .css({
291 'background': message.role === 'user' ? userMessageBgColor : botMessageBgColor,
292 'color': message.role === 'user' ? userMessageFontColor : botMessageFontColor
293 })
294 .html(linkify(formatBoldText(convertNewlinesToBreaks(message.content))));
295 $fragment.append(messageElement);
296 });
297
298 $chatBox.append($fragment);
299 scrollToBottom(true);
300
301 } else {
302 console.warn("No conversation history found.");
303 }
304 },
305 error: function(xhr, status, error) {
306 //console.error("Error loading chat history:", status, error);
307 appendMessage("bot", "Unable to load chat history.");
308 }
309 });
310 } else {
311 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
312 }
313 }
314
315
316 $(document).ready(function() {
317 loadChatHistory();
318 });
319
320
321
322 // Helper function to check if a string is an image HTML
323 function isImageHtml(str) {
324 return str.startsWith('<img') && str.endsWith('>');
325 }
326
327 // Function to remove thinking dots
328 function removeThinkingDots() {
329 $('.thinking-dots').closest('.temporary-message').remove();
330 }
331
332 function isMobile() {
333 // This can be a simple check, or more sophisticated detection of mobile devices
334 return window.innerWidth <= 768; // Example threshold for mobile devices
335 }
336
337 function disableScroll() {
338 if (isMobile()) {
339 $('body').css('overflow', 'hidden');
340 }
341 }
342
343 function enableScroll() {
344 if (isMobile()) {
345 $('body').css('overflow', '');
346 }
347 }
348
349 // Function to show the chatbot widget (moved outside the Complianz logic)
350 function showChatWidget() {
351 setTimeout(function() {
352 $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
353 }, 250);
354 }
355
356 // Function to hide the chatbot widget
357 function hideChatWidget() {
358 $('#floating-chatbot-button').css('display', 'none');
359 }
360
361 // Pre-chat dismissal check function (wrapped in a function for reuse)
362 function checkPreChatDismissal() {
363 $.ajax({
364 url: mxchatChat.ajax_url,
365 type: 'POST',
366 data: {
367 action: 'mxchat_check_pre_chat_message_status',
368 _ajax_nonce: mxchatChat.nonce
369 },
370 success: function(response) {
371 if (response.success && !response.data.dismissed) {
372 $('#pre-chat-message').fadeIn(250);
373 } else {
374 $('#pre-chat-message').hide();
375 }
376 },
377 error: function() {
378 console.error('Failed to check pre-chat message dismissal status.');
379 }
380 });
381 }
382
383 // Function to dismiss pre-chat message for 24 hours
384 function handlePreChatDismissal() {
385 $('#pre-chat-message').fadeOut(200);
386 $.ajax({
387 url: mxchatChat.ajax_url,
388 type: 'POST',
389 data: {
390 action: 'mxchat_dismiss_pre_chat_message',
391 _ajax_nonce: mxchatChat.nonce
392 },
393 success: function() {
394 $('#pre-chat-message').hide();
395 },
396 error: function() {
397 console.error('Failed to dismiss pre-chat message.');
398 }
399 });
400 }
401
402 // Handle pre-chat message dismissal on button click
403 $(document).on('click', '.close-pre-chat-message', function(e) {
404 e.stopPropagation();
405 handlePreChatDismissal();
406 });
407
408 // Function for Complianz logic
409 var applyComplianzLogic = mxchatChat.complianz_toggle;
410 if (applyComplianzLogic) {
411 function checkConsentAndShowChat() {
412 var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
413 var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
414
415 if (consentType === 'optin' && !consentStatus) {
416 hideChatWidget();
417 } else if (consentType === 'optout' && !consentStatus) {
418 hideChatWidget();
419 } else {
420 showChatWidget();
421 checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
422 }
423 }
424
425 checkConsentAndShowChat();
426 $(document).on('cmplz_status_change', function(event, category) {
427 checkConsentAndShowChat();
428 });
429 } else {
430 showChatWidget();
431 checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
432 }
433
434 // Toggle chatbot visibility on floating button click
435 $(document).on('click', '#floating-chatbot-button', function() {
436 var chatbot = $('#floating-chatbot');
437 if (chatbot.hasClass('hidden')) {
438 chatbot.removeClass('hidden').addClass('visible');
439 $(this).addClass('hidden');
440 disableScroll();
441 handlePreChatDismissal();
442 } else {
443 chatbot.removeClass('visible').addClass('hidden');
444 $(this).removeClass('hidden');
445 enableScroll();
446 checkPreChatDismissal();
447 }
448 });
449
450 $(document).on('click', '#exit-chat-button', function() {
451 $('#floating-chatbot').addClass('hidden').removeClass('visible');
452 $('#floating-chatbot-button').removeClass('hidden');
453 enableScroll();
454 });
455
456 // Close pre-chat message on click
457 $(document).on('click', '.close-pre-chat-message', function(e) {
458 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
459 $('#pre-chat-message').fadeOut(200, function() {
460 $(this).remove();
461 });
462 });
463
464 // Open chatbot when pre-chat message is clicked
465 $(document).on('click', '#pre-chat-message', function() {
466 var chatbot = $('#floating-chatbot');
467 if (chatbot.hasClass('hidden')) {
468 chatbot.removeClass('hidden').addClass('visible');
469 $('#floating-chatbot-button').addClass('hidden');
470 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
471 disableScroll(); // Disable scroll when chatbot opens
472 }
473 });
474
475 // If the chatbot is initially hidden, ensure the button is visible
476 if ($('#floating-chatbot').hasClass('hidden')) {
477 $('#floating-chatbot-button').removeClass('hidden');
478 }
479
480 function setFullHeight() {
481 var vh = $(window).innerHeight() * 0.01;
482 $(':root').css('--vh', vh + 'px');
483 }
484
485 // Set the height when the page loads
486 $(document).ready(function() {
487 setFullHeight();
488 });
489
490 // Set the height on resize and orientation change events
491 $(window).on('resize orientationchange', function() {
492 setFullHeight();
493 });
494
495
496 // Now handle the close button to dismiss the pre-chat message for 24 hours
497 var closeButton = document.querySelector('.close-pre-chat-message');
498 if (closeButton) {
499 closeButton.addEventListener('click', function() {
500 $('#pre-chat-message').fadeOut(200); // Hide the message
501
502 // Send an AJAX request to set the transient flag for 24 hours
503 $.ajax({
504 url: mxchatChat.ajax_url,
505 type: 'POST',
506 data: {
507 action: 'mxchat_dismiss_pre_chat_message',
508 _ajax_nonce: mxchatChat.nonce
509 },
510 success: function() {
511 //console.log('Pre-chat message dismissed for 24 hours.');
512
513 // Ensure the message is hidden after dismissal
514 $('#pre-chat-message').hide();
515 },
516 error: function() {
517 //console.error('Failed to dismiss pre-chat message.');
518 }
519 });
520 });
521 }
522
523
524 });
525