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

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