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

1,705 lines 61.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 //console.log('Link Target Toggle Value:', mxchatChat.link_target_toggle);
4 // Add these variables at the top of your chat-script.js file
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 let lastSeenMessageId = '';
19 let notificationCheckInterval;
20 let notificationBadge;
21 // Initialize session ID
22 var sessionId = getChatSession();
23
24 let pollingInterval; // Variable to store the interval ID
25 let processedMessageIds = new Set(); // Add this at the top with your other variables
26 //console.log('Live Agent BG Color:', liveAgentMessageBgColor);
27 //console.log('Live Agent Font Color:', liveAgentMessageFontColor);
28 let activePdfFile = null;
29 let activeWordFile = null;
30
31
32 // Function to create and append notification badge
33 // Function to create and append notification badge
34 function createNotificationBadge() {
35 console.log("Creating notification badge...");
36 const chatButton = document.getElementById('floating-chatbot-button');
37 console.log("Chat button found:", !!chatButton);
38
39 if (!chatButton) return;
40
41 // Remove any existing badge first
42 const existingBadge = chatButton.querySelector('.chat-notification-badge');
43 if (existingBadge) {
44 console.log("Removing existing badge");
45 existingBadge.remove();
46 }
47
48 notificationBadge = document.createElement('div');
49 notificationBadge.className = 'chat-notification-badge';
50 notificationBadge.style.cssText = `
51 display: none;
52 position: absolute;
53 top: -5px;
54 right: -5px;
55 background-color: red;
56 color: white;
57 border-radius: 50%;
58 padding: 4px 8px;
59 font-size: 12px;
60 font-weight: bold;
61 z-index: 10001;
62 `;
63 chatButton.style.position = 'relative';
64 chatButton.appendChild(notificationBadge);
65
66 console.log("Notification badge created and appended:", {
67 exists: !!notificationBadge,
68 parent: notificationBadge?.parentNode?.id,
69 display: notificationBadge?.style?.display
70 });
71 }
72
73 // Function to check for new messages
74 function checkForNewMessages() {
75 const sessionId = getChatSession();
76 const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
77
78 if (!chatPersistenceEnabled) return;
79
80 $.ajax({
81 url: mxchatChat.ajax_url,
82 type: 'POST',
83 data: {
84 action: 'mxchat_check_new_messages',
85 session_id: sessionId,
86 last_seen_id: lastSeenMessageId,
87 nonce: mxchatChat.nonce
88 },
89 success: function(response) {
90 if (response.success && response.data.hasNewMessages) {
91 showNotification();
92 }
93 }
94 });
95 }
96
97 // Function to show notification
98 function showNotification() {
99 const badge = document.getElementById('chat-notification-badge');
100 if (badge && $('#floating-chatbot').hasClass('hidden')) {
101 badge.style.display = 'block';
102 badge.textContent = '1';
103 }
104 }
105
106 function hideNotification() {
107 const badge = document.getElementById('chat-notification-badge');
108 if (badge) {
109 badge.style.display = 'none';
110 }
111 }
112 // Function to start notification checking
113 function startNotificationChecking() {
114 const chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
115 if (!chatPersistenceEnabled) return;
116
117 createNotificationBadge();
118 notificationCheckInterval = setInterval(checkForNewMessages, 30000); // Check every 30 seconds
119 }
120
121 // Function to stop notification checking
122 function stopNotificationChecking() {
123 if (notificationCheckInterval) {
124 clearInterval(notificationCheckInterval);
125 }
126 }
127
128
129
130
131 function getChatSession() {
132 var sessionId = getCookie('mxchat_session_id');
133 //console.log("Session ID retrieved from cookie: ", sessionId);
134
135 if (!sessionId) {
136 sessionId = generateSessionId();
137 //console.log("Generated new session ID: ", sessionId);
138 setChatSession(sessionId);
139 }
140
141 //console.log("Final session ID: ", sessionId);
142 return sessionId;
143 }
144
145 function setChatSession(sessionId) {
146 // Set the cookie with a 24-hour expiration (86400 seconds)
147 document.cookie = "mxchat_session_id=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
148 }
149
150 // Get cookie value by name
151 function getCookie(name) {
152 let value = "; " + document.cookie;
153 let parts = value.split("; " + name + "=");
154 if (parts.length == 2) return parts.pop().split(";").shift();
155 }
156
157 // Generate a new session ID
158 function generateSessionId() {
159 return 'mxchat_chat_' + Math.random().toString(36).substr(2, 9);
160 }
161
162 // Function to send the message to the chatbot (backend)
163 function sendMessageToChatbot(message) {
164 var sessionId = getChatSession(); // Reuse the session ID logic
165
166 // Hide the popular questions section
167 $('#mxchat-popular-questions').hide();
168
169 // Show thinking indicator (no need to append the user's message again)
170 appendThinkingMessage();
171 scrollToBottom();
172
173 //console.log("Sending message to chatbot:", message); // Log the message
174 //console.log("Session ID:", sessionId); // Log the session ID
175
176 // Call the chatbot using the same call logic as sendMessage
177 callMxChat(message, function(response) {
178 // ** Ensure temporary thinking message is removed before adding new response **
179 $('.temporary-message').remove();
180
181 // Replace thinking indicator with actual response
182 replaceLastMessage("bot", response);
183 });
184 }
185
186
187
188
189 function sendMessage() {
190 var message = $('#chat-input').val(); // Get value from textarea
191 if (message) {
192 appendMessage("user", message); // Append user's message
193 $('#chat-input').val(''); // Clear the textarea
194 $('#chat-input').css('height', 'auto'); // Reset height after clearing content
195
196 // Hide the popular questions section
197 $('#mxchat-popular-questions').hide();
198
199 // Show typing indicator
200 appendThinkingMessage();
201 scrollToBottom();
202
203 callMxChat(message, function(response) {
204 // Replace typing indicator with actual response
205 replaceLastMessage("bot", response);
206 });
207 }
208 }
209
210
211
212 // Function to append a thinking message with animation
213 function appendThinkingMessage() {
214 // Remove any existing thinking dots first
215 $('.thinking-dots').remove();
216
217 // Retrieve the bot message font color and background color
218 var botMessageFontColor = mxchatChat.bot_message_font_color;
219 var botMessageBgColor = mxchatChat.bot_message_bg_color;
220
221
222 var thinkingHtml = '<div class="thinking-dots-container">' +
223 '<div class="thinking-dots">' +
224 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
225 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
226 '<span class="dot" style="background-color: ' + botMessageFontColor + ';"></span>' +
227 '</div>' +
228 '</div>';
229
230 // Append the thinking dots to the chat container (or within the temporary message div)
231 $("#chat-box").append('<div class="bot-message temporary-message" style="background-color: ' + botMessageBgColor + ';">' + thinkingHtml + '</div>');
232 scrollToBottom();
233 }
234
235 // Trigger send button click when "Enter" key is pressed in the textarea
236 $('#chat-input').keypress(function(e) {
237 if (e.which == 13 && !e.shiftKey) { // Check if "Enter" is pressed without Shift
238 e.preventDefault(); // Prevent default "Enter" behavior
239 $('#send-button').click(); // Trigger send button click
240 }
241 });
242
243 // Handle send button click
244 $('#send-button').click(function() {
245 sendMessage();
246 });
247
248 // Handle click on popular questions
249 $('.mxchat-popular-question').on('click', function () {
250 var question = $(this).text(); // Get the text of the clicked question
251
252 // Append the question as if the user typed it
253 appendMessage("user", question);
254
255 // Send the question to the server (backend)
256 sendMessageToChatbot(question);
257 });
258
259
260 // Add this new function to handle markdown headers
261 function formatMarkdownHeaders(text) {
262 // Handle h1 to h6 headers
263 return text.replace(/^(#{1,6})\s(.+)$/gm, function(match, hashes, content) {
264 const level = hashes.length;
265 return `<h${level} class="chat-heading">${content}</h${level}>`;
266 });
267 }
268
269 // Update the linkify function to handle URLs, markdown, and phone numbers
270 function linkify(inputText) {
271 if (!inputText) return '';
272
273 // Process markdown headers
274 let processedText = formatMarkdownHeaders(inputText);
275
276 // Process markdown links
277 const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
278 processedText = processedText.replace(markdownLinkPattern, (match, text, url) => {
279 const safeUrl = encodeURI(url);
280 const safeText = sanitizeUserInput(text);
281 return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`;
282 });
283
284 // Process phone numbers (tel:)
285 const phonePattern = /\[([^\]]+)\]\((tel:[\d+]+)\)/g;
286 processedText = processedText.replace(phonePattern, (match, text, phone) => {
287 const safePhone = encodeURI(phone);
288 const safeText = sanitizeUserInput(text);
289 return `<a href="${safePhone}">${safeText}</a>`;
290 });
291
292 // Process standalone URLs
293 const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
294 processedText = processedText.replace(urlPattern, (match, prefix, url) => {
295 const safeUrl = encodeURI(url);
296 return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
297 });
298
299 // Process www. URLs
300 const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
301 processedText = processedText.replace(wwwPattern, (match, prefix, url) => {
302 const safeUrl = encodeURI(`http://${url}`);
303 return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
304 });
305
306 return processedText;
307 }
308
309 function scrollElementToTop(element) {
310 var chatBox = $('#chat-box');
311 var elementTop = element.position().top + chatBox.scrollTop();
312 chatBox.animate({ scrollTop: elementTop }, 500);
313 }
314
315
316 // Optimized scrollToBottom function for instant scrolling
317 function scrollToBottom(instant = false) {
318 var chatBox = $('#chat-box');
319 if (instant) {
320 // Instantly set the scroll position to the bottom
321 chatBox.scrollTop(chatBox.prop("scrollHeight"));
322 } else {
323 // Use requestAnimationFrame for smoother scrolling if needed
324 let start = null;
325 const scrollHeight = chatBox.prop("scrollHeight");
326 const initialScroll = chatBox.scrollTop();
327 const distance = scrollHeight - initialScroll;
328 const duration = 500; // Duration in ms
329
330 function smoothScroll(timestamp) {
331 if (!start) start = timestamp;
332 const progress = timestamp - start;
333 const currentScroll = initialScroll + (distance * (progress / duration));
334 chatBox.scrollTop(currentScroll);
335
336 if (progress < duration) {
337 requestAnimationFrame(smoothScroll);
338 } else {
339 chatBox.scrollTop(scrollHeight); // Ensure it's exactly at the bottom
340 }
341 }
342
343 requestAnimationFrame(smoothScroll);
344 }
345 }
346
347
348 // Function to format text with **bold** inside double asterisks
349 function formatBoldText(text) {
350 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
351 }
352
353 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
354 function convertNewlinesToBreaks(text) {
355 // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
356 const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
357
358 // Wrap each paragraph in <p> tags
359 return paragraphs
360 .map(para => `<p>${para.trim()}</p>`)
361 .join('');
362 }
363 // Copy to clipboard function
364 // Function to copy text to clipboard
365 function copyToClipboard(text) {
366 var tempInput = $('<input>');
367 $('body').append(tempInput);
368 tempInput.val(text).select();
369 document.execCommand('copy');
370 tempInput.remove();
371 }
372
373
374 function updateChatModeIndicator(mode) {
375 const indicator = document.getElementById('chat-mode-indicator');
376 if (indicator) {
377 // For Live Agent, keep as is; for AI mode, use the customized text
378 if (mode === 'agent') {
379 indicator.textContent = 'Live Agent';
380 } else {
381 // Get the custom AI agent text from a data attribute we'll add to the element
382 const customAiText = indicator.getAttribute('data-ai-text') || 'AI Agent';
383 indicator.textContent = customAiText;
384 }
385 }
386 // Start or stop polling based on mode
387 if (mode === 'agent') {
388 startPolling();
389 } else {
390 stopPolling();
391 }
392 }
393
394 function callMxChat(message, callback) {
395 $.ajax({
396 url: mxchatChat.ajax_url,
397 type: 'POST',
398 dataType: 'json',
399 data: {
400 action: 'mxchat_handle_chat_request',
401 message: message,
402 session_id: getChatSession(),
403 nonce: mxchatChat.nonce
404 },
405 success: function(response) {
406 // Log the full response for debugging
407 console.log("API Response:", response);
408
409 // First check if this is a successful response by looking for text, html, or message fields
410 // This preserves compatibility with your server response format
411 if (response.text !== undefined || response.html !== undefined || response.message !== undefined ||
412 (response.success === true && response.data && response.data.status === 'waiting_for_agent')) {
413
414 // Handle successful response - this is your original success handling code
415
416 // Existing chat mode check
417 if (response.chat_mode) {
418 updateChatModeIndicator(response.chat_mode);
419 }
420 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
421 updateChatModeIndicator(response.fallbackResponse.chat_mode);
422 }
423
424 // Add PDF filename handling
425 if (response.data && response.data.filename) {
426 showActivePdf(response.data.filename);
427 activePdfFile = response.data.filename;
428 }
429
430 // Add redirect check here
431 if (response.redirect_url) {
432 let responseText = response.text || '';
433 if (responseText) {
434 replaceLastMessage("bot", responseText);
435 }
436 setTimeout(() => {
437 window.location.href = response.redirect_url;
438 }, 1500);
439 return;
440 }
441
442 // Check for live agent response
443 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
444 updateChatModeIndicator('agent');
445 return;
446 }
447
448 // Handle other responses
449 let responseText = response.text || '';
450 let responseHtml = response.html || '';
451 let responseMessage = response.message || '';
452
453 if (responseText === 'You are now chatting with the AI chatbot.') {
454 updateChatModeIndicator('ai');
455 }
456
457 // Handle the message and show notification if chat is hidden
458 if (responseText || responseHtml || responseMessage) {
459 // Update the messages as before
460 if (responseText && responseHtml) {
461 replaceLastMessage("bot", responseText, responseHtml);
462 } else if (responseText) {
463 replaceLastMessage("bot", responseText);
464 } else if (responseHtml) {
465 replaceLastMessage("bot", "", responseHtml);
466 } else if (responseMessage) {
467 replaceLastMessage("bot", responseMessage);
468 }
469
470 // Check if chat is hidden and show notification
471 if ($('#floating-chatbot').hasClass('hidden')) {
472 const badge = $('#chat-notification-badge');
473 if (badge.length) {
474 badge.show();
475 }
476 }
477 } else {
478 console.error("Unexpected response format:", response);
479 replaceLastMessage("bot", "I received an empty response. Please try again or contact support if this persists.");
480 }
481
482 if (response.message_id) {
483 lastSeenMessageId = response.message_id;
484 }
485
486 return;
487 }
488
489 // If we got here, it's likely an error response
490 // Now we can check for error conditions with our robust error handling
491
492 let errorMessage = "";
493 let errorCode = "";
494
495 // Check various possible error locations in the response
496 if (response.data && response.data.error_message) {
497 errorMessage = response.data.error_message;
498 errorCode = response.data.error_code || "";
499 } else if (response.error_message) {
500 errorMessage = response.error_message;
501 errorCode = response.error_code || "";
502 } else if (response.message) {
503 errorMessage = response.message;
504 } else if (typeof response.data === 'string') {
505 errorMessage = response.data;
506 } else if (!response.success) {
507 // Explicit check for success: false without other error info
508 errorMessage = "An error occurred. Please try again or contact support.";
509 } else {
510 // Fallback for any other unexpected response format
511 errorMessage = "Unexpected response received. Please try again or contact support.";
512 }
513
514 // Log the error with code for debugging
515 console.log("Response data:", response.data);
516 console.error("API Error:", errorMessage, "Code:", errorCode);
517
518 // Format user-friendly error message
519 let displayMessage = errorMessage;
520
521 // Customize message for admin users
522 if (mxchatChat.is_admin) {
523 // For admin users, show more technical details including error code
524 displayMessage = errorMessage + (errorCode ? " (Error code: " + errorCode + ")" : "");
525 }
526
527 replaceLastMessage("bot", displayMessage);
528 },
529 error: function(xhr, status, error) {
530 console.error("AJAX Error:", status, error);
531 console.log("Response Text:", xhr.responseText);
532
533 let errorMessage = "An unexpected error occurred.";
534
535 // Try to parse the response if it's JSON
536 try {
537 const responseJson = JSON.parse(xhr.responseText);
538 console.log("Parsed error response:", responseJson);
539
540 if (responseJson.data && responseJson.data.error_message) {
541 errorMessage = responseJson.data.error_message;
542 } else if (responseJson.message) {
543 errorMessage = responseJson.message;
544 }
545 } catch (e) {
546 // Not JSON or parsing failed, use HTTP status based messages
547 if (xhr.status === 0) {
548 errorMessage = "Network error: Please check your internet connection.";
549 } else if (xhr.status === 403) {
550 errorMessage = "Access denied: Your session may have expired. Please refresh the page.";
551 } else if (xhr.status === 404) {
552 errorMessage = "API endpoint not found. Please contact support.";
553 } else if (xhr.status === 429) {
554 errorMessage = "Too many requests. Please try again in a moment.";
555 } else if (xhr.status >= 500) {
556 errorMessage = "Server error: The server encountered an issue. Please try again later.";
557 }
558 }
559
560 replaceLastMessage("bot", errorMessage);
561 }
562 });
563 }
564 // Sanitize only user input
565 function sanitizeUserInput(text) {
566 const div = document.createElement('div');
567 div.textContent = text;
568 return div.innerHTML;
569 }
570
571 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
572 try {
573 // Determine styles based on sender type
574 let messageClass, bgColor, fontColor;
575
576 if (sender === "user") {
577 messageClass = "user-message";
578 bgColor = userMessageBgColor;
579 fontColor = userMessageFontColor;
580 // Only sanitize user input
581 messageText = sanitizeUserInput(messageText);
582 } else if (sender === "agent") {
583 messageClass = "agent-message";
584 bgColor = liveAgentMessageBgColor;
585 fontColor = liveAgentMessageFontColor;
586 } else {
587 messageClass = "bot-message";
588 bgColor = botMessageBgColor;
589 fontColor = botMessageFontColor;
590 }
591
592 const messageDiv = $('<div>')
593 .addClass(messageClass)
594 .attr('dir', 'auto') // Add dir="auto" for automatic text direction
595 .css({
596 'background': bgColor,
597 'color': fontColor,
598 'margin-bottom': '1em'
599 });
600
601 // Process the message content based on sender
602 let fullMessage;
603 if (sender === "user") {
604 // For user messages, apply linkify after sanitization
605 fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
606 } else {
607 // For bot/agent messages, preserve HTML
608 fullMessage = messageText;
609 }
610
611 // Add images if provided
612 if (images && images.length > 0) {
613 fullMessage += '<div class="image-gallery" dir="auto">'; // Add dir="auto" to image gallery
614 images.forEach(img => {
615 // Ensure image URLs and titles are properly escaped
616 const safeTitle = sanitizeUserInput(img.title);
617 const safeUrl = encodeURI(img.image_url);
618 const safeThumbnail = encodeURI(img.thumbnail_url);
619
620 fullMessage += `
621 <div style="margin-bottom: 10px;">
622 <strong>${safeTitle}</strong><br>
623 <a href="${safeUrl}" target="_blank">
624 <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" />
625 </a>
626 </div>`;
627 });
628 fullMessage += '</div>';
629 }
630
631 // Append HTML content if provided
632 if (messageHtml && sender !== "user") {
633 fullMessage += '<br><br>' + messageHtml;
634 }
635
636 messageDiv.html(fullMessage);
637
638 if (isTemporary) {
639 messageDiv.addClass('temporary-message');
640 }
641
642 messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
643 if (sender === "bot") {
644 const lastUserMessage = $('#chat-box').find('.user-message').last();
645 if (lastUserMessage.length) {
646 scrollElementToTop(lastUserMessage);
647 }
648 }
649 });
650
651 if (messageText.id) {
652 lastSeenMessageId = messageText.id;
653 hideNotification();
654 }
655 } catch (error) {
656 console.error("Error rendering message:", error);
657 }
658 }
659
660 function replaceLastMessage(sender, responseText, responseHtml = '', images = []) {
661 var messageClass = sender === "user" ? "user-message" : sender === "agent" ? "agent-message" : "bot-message";
662 var lastMessageDiv = $('#chat-box').find('.bot-message.temporary-message, .agent-message.temporary-message').last();
663
664 // Determine styles
665 let bgColor, fontColor;
666 if (sender === "user") {
667 bgColor = userMessageBgColor;
668 fontColor = userMessageFontColor;
669 } else if (sender === "agent") {
670 bgColor = liveAgentMessageBgColor;
671 fontColor = liveAgentMessageFontColor;
672 } else {
673 bgColor = botMessageBgColor;
674 fontColor = botMessageFontColor;
675 }
676
677 var fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(responseText))));
678 if (responseHtml) {
679 fullMessage += '<br><br>' + responseHtml;
680 }
681
682 if (images.length > 0) {
683 fullMessage += '<div class="image-gallery" dir="auto">'; // Add dir="auto" to image gallery
684 images.forEach(img => {
685 fullMessage += `
686 <div style="margin-bottom: 10px;">
687 <strong>${img.title}</strong><br>
688 <a href="${img.image_url}" target="_blank">
689 <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
690 </a>
691 </div>`;
692 });
693 fullMessage += '</div>';
694 }
695
696 if (lastMessageDiv.length) {
697 lastMessageDiv.fadeOut(200, function() {
698 $(this)
699 .html(fullMessage)
700 .removeClass('bot-message user-message')
701 .addClass(messageClass)
702 .attr('dir', 'auto') // Add dir="auto" for automatic text direction
703 .css({
704 'background-color': bgColor,
705 'color': fontColor,
706 })
707 .removeClass('temporary-message')
708 .fadeIn(200, function() {
709 if (sender === "bot" || sender === "agent") {
710 const lastUserMessage = $('#chat-box').find('.user-message').last();
711 if (lastUserMessage.length) {
712 scrollElementToTop(lastUserMessage);
713 }
714 // Show notification if chat is hidden
715 if ($('#floating-chatbot').hasClass('hidden')) {
716 showNotification();
717 }
718 }
719 });
720 });
721 } else {
722 appendMessage(sender, responseText, responseHtml, images);
723 }
724 }
725
726
727
728 function startPolling() {
729 // Clear any existing interval first
730 stopPolling();
731 // Start new polling interval
732 pollingInterval = setInterval(checkForAgentMessages, 5000);
733 //console.log("Started agent message polling");
734 }
735
736 function stopPolling() {
737 if (pollingInterval) {
738 clearInterval(pollingInterval);
739 pollingInterval = null;
740 //console.log("Stopped agent message polling");
741 }
742 }
743
744
745 // Update your checkForAgentMessages function
746 function checkForAgentMessages() {
747 const sessionId = getChatSession();
748 $.ajax({
749 url: mxchatChat.ajax_url,
750 type: 'POST',
751 dataType: 'json',
752 data: {
753 action: 'mxchat_fetch_new_messages',
754 session_id: sessionId,
755 last_seen_id: lastSeenMessageId,
756 nonce: mxchatChat.nonce
757 },
758 success: function (response) {
759 if (response.success && response.data?.new_messages) {
760 let hasNewMessage = false;
761
762 response.data.new_messages.forEach(function (message) {
763 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
764 hasNewMessage = true;
765 replaceLastMessage("agent", message.content);
766 lastSeenMessageId = message.id;
767 processedMessageIds.add(message.id);
768 }
769 });
770
771 if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
772 showNotification();
773 }
774
775 scrollToBottom(true);
776 }
777 },
778 error: function (xhr, status, error) {
779 console.error("Polling error:", xhr, status, error);
780 }
781 });
782 }
783
784
785
786 function loadChatHistory() {
787 var sessionId = getChatSession();
788 var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
789
790 if (chatPersistenceEnabled && sessionId) {
791 $.ajax({
792 url: mxchatChat.ajax_url,
793 type: 'POST',
794 dataType: 'json',
795 data: {
796 action: 'mxchat_fetch_conversation_history',
797 session_id: sessionId
798 },
799 success: function(response) {
800 if (response.success && response.data && Array.isArray(response.data.conversation)) {
801
802
803 var $chatBox = $('#chat-box');
804 var $fragment = $(document.createDocumentFragment());
805 let highestMessageId = lastSeenMessageId;
806
807 if (response.data.chat_mode) {
808 updateChatModeIndicator(response.data.chat_mode);
809 }
810
811 $.each(response.data.conversation, function(index, message) {
812 // Skip agent messages if persistence is off
813 if (!chatPersistenceEnabled && message.role === 'agent') {
814 return;
815 }
816
817 var messageClass, messageBgColor, messageFontColor;
818
819 switch (message.role) {
820 case 'user':
821 messageClass = 'user-message';
822 messageBgColor = userMessageBgColor;
823 messageFontColor = userMessageFontColor;
824 break;
825 case 'agent':
826 messageClass = 'agent-message';
827 messageBgColor = liveAgentMessageBgColor;
828 messageFontColor = liveAgentMessageFontColor;
829 break;
830 default:
831 messageClass = 'bot-message';
832 messageBgColor = botMessageBgColor;
833 messageFontColor = botMessageFontColor;
834 break;
835 }
836
837 var messageElement = $('<div>').addClass(messageClass)
838 .css({
839 'background': messageBgColor,
840 'color': messageFontColor
841 });
842
843 var content = message.content;
844 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
845 content = decodeHTMLEntities(content);
846
847 if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
848 messageElement.html(content);
849 } else {
850 var formattedContent = linkify(
851 formatBoldText(
852 convertNewlinesToBreaks(formatCodeBlocks(content))
853 )
854 );
855 messageElement.html(formattedContent);
856 }
857
858 $fragment.append(messageElement);
859
860 // In loadChatHistory, change this part:
861 if (message.id) {
862 highestMessageId = Math.max(highestMessageId, message.id);
863 processedMessageIds.add(message.id); // Add all message IDs to processed set
864 }
865 });
866
867 $chatBox.append($fragment);
868 scrollToBottom(true);
869
870 if (response.data.conversation.length > 0) {
871 $('#mxchat-popular-questions').hide();
872 }
873
874 // Update lastSeenMessageId after history loads
875 lastSeenMessageId = highestMessageId;
876
877 // Only update chat mode if persistence is enabled
878 if (chatPersistenceEnabled && response.data.conversation.length > 0) {
879 var lastMessage = response.data.conversation[response.data.conversation.length - 1];
880 if (lastMessage.role === 'agent') {
881 updateChatModeIndicator('agent');
882 }
883 }
884 } else {
885 console.warn("No conversation history found.");
886 }
887 },
888 error: function(xhr, status, error) {
889 console.error("Error loading chat history:", status, error);
890 appendMessage("bot", "Unable to load chat history.");
891 }
892 });
893 } else {
894 console.warn("Chat persistence is disabled or no session ID found. Not loading history.");
895 }
896 }
897
898 // Function to decode HTML entities
899 function decodeHTMLEntities(text) {
900 var textArea = document.createElement('textarea');
901 textArea.innerHTML = text;
902 return textArea.value;
903 }
904
905
906 // Update formatCodeBlocks function
907 function formatCodeBlocks(text) {
908 // First handle raw PHP tags
909 text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
910 return `<pre><code class="language-php">${escapeHtml(match)}</code></pre>`;
911 });
912
913 // Then handle code blocks with backticks
914 text = text.replace(/```php5?\n([\s\S]+?)```/gi, (match, code) => {
915 return `<pre><code class="language-php">${escapeHtml(code)}</code></pre>`;
916 });
917
918 return text;
919 }
920
921 // Update escapeHtml function to preserve existing code blocks
922 function escapeHtml(unsafe) {
923 // First check if it's already a code block
924 if (unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
925 return unsafe;
926 }
927
928 return unsafe
929 .replace(/&/g, "&amp;")
930 .replace(/</g, "&lt;")
931 .replace(/>/g, "&gt;")
932 .replace(/"/g, "&quot;")
933 .replace(/'/g, "&#039;");
934 }
935 // Utility function to escape HTML
936 function escapeHtml(unsafe) {
937 return unsafe
938 .replace(/&/g, "&amp;")
939 .replace(/</g, "&lt;")
940 .replace(/>/g, "&gt;")
941 .replace(/"/g, "&quot;")
942 .replace(/'/g, "&#039;");
943 }
944
945
946
947
948 // Function to convert newlines, skipping preformatted text
949 function convertNewlinesToBreaks(text) {
950 // Split while preserving code blocks
951 return text.split(/(<pre\b[^>]*>[\s\S]*?<\/pre>)/g).map(part => {
952 if (part.startsWith('<pre')) return part;
953 return part.replace(/(^|[^>])\n/g, '$1<br>');
954 }).join('');
955 }
956
957
958
959
960 // Helper function to check if a string is an image HTML
961 function isImageHtml(str) {
962 return str.startsWith('<img') && str.endsWith('>');
963 }
964
965 // Function to remove thinking dots
966 function removeThinkingDots() {
967 $('.thinking-dots').closest('.temporary-message').remove();
968 }
969
970 function isMobile() {
971 // This can be a simple check, or more sophisticated detection of mobile devices
972 return window.innerWidth <= 768; // Example threshold for mobile devices
973 }
974
975 function disableScroll() {
976 if (isMobile()) {
977 $('body').css('overflow', 'hidden');
978 }
979 }
980
981 function enableScroll() {
982 if (isMobile()) {
983 $('body').css('overflow', '');
984 }
985 }
986
987 // Pre-chat dismissal check function (wrapped in a function for reuse)
988 function checkPreChatDismissal() {
989 $.ajax({
990 url: mxchatChat.ajax_url,
991 type: 'POST',
992 data: {
993 action: 'mxchat_check_pre_chat_message_status',
994 _ajax_nonce: mxchatChat.nonce
995 },
996 success: function(response) {
997 if (response.success && !response.data.dismissed) {
998 $('#pre-chat-message').fadeIn(250);
999 } else {
1000 $('#pre-chat-message').hide();
1001 }
1002 },
1003 error: function() {
1004 console.error('Failed to check pre-chat message dismissal status.');
1005 }
1006 });
1007 }
1008
1009 // Function to show the chatbot widget
1010 function showChatWidget() {
1011 // First ensure display is set
1012 $('#floating-chatbot-button').css('display', 'flex');
1013 // Then handle the fade
1014 $('#floating-chatbot-button').fadeTo(500, 1);
1015 // Force visibility
1016 $('#floating-chatbot-button').removeClass('hidden');
1017 //console.log('Showing widget');
1018 }
1019
1020 // Function to hide the chatbot widget
1021 function hideChatWidget() {
1022 $('#floating-chatbot-button').css('display', 'none');
1023 $('#floating-chatbot-button').addClass('hidden');
1024 //console.log('Hiding widget');
1025 }
1026
1027 function initializeChatVisibility() {
1028 //console.log('Initializing chat visibility');
1029 const complianzEnabled = mxchatChat.complianz_toggle === 'on' ||
1030 mxchatChat.complianz_toggle === '1' ||
1031 mxchatChat.complianz_toggle === 1;
1032
1033 if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
1034 // Initial check
1035 checkConsentAndShowChat();
1036
1037 // Listen for consent changes
1038 $(document).on('cmplz_status_change', function(event) {
1039 //console.log('Status change detected');
1040 checkConsentAndShowChat();
1041 });
1042 } else {
1043 // If Complianz is not enabled, always show
1044 $('#floating-chatbot-button')
1045 .css('display', 'flex')
1046 .removeClass('hidden no-consent')
1047 .fadeTo(500, 1);
1048
1049 // Also check pre-chat message when Complianz is not enabled
1050 checkPreChatDismissal();
1051 }
1052 }
1053
1054
1055
1056 function checkConsentAndShowChat() {
1057 var consentStatus = cmplz_has_consent('marketing');
1058 var consentType = complianz.consenttype;
1059
1060 //console.log('Checking consent:', {status: consentStatus,type: consentType});
1061
1062 let $widget = $('#floating-chatbot-button');
1063 let $chatbot = $('#floating-chatbot');
1064 let $preChat = $('#pre-chat-message');
1065
1066 if (consentStatus === true) {
1067 //console.log('Consent granted - showing widget');
1068 $widget
1069 .removeClass('no-consent')
1070 .css('display', 'flex')
1071 .removeClass('hidden')
1072 .fadeTo(500, 1);
1073 $chatbot.removeClass('no-consent');
1074
1075 // Show pre-chat message if not dismissed
1076 checkPreChatDismissal();
1077 } else {
1078 //console.log('No consent - hiding widget');
1079 $widget
1080 .addClass('no-consent')
1081 .fadeTo(500, 0, function() {
1082 $(this)
1083 .css('display', 'none')
1084 .addClass('hidden');
1085 });
1086 $chatbot.addClass('no-consent');
1087
1088 // Hide pre-chat message when no consent
1089 $preChat.hide();
1090 }
1091 }
1092
1093 // Function to dismiss pre-chat message for 24 hours
1094 function handlePreChatDismissal() {
1095 $('#pre-chat-message').fadeOut(200);
1096 $.ajax({
1097 url: mxchatChat.ajax_url,
1098 type: 'POST',
1099 data: {
1100 action: 'mxchat_dismiss_pre_chat_message',
1101 _ajax_nonce: mxchatChat.nonce
1102 },
1103 success: function() {
1104 $('#pre-chat-message').hide();
1105 },
1106 error: function() {
1107 console.error('Failed to dismiss pre-chat message.');
1108 }
1109 });
1110 }
1111
1112 // Handle pre-chat message dismissal on button click
1113 $(document).on('click', '.close-pre-chat-message', function(e) {
1114 e.stopPropagation();
1115 handlePreChatDismissal();
1116 });
1117
1118 // Toggle chatbot visibility on floating button click
1119 $(document).on('click', '#floating-chatbot-button', function() {
1120 var chatbot = $('#floating-chatbot');
1121 if (chatbot.hasClass('hidden')) {
1122 chatbot.removeClass('hidden').addClass('visible');
1123 $(this).addClass('hidden');
1124 $('#chat-notification-badge').hide(); // Hide notification when opening chat
1125 disableScroll();
1126 $('#pre-chat-message').fadeOut(250);
1127 } else {
1128 chatbot.removeClass('visible').addClass('hidden');
1129 $(this).removeClass('hidden');
1130 enableScroll();
1131 checkPreChatDismissal();
1132 }
1133 });
1134
1135 $(document).on('click', '#exit-chat-button', function() {
1136 $('#floating-chatbot').addClass('hidden').removeClass('visible');
1137 $('#floating-chatbot-button').removeClass('hidden');
1138 enableScroll();
1139 });
1140
1141 // Close pre-chat message on click
1142 $(document).on('click', '.close-pre-chat-message', function(e) {
1143 e.stopPropagation(); // Prevent triggering the parent .pre-chat-message click
1144 $('#pre-chat-message').fadeOut(200, function() {
1145 $(this).remove();
1146 });
1147 });
1148
1149 // Open chatbot when pre-chat message is clicked
1150 $(document).on('click', '#pre-chat-message', function() {
1151 var chatbot = $('#floating-chatbot');
1152 if (chatbot.hasClass('hidden')) {
1153 chatbot.removeClass('hidden').addClass('visible');
1154 $('#floating-chatbot-button').addClass('hidden');
1155 $('#pre-chat-message').fadeOut(250); // Hide pre-chat message
1156 disableScroll(); // Disable scroll when chatbot opens
1157 }
1158 });
1159
1160 // If the chatbot is initially hidden, ensure the button is visible
1161 if ($('#floating-chatbot').hasClass('hidden')) {
1162 $('#floating-chatbot-button').removeClass('hidden');
1163 }
1164
1165 function setFullHeight() {
1166 var vh = $(window).innerHeight() * 0.01;
1167 $(':root').css('--vh', vh + 'px');
1168 }
1169
1170 // Set the height when the page loads
1171
1172
1173 // Set the height on resize and orientation change events
1174 $(window).on('resize orientationchange', function() {
1175 setFullHeight();
1176 });
1177
1178
1179 // Now handle the close button to dismiss the pre-chat message for 24 hours
1180 var closeButton = document.querySelector('.close-pre-chat-message');
1181 if (closeButton) {
1182 closeButton.addEventListener('click', function() {
1183 $('#pre-chat-message').fadeOut(200); // Hide the message
1184
1185 // Send an AJAX request to set the transient flag for 24 hours
1186 $.ajax({
1187 url: mxchatChat.ajax_url,
1188 type: 'POST',
1189 data: {
1190 action: 'mxchat_dismiss_pre_chat_message',
1191 _ajax_nonce: mxchatChat.nonce
1192 },
1193 success: function() {
1194 //console.log('Pre-chat message dismissed for 24 hours.');
1195
1196 // Ensure the message is hidden after dismissal
1197 $('#pre-chat-message').hide();
1198 },
1199 error: function() {
1200 //console.error('Failed to dismiss pre-chat message.');
1201 }
1202 });
1203 });
1204 }
1205
1206
1207
1208
1209 // Event listener for Add to Cart button
1210 $(document).on('click', '.mxchat-add-to-cart-button', function() {
1211 var productId = $(this).data('product-id');
1212 // Add a special prefix to indicate this is from button
1213 appendMessage("user", "add to cart");
1214 sendMessageToChatbot("!addtocart"); // Special command to indicate button click
1215 });
1216
1217
1218 if (document.getElementById('pdf-upload-btn')) {
1219 document.getElementById('pdf-upload-btn').addEventListener('click', function() {
1220 document.getElementById('pdf-upload').click();
1221 });
1222 }
1223
1224 if (document.getElementById('word-upload-btn')) {
1225 document.getElementById('word-upload-btn').addEventListener('click', function() {
1226 document.getElementById('word-upload').click();
1227 });
1228 }
1229
1230 function addSafeEventListener(elementId, eventType, handler) {
1231 const element = document.getElementById(elementId);
1232 if (element) {
1233 element.addEventListener(eventType, handler);
1234 }
1235 }
1236
1237
1238 // PDF file input change handler
1239 addSafeEventListener('pdf-upload', 'change', async function(e) {
1240 const file = e.target.files[0];
1241
1242 if (!file || file.type !== 'application/pdf') {
1243 alert('Please select a valid PDF file.');
1244 return;
1245 }
1246
1247 if (!sessionId) {
1248 console.error('No session ID found');
1249 alert('Error: No session ID found');
1250 return;
1251 }
1252
1253 if (!mxchatChat || !mxchatChat.ajax_url || !mxchatChat.nonce) {
1254 console.error('mxchatChat not properly configured:', mxchatChat);
1255 alert('Error: Ajax configuration missing');
1256 return;
1257 }
1258
1259 // Disable buttons and show loading state
1260 const uploadBtn = document.getElementById('pdf-upload-btn');
1261 const sendBtn = document.getElementById('send-button');
1262 const originalBtnContent = uploadBtn.innerHTML;
1263
1264 try {
1265 const formData = new FormData();
1266 formData.append('action', 'mxchat_upload_pdf');
1267 formData.append('pdf_file', file);
1268 formData.append('session_id', sessionId);
1269 formData.append('nonce', mxchatChat.nonce);
1270
1271 uploadBtn.disabled = true;
1272 sendBtn.disabled = true;
1273 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1274 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1275 </svg>`;
1276
1277 const response = await fetch(mxchatChat.ajax_url, {
1278 method: 'POST',
1279 body: formData
1280 });
1281
1282 const data = await response.json();
1283
1284 if (data.success) {
1285 // Hide popular questions if they exist
1286 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1287 if (popularQuestionsContainer) {
1288 popularQuestionsContainer.style.display = 'none';
1289 }
1290
1291 // Show the active PDF name
1292 showActivePdf(data.data.filename);
1293
1294 appendMessage('bot', data.data.message);
1295 scrollToBottom();
1296 activePdfFile = data.data.filename;
1297 } else {
1298 console.error('Upload failed:', data.data);
1299 alert('Failed to upload PDF. Please try again.');
1300 }
1301 } catch (error) {
1302 console.error('Upload error:', error);
1303 alert('Error uploading file. Please try again.');
1304 } finally {
1305 uploadBtn.disabled = false;
1306 sendBtn.disabled = false;
1307 uploadBtn.innerHTML = originalBtnContent;
1308 this.value = ''; // Reset file input
1309 }
1310 });
1311
1312 // Word file input change handler
1313 addSafeEventListener('word-upload', 'change', async function(e) {
1314 const file = e.target.files[0];
1315
1316 if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1317 alert('Please select a valid Word document (.docx).');
1318 return;
1319 }
1320
1321 if (!sessionId) {
1322 console.error('No session ID found');
1323 alert('Error: No session ID found');
1324 return;
1325 }
1326
1327 // Disable buttons and show loading state
1328 const uploadBtn = document.getElementById('word-upload-btn');
1329 const sendBtn = document.getElementById('send-button');
1330 const originalBtnContent = uploadBtn.innerHTML;
1331
1332 try {
1333 const formData = new FormData();
1334 formData.append('action', 'mxchat_upload_word');
1335 formData.append('word_file', file);
1336 formData.append('session_id', sessionId);
1337 formData.append('nonce', mxchatChat.nonce);
1338
1339 uploadBtn.disabled = true;
1340 sendBtn.disabled = true;
1341 uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1342 <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1343 </svg>`;
1344
1345 const response = await fetch(mxchatChat.ajax_url, {
1346 method: 'POST',
1347 body: formData
1348 });
1349
1350 const data = await response.json();
1351
1352 if (data.success) {
1353 // Hide popular questions if they exist
1354 const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1355 if (popularQuestionsContainer) {
1356 popularQuestionsContainer.style.display = 'none';
1357 }
1358
1359 // Show the active Word document name
1360 showActiveWord(data.data.filename);
1361
1362 appendMessage('bot', data.data.message);
1363 scrollToBottom();
1364 activeWordFile = data.data.filename;
1365 } else {
1366 console.error('Upload failed:', data.data);
1367 alert('Failed to upload Word document. Please try again.');
1368 }
1369 } catch (error) {
1370 console.error('Upload error:', error);
1371 alert('Error uploading file. Please try again.');
1372 } finally {
1373 uploadBtn.disabled = false;
1374 sendBtn.disabled = false;
1375 uploadBtn.innerHTML = originalBtnContent;
1376 this.value = ''; // Reset file input
1377 }
1378 });
1379
1380 // Function to show active PDF name in toolbar
1381 function showActivePdf(filename) {
1382 const container = document.getElementById('active-pdf-container');
1383 const nameElement = document.getElementById('active-pdf-name');
1384
1385 if (!container || !nameElement) {
1386 console.error('PDF container elements not found');
1387 return;
1388 }
1389
1390 nameElement.textContent = filename;
1391 container.style.display = 'flex';
1392 }
1393
1394 // Function to show active Word document name in toolbar
1395 function showActiveWord(filename) {
1396 const container = document.getElementById('active-word-container');
1397 const nameElement = document.getElementById('active-word-name');
1398
1399 if (!container || !nameElement) {
1400 console.error('Word document container elements not found');
1401 return;
1402 }
1403
1404 nameElement.textContent = filename;
1405 container.style.display = 'flex';
1406 }
1407
1408 // Function to remove active PDF
1409 function removeActivePdf() {
1410 const container = document.getElementById('active-pdf-container');
1411 const nameElement = document.getElementById('active-pdf-name');
1412
1413 if (!container || !nameElement || !activePdfFile) return;
1414
1415 fetch(mxchatChat.ajax_url, {
1416 method: 'POST',
1417 headers: {
1418 'Content-Type': 'application/x-www-form-urlencoded',
1419 },
1420 body: new URLSearchParams({
1421 'action': 'mxchat_remove_pdf',
1422 'session_id': sessionId,
1423 'nonce': mxchatChat.nonce
1424 })
1425 })
1426 .then(response => response.json())
1427 .then(data => {
1428 if (data.success) {
1429 container.style.display = 'none';
1430 nameElement.textContent = '';
1431 activePdfFile = null;
1432 appendMessage('bot', 'PDF removed.');
1433 }
1434 })
1435 .catch(error => {
1436 console.error('Error removing PDF:', error);
1437 });
1438 }
1439
1440 // Function to remove active Word document
1441 function removeActiveWord() {
1442 const container = document.getElementById('active-word-container');
1443 const nameElement = document.getElementById('active-word-name');
1444
1445 if (!container || !nameElement || !activeWordFile) return;
1446
1447 fetch(mxchatChat.ajax_url, {
1448 method: 'POST',
1449 headers: {
1450 'Content-Type': 'application/x-www-form-urlencoded',
1451 },
1452 body: new URLSearchParams({
1453 'action': 'mxchat_remove_word',
1454 'session_id': sessionId,
1455 'nonce': mxchatChat.nonce
1456 })
1457 })
1458 .then(response => response.json())
1459 .then(data => {
1460 if (data.success) {
1461 container.style.display = 'none';
1462 nameElement.textContent = '';
1463 activeWordFile = null;
1464 appendMessage('bot', 'Word document removed.');
1465 }
1466 })
1467 .catch(error => {
1468 console.error('Error removing Word document:', error);
1469 });
1470 }
1471
1472 // Add remove button click handlers
1473 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1474 e.preventDefault();
1475 e.stopPropagation();
1476 removeActivePdf();
1477 });
1478
1479 document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
1480 e.preventDefault();
1481 e.stopPropagation();
1482 removeActiveWord();
1483 });
1484
1485 // Check initial document status
1486 function checkInitialDocumentStatus() {
1487 if (!sessionId) return;
1488
1489 // Check PDF status
1490 fetch(mxchatChat.ajax_url, {
1491 method: 'POST',
1492 headers: {
1493 'Content-Type': 'application/x-www-form-urlencoded',
1494 },
1495 body: new URLSearchParams({
1496 'action': 'mxchat_check_pdf_status',
1497 'session_id': sessionId,
1498 'nonce': mxchatChat.nonce
1499 })
1500 })
1501 .then(response => response.json())
1502 .then(data => {
1503 if (data.success && data.data.filename) {
1504 showActivePdf(data.data.filename);
1505 activePdfFile = data.data.filename;
1506 }
1507 })
1508 .catch(error => {
1509 console.error('Error checking PDF status:', error);
1510 });
1511
1512 // Check Word document status
1513 fetch(mxchatChat.ajax_url, {
1514 method: 'POST',
1515 headers: {
1516 'Content-Type': 'application/x-www-form-urlencoded',
1517 },
1518 body: new URLSearchParams({
1519 'action': 'mxchat_check_word_status',
1520 'session_id': sessionId,
1521 'nonce': mxchatChat.nonce
1522 })
1523 })
1524 .then(response => response.json())
1525 .then(data => {
1526 if (data.success && data.data.filename) {
1527 showActiveWord(data.data.filename);
1528 activeWordFile = data.data.filename;
1529 }
1530 })
1531 .catch(error => {
1532 console.error('Error checking Word document status:', error);
1533 });
1534 }
1535
1536 // Apply toolbar settings
1537 if (mxchatChat.chat_toolbar_toggle === 'on') {
1538 $('.chat-toolbar').show();
1539 } else {
1540 $('.chat-toolbar').hide();
1541 }
1542
1543 // Initialize on page load
1544 document.addEventListener('DOMContentLoaded', function() {
1545 checkInitialDocumentStatus();
1546 });
1547
1548 const toolbarElements = [
1549 '#mxchat-chatbot .toolbar-btn svg',
1550 '#mxchat-chatbot .active-pdf-name',
1551 '#mxchat-chatbot .active-word-name',
1552 '#mxchat-chatbot .remove-pdf-btn svg',
1553 '#mxchat-chatbot .remove-word-btn svg',
1554 '#mxchat-chatbot .toolbar-perplexity svg'
1555 ];
1556
1557 toolbarElements.forEach(selector => {
1558 $(selector).css({
1559 'fill': toolbarIconColor,
1560 'stroke': toolbarIconColor,
1561 'color': toolbarIconColor
1562 });
1563 });
1564
1565
1566 // Ensure essential elements are defined
1567 const emailForm = document.getElementById('email-collection-form');
1568 const emailBlocker = document.getElementById('email-blocker');
1569 const chatbotWrapper = document.getElementById('chat-container');
1570
1571 // Only show error if email collection is enabled but elements are missing
1572 if (emailForm && emailBlocker && chatbotWrapper) {
1573 // Check if email exists for the current session
1574 function checkSessionAndEmail() {
1575 const sessionId = getChatSession();
1576 //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);
1577
1578 fetch(mxchatChat.ajax_url, {
1579 method: 'POST',
1580 headers: {
1581 'Content-Type': 'application/x-www-form-urlencoded',
1582 },
1583 body: new URLSearchParams({
1584 action: 'mxchat_check_email_provided',
1585 session_id: sessionId,
1586 nonce: mxchatChat.nonce,
1587 }),
1588 })
1589 .then((response) => response.json())
1590 .then((data) => {
1591 //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
1592
1593 if (data.success) {
1594 if (data.data.logged_in) {
1595 //console.log("[DEBUG JS] User is logged in. Hiding email form.");
1596 emailBlocker.style.display = 'none';
1597 chatbotWrapper.style.display = 'flex';
1598 } else if (data.data.email) {
1599 //console.log("[DEBUG JS] Email found for session. Hiding email form.");
1600 emailBlocker.style.display = 'none';
1601 chatbotWrapper.style.display = 'flex';
1602 } else {
1603 //console.log("[DEBUG JS] No email provided. Showing email form.");
1604 emailBlocker.style.display = 'flex';
1605 chatbotWrapper.style.display = 'none';
1606 }
1607 } else {
1608 //console.log("[DEBUG JS] Error or no data received. Showing email form.");
1609 emailBlocker.style.display = 'flex';
1610 chatbotWrapper.style.display = 'none';
1611 }
1612 })
1613 .catch((error) => {
1614 // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
1615 emailBlocker.style.display = 'flex';
1616 chatbotWrapper.style.display = 'none';
1617 });
1618 }
1619
1620 // Handle email form submission
1621 emailForm.addEventListener('submit', function (event) {
1622 event.preventDefault();
1623 const userEmail = document.getElementById('user-email').value;
1624 const sessionId = getChatSession();
1625
1626 if (userEmail) {
1627 fetch(mxchatChat.ajax_url, {
1628 method: 'POST',
1629 headers: {
1630 'Content-Type': 'application/x-www-form-urlencoded',
1631 },
1632 body: new URLSearchParams({
1633 action: 'mxchat_handle_save_email_and_response',
1634 email: userEmail,
1635 session_id: sessionId,
1636 nonce: mxchatChat.nonce,
1637 }),
1638 })
1639 .then((response) => response.json())
1640 .then((data) => {
1641 //console.log('Backend response:', data);
1642 if (data.success) {
1643 //console.log('Email saved successfully:', userEmail);
1644 emailBlocker.style.display = 'none';
1645 chatbotWrapper.style.display = 'flex';
1646
1647 // Optionally handle bot response
1648 if (data.message) {
1649 appendMessage('bot', data.message);
1650 scrollToBottom();
1651 }
1652 } else {
1653 console.error('Error saving email:', data.message || 'Unknown error');
1654 }
1655 })
1656 .catch((error) => {
1657 console.error('AJAX error:', error);
1658 });
1659 }
1660 });
1661
1662 // Check session and email status on page load
1663 checkSessionAndEmail();
1664 } else if (mxchatChat.email_collection_enabled) {
1665 // Only show error if email collection is enabled but elements are missing
1666 console.error('Essential elements for email handling are missing.');
1667 }
1668
1669
1670 // Initialize when document is ready
1671 $(document).ready(function() {
1672 setFullHeight();
1673 initializeChatVisibility();
1674 loadChatHistory();
1675
1676 });
1677
1678 });
1679
1680 // Event listener for copy button
1681 document.addEventListener("click", (e) => {
1682 if (e.target.classList.contains("mxchat-copy-button")) {
1683 const copyButton = e.target;
1684 const codeBlock = copyButton
1685 .closest(".mxchat-code-block-container")
1686 .querySelector(".mxchat-code-block code");
1687
1688 if (codeBlock) {
1689 // Preserve formatting using innerText
1690 navigator.clipboard.writeText(codeBlock.innerText).then(() => {
1691 copyButton.textContent = "Copied!";
1692 copyButton.setAttribute("aria-label", "Copied to clipboard");
1693
1694 setTimeout(() => {
1695 copyButton.textContent = "Copy";
1696 copyButton.setAttribute("aria-label", "Copy to clipboard");
1697 }, 2000);
1698 });
1699 }
1700 }
1701 });
1702
1703
1704
1705