PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 1.5.4
MxChat – AI Chatbot & Content Generation for WordPress v1.5.4
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
← All changes | js/chat-script.js +175 -632 2.0.61.5.4 View file →
@@ -1,8 +1,8 @@
1 1 jQuery(document).ready(function($) {
2 2 //console.log('mxchatChat object:', mxchatChat);
3 3 //console.log('Link Target Toggle Value:', mxchatChat.link_target_toggle);
4 -// Add these variables at the top of your chat-script.js file
4 +
5 5 const toolbarIconColor = mxchatChat.toolbar_icon_color || '#212121';
6 6
7 7 // Initialize color settings
8 8 var userMessageBgColor = mxchatChat.user_message_bg_color;
@@ -14,11 +14,9 @@
14 14 var liveAgentMessageFontColor = mxchatChat.live_agent_message_font_color;
15 15
16 16
17 17 var linkTarget = mxchatChat.link_target_toggle === 'on' ? '_blank' : '_self';
18 - let lastSeenMessageId = '';
19 - let notificationCheckInterval;
20 - let notificationBadge;
18 + var lastSeenMessageId = '';
21 19 // Initialize session ID
22 20 var sessionId = getChatSession();
23 21
24 22 let pollingInterval; // Variable to store the interval ID
@@ -25,110 +23,12 @@
25 23 let processedMessageIds = new Set(); // Add this at the top with your other variables
26 24 //console.log('Live Agent BG Color:', liveAgentMessageBgColor);
27 25 //console.log('Live Agent Font Color:', liveAgentMessageFontColor);
28 26 let activePdfFile = null;
29 - let activeWordFile = null;
30 27
31 28
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 29
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 30
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 31 function getChatSession() {
132 32 var sessionId = getCookie('mxchat_session_id');
133 33 //console.log("Session ID retrieved from cookie: ", sessionId);
134 34
@@ -256,48 +156,24 @@
256 156 sendMessageToChatbot(question);
257 157 });
258 158
259 159
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 both URLs and markdown
270 -// Modified linkify function that only processes URLs in user content
160 +// Use the linkTarget in your linkify function
271 161 function linkify(inputText) {
272 - if (!inputText) return '';
273 -
274 - // Process markdown headers
275 - let processedText = formatMarkdownHeaders(inputText);
276 -
277 - // Process markdown links
278 - const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
279 - processedText = processedText.replace(markdownLinkPattern, (match, text, url) => {
280 - const safeUrl = encodeURI(url);
281 - const safeText = sanitizeUserInput(text);
282 - return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`;
283 - });
162 + // Check for already linked URLs and skip them
163 + // We use negative lookaheads to skip anything already in an <a> tag
164 + var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
165 + var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
284 166
285 - // Process standalone URLs
286 - const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
287 - processedText = processedText.replace(urlPattern, (match, prefix, url) => {
288 - const safeUrl = encodeURI(url);
289 - return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
290 - });
167 + // Replace standalone URLs not already in an <a> tag
168 + var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
169 + replacedText = replacedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
291 170
292 - // Process www. URLs
293 - const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
294 - processedText = processedText.replace(wwwPattern, (match, prefix, url) => {
295 - const safeUrl = encodeURI(`http://${url}`);
296 - return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
297 - });
171 + // Replace "www." prefixed URLs not already in an <a> tag
172 + var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
173 + replacedText = replacedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
298 174
299 - return processedText;
175 + return replacedText;
300 176 }
301 177
302 178
303 179 function scrollElementToTop(element) {
@@ -344,17 +220,19 @@
344 220 return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
345 221 }
346 222
347 223 // Function to convert newline characters to HTML line breaks and handle paragraph spacing
348 -function convertNewlinesToBreaks(text) {
349 - // Split the text into paragraphs (marked by double newlines or multiple <br> tags)
350 - const paragraphs = text.split(/(?:\n\n|\<br\>\s*\<br\>)/g);
351 -
352 - // Wrap each paragraph in <p> tags
353 - return paragraphs
354 - .map(para => `<p>${para.trim()}</p>`)
355 - .join('');
356 -}
224 + function convertNewlinesToBreaks(text) {
225 + var lines = text.split('\n');
226 + var formattedText = '';
227 +
228 + for (var i = 0; i < lines.length; i++) {
229 + formattedText += lines[i] + '<br>';
230 + }
231 +
232 + return formattedText;
233 + }
234 +
357 235 // Copy to clipboard function
358 236 // Function to copy text to clipboard
359 237 function copyToClipboard(text) {
360 238 var tempInput = $('<input>');
@@ -379,8 +257,10 @@
379 257 }
380 258 }
381 259
382 260 function callMxChat(message, callback) {
261 + //console.log("Sending message to chatbot:", message);
262 +
383 263 $.ajax({
384 264 url: mxchatChat.ajax_url,
385 265 type: 'POST',
386 266 dataType: 'json',
@@ -390,38 +270,37 @@
390 270 session_id: getChatSession(),
391 271 nonce: mxchatChat.nonce
392 272 },
393 273 success: function(response) {
394 - // Existing chat mode check
274 + //console.log("callMxChat response:", response);
275 +
276 + // Check for chat_mode in the response
395 277 if (response.chat_mode) {
396 278 updateChatModeIndicator(response.chat_mode);
397 279 }
280 + // Also check in fallbackResponse if exists
398 281 else if (response.fallbackResponse && response.fallbackResponse.chat_mode) {
399 282 updateChatModeIndicator(response.fallbackResponse.chat_mode);
400 283 }
401 -
402 - // Add PDF filename handling
403 - if (response.data && response.data.filename) {
404 - showActivePdf(response.data.filename);
405 - activePdfFile = response.data.filename;
406 - }
407 284
408 - // Add redirect check here
285 + // Add redirect check here
409 286 if (response.redirect_url) {
287 + // Show the message first
410 288 let responseText = response.text || '';
411 289 if (responseText) {
412 290 replaceLastMessage("bot", responseText);
413 291 }
292 + // Then redirect after a short delay
414 293 setTimeout(() => {
415 294 window.location.href = response.redirect_url;
416 295 }, 1500);
417 - return;
296 + return; // Exit early since we're redirecting
418 297 }
419 298
420 -
421 299 // Check for live agent response
422 300 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
423 301 updateChatModeIndicator('agent');
302 + // Do not replace the thinking dots; just wait for the agent's actual response
424 303 return;
425 304 }
426 305
427 306 // Handle other responses
@@ -428,33 +307,31 @@
428 307 let responseText = response.text || '';
429 308 let responseHtml = response.html || '';
430 309 let responseMessage = response.message || '';
431 310
311 + // Check for mode change in text response
432 312 if (responseText === 'You are now chatting with the AI chatbot.') {
433 313 updateChatModeIndicator('ai');
434 314 }
435 315
436 - // Handle the message and show notification if chat is hidden
437 - if (responseText || responseHtml || responseMessage) {
438 - // Update the messages as before
439 - if (responseText && responseHtml) {
440 - replaceLastMessage("bot", responseText, responseHtml);
441 - } else if (responseText) {
442 - replaceLastMessage("bot", responseText);
443 - } else if (responseHtml) {
444 - replaceLastMessage("bot", "", responseHtml);
445 - } else if (responseMessage) {
446 - replaceLastMessage("bot", responseMessage);
447 - }
448 -
449 - // Check if chat is hidden and show notification
450 - if ($('#floating-chatbot').hasClass('hidden')) {
451 - const badge = $('#chat-notification-badge');
452 - if (badge.length) {
453 - badge.show();
454 - }
455 - }
456 - } else {
316 + // For product card or chatbot responses with HTML
317 + if (responseText && responseHtml) {
318 + replaceLastMessage("bot", responseText, responseHtml);
319 + }
320 + // For regular chatbot responses with just text
321 + else if (responseText) {
322 + replaceLastMessage("bot", responseText);
323 + }
324 + // For responses with only HTML (like product cards)
325 + else if (responseHtml) {
326 + replaceLastMessage("bot", "", responseHtml);
327 + }
328 + // For legacy message format
329 + else if (responseMessage) {
330 + replaceLastMessage("bot", responseMessage);
331 + }
332 + // Fallback error case
333 + else {
457 334 console.error("Unexpected response format:", response);
458 335 replaceLastMessage("bot", "I'm sorry, something went wrong.");
459 336 }
460 337
@@ -462,22 +339,17 @@
462 339 lastSeenMessageId = response.message_id;
463 340 }
464 341 },
465 342 error: function(xhr, status, error) {
343 + //console.log("Error communicating with the server:", xhr.status, error);
466 344 replaceLastMessage("bot", "An unexpected error occurred.");
467 345 }
468 346 });
469 347 }
470 348
471 -// Sanitize only user input
472 -function sanitizeUserInput(text) {
473 - const div = document.createElement('div');
474 - div.textContent = text;
475 - return div.innerHTML;
476 -}
349 +function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
350 + //console.log("Appending message. Sender:", sender, "Content:", messageText);
477 351
478 -// Modified appendMessage function that only sanitizes user content
479 -function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
480 352 try {
481 353 // Determine styles based on sender type
482 354 let messageClass, bgColor, fontColor;
483 355
@@ -484,15 +356,13 @@
484 356 if (sender === "user") {
485 357 messageClass = "user-message";
486 358 bgColor = userMessageBgColor;
487 359 fontColor = userMessageFontColor;
488 - // Only sanitize user input
489 - messageText = sanitizeUserInput(messageText);
490 - } else if (sender === "agent") {
491 - messageClass = "agent-message";
492 - bgColor = liveAgentMessageBgColor;
493 - fontColor = liveAgentMessageFontColor;
494 - } else {
360 + } else if (sender === "agent") {
361 + messageClass = "agent-message";
362 + bgColor = liveAgentMessageBgColor;
363 + fontColor = liveAgentMessageFontColor;
364 + } else {
495 365 messageClass = "bot-message";
496 366 bgColor = botMessageBgColor;
497 367 fontColor = botMessageFontColor;
498 368 }
@@ -501,35 +371,22 @@
501 371 .addClass(messageClass)
502 372 .css({
503 373 'background': bgColor,
504 374 'color': fontColor,
505 - 'margin-bottom': '1em'
506 375 });
507 376
508 - // Process the message content based on sender
509 - let fullMessage;
510 - if (sender === "user") {
511 - // For user messages, apply linkify after sanitization
512 - fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
513 - } else {
514 - // For bot/agent messages, preserve HTML
515 - fullMessage = messageText;
516 - }
377 + // Format and process the message content
378 + let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
517 379
518 380 // Add images if provided
519 381 if (images && images.length > 0) {
520 382 fullMessage += '<div class="image-gallery">';
521 383 images.forEach(img => {
522 - // Ensure image URLs and titles are properly escaped
523 - const safeTitle = sanitizeUserInput(img.title);
524 - const safeUrl = encodeURI(img.image_url);
525 - const safeThumbnail = encodeURI(img.thumbnail_url);
526 -
527 384 fullMessage += `
528 385 <div style="margin-bottom: 10px;">
529 - <strong>${safeTitle}</strong><br>
530 - <a href="${safeUrl}" target="_blank">
531 - <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" />
386 + <strong>${img.title}</strong><br>
387 + <a href="${img.image_url}" target="_blank">
388 + <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
532 389 </a>
533 390 </div>`;
534 391 });
535 392 fullMessage += '</div>';
@@ -535,20 +392,23 @@
535 392 fullMessage += '</div>';
536 393 }
537 394
538 395 // Append HTML content if provided
539 - if (messageHtml && sender !== "user") {
396 + if (messageHtml) {
540 397 fullMessage += '<br><br>' + messageHtml;
541 398 }
542 399
543 400 messageDiv.html(fullMessage);
544 401
402 + // Add a class for temporary messages if needed
545 403 if (isTemporary) {
546 404 messageDiv.addClass('temporary-message');
547 405 }
548 406
549 - messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
407 + // Append the message to the chat box
408 + messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
550 409 if (sender === "bot") {
410 + // After bot's message is displayed, scroll the last user message to the top
551 411 const lastUserMessage = $('#chat-box').find('.user-message').last();
552 412 if (lastUserMessage.length) {
553 413 scrollElementToTop(lastUserMessage);
554 414 }
@@ -554,14 +414,14 @@
554 414 }
555 415 }
556 416 });
557 417
418 + // Update the last seen message ID if applicable
558 419 if (messageText.id) {
559 420 lastSeenMessageId = messageText.id;
560 - hideNotification();
561 421 }
562 422 } catch (error) {
563 - console.error("Error rendering message:", error);
423 + console.error("Error rendering message with images:", error);
564 424 }
565 425 }
566 426
567 427
@@ -611,20 +471,9 @@
611 471 'background-color': bgColor,
612 472 'color': fontColor,
613 473 })
614 474 .removeClass('temporary-message')
615 - .fadeIn(200, function() {
616 - if (sender === "bot" || sender === "agent") {
617 - const lastUserMessage = $('#chat-box').find('.user-message').last();
618 - if (lastUserMessage.length) {
619 - scrollElementToTop(lastUserMessage);
620 - }
621 - // Show notification if chat is hidden
622 - if ($('#floating-chatbot').hasClass('hidden')) {
623 - showNotification();
624 - }
625 - }
626 - });
475 + .fadeIn(200);
627 476 });
628 477 } else {
629 478 appendMessage(sender, responseText, responseHtml, images);
630 479 }
@@ -630,8 +479,9 @@
630 479 }
631 480 }
632 481
633 482
483 +
634 484 function startPolling() {
635 485 // Clear any existing interval first
636 486 stopPolling();
637 487 // Start new polling interval
@@ -647,11 +497,11 @@
647 497 }
648 498 }
649 499
650 500
651 -// Update your checkForAgentMessages function
652 501 function checkForAgentMessages() {
653 502 const sessionId = getChatSession();
503 +
654 504 $.ajax({
655 505 url: mxchatChat.ajax_url,
656 506 type: 'POST',
657 507 dataType: 'json',
@@ -661,24 +511,17 @@
661 511 last_seen_id: lastSeenMessageId,
662 512 nonce: mxchatChat.nonce
663 513 },
664 514 success: function (response) {
515 + //console.log("Agent messages polling response:", response);
665 516 if (response.success && response.data?.new_messages) {
666 - let hasNewMessage = false;
667 -
668 517 response.data.new_messages.forEach(function (message) {
669 518 if (message.role === "agent" && !processedMessageIds.has(message.id)) {
670 - hasNewMessage = true;
671 519 replaceLastMessage("agent", message.content);
672 520 lastSeenMessageId = message.id;
673 521 processedMessageIds.add(message.id);
674 522 }
675 523 });
676 -
677 - if (hasNewMessage && $('#floating-chatbot').hasClass('hidden')) {
678 - showNotification();
679 - }
680 -
681 524 scrollToBottom(true);
682 525 }
683 526 },
684 527 error: function (xhr, status, error) {
@@ -805,37 +648,29 @@
805 648 return textArea.value;
806 649 }
807 650
808 651
809 -// Update formatCodeBlocks function
810 652 function formatCodeBlocks(text) {
811 - // First handle raw PHP tags
812 - text = text.replace(/(<\?php[\s\S]*?\?>)/g, (match) => {
813 - return `<pre><code class="language-php">${escapeHtml(match)}</code></pre>`;
814 - });
653 + // Ensure the input is a string; otherwise, convert or return empty
654 + if (typeof text !== 'string') {
655 + console.error("formatCodeBlocks: Input is not a string:", text);
656 + return typeof text === 'object' && text.text ? text.text : ""; // Use .text if available, else empty
657 + }
815 658
816 - // Then handle code blocks with backticks
817 - text = text.replace(/```php5?\n([\s\S]+?)```/gi, (match, code) => {
818 - return `<pre><code class="language-php">${escapeHtml(code)}</code></pre>`;
659 + const codeBlockPattern = /```(\w+)?\n?([\s\S]+?)```/g;
660 +
661 + return text.replace(codeBlockPattern, (_, language, codeContent) => {
662 + language = language || 'plaintext';
663 +
664 + return `
665 + <div class="mxchat-code-block-container">
666 + <button class="mxchat-copy-button" aria-label="Copy to clipboard">Copy</button>
667 + <pre class="mxchat-code-block"><code class="mxchat-language-${language}">${escapeHtml(codeContent)}</code></pre>
668 + </div>`;
819 669 });
670 +}
820 671
821 - return text;
822 -}
823 672
824 -// Update escapeHtml function to preserve existing code blocks
825 -function escapeHtml(unsafe) {
826 - // First check if it's already a code block
827 - if (unsafe.includes('<pre><code') || unsafe.includes('</code></pre>')) {
828 - return unsafe;
829 - }
830 -
831 - return unsafe
832 - .replace(/&/g, "&amp;")
833 - .replace(/</g, "&lt;")
834 - .replace(/>/g, "&gt;")
835 - .replace(/"/g, "&quot;")
836 - .replace(/'/g, "&#039;");
837 -}
838 673 // Utility function to escape HTML
839 674 function escapeHtml(unsafe) {
840 675 return unsafe
841 676 .replace(/&/g, "&amp;")
@@ -849,18 +684,20 @@
849 684
850 685
851 686 // Function to convert newlines, skipping preformatted text
852 687 function convertNewlinesToBreaks(text) {
853 - // Split while preserving code blocks
854 - return text.split(/(<pre\b[^>]*>[\s\S]*?<\/pre>)/g).map(part => {
855 - if (part.startsWith('<pre')) return part;
856 - return part.replace(/(^|[^>])\n/g, '$1<br>');
857 - }).join('');
688 + // Regex to exclude <pre> and <code> tags from adding <br> tags
689 + return text.replace(/(^|[^>])\n/g, '$1<br>');
858 690 }
859 691
860 692
861 693
694 +$(document).ready(function() {
695 + loadChatHistory();
696 +});
862 697
698 +
699 +
863 700 // Helper function to check if a string is an image HTML
864 701 function isImageHtml(str) {
865 702 return str.startsWith('<img') && str.endsWith('>');
866 703 }
@@ -886,8 +723,20 @@
886 723 $('body').css('overflow', '');
887 724 }
888 725 }
889 726
727 + // Function to show the chatbot widget (moved outside the Complianz logic)
728 + function showChatWidget() {
729 + setTimeout(function() {
730 + $('#floating-chatbot-button').css('display', 'flex').fadeTo(500, 1);
731 + }, 250);
732 + }
733 +
734 + // Function to hide the chatbot widget
735 + function hideChatWidget() {
736 + $('#floating-chatbot-button').css('display', 'none');
737 + }
738 +
890 739 // Pre-chat dismissal check function (wrapped in a function for reuse)
891 740 function checkPreChatDismissal() {
892 741 $.ajax({
893 742 url: mxchatChat.ajax_url,
@@ -907,93 +756,9 @@
907 756 console.error('Failed to check pre-chat message dismissal status.');
908 757 }
909 758 });
910 759 }
911 -
912 - // Function to show the chatbot widget
913 -function showChatWidget() {
914 - // First ensure display is set
915 - $('#floating-chatbot-button').css('display', 'flex');
916 - // Then handle the fade
917 - $('#floating-chatbot-button').fadeTo(500, 1);
918 - // Force visibility
919 - $('#floating-chatbot-button').removeClass('hidden');
920 - //console.log('Showing widget');
921 -}
922 760
923 -// Function to hide the chatbot widget
924 -function hideChatWidget() {
925 - $('#floating-chatbot-button').css('display', 'none');
926 - $('#floating-chatbot-button').addClass('hidden');
927 - //console.log('Hiding widget');
928 -}
929 -
930 -function initializeChatVisibility() {
931 - //console.log('Initializing chat visibility');
932 - const complianzEnabled = mxchatChat.complianz_toggle === 'on' ||
933 - mxchatChat.complianz_toggle === '1' ||
934 - mxchatChat.complianz_toggle === 1;
935 -
936 - if (complianzEnabled && typeof cmplz_has_consent === "function" && typeof complianz !== 'undefined') {
937 - // Initial check
938 - checkConsentAndShowChat();
939 -
940 - // Listen for consent changes
941 - $(document).on('cmplz_status_change', function(event) {
942 - //console.log('Status change detected');
943 - checkConsentAndShowChat();
944 - });
945 - } else {
946 - // If Complianz is not enabled, always show
947 - $('#floating-chatbot-button')
948 - .css('display', 'flex')
949 - .removeClass('hidden no-consent')
950 - .fadeTo(500, 1);
951 -
952 - // Also check pre-chat message when Complianz is not enabled
953 - checkPreChatDismissal();
954 - }
955 -}
956 -
957 -
958 -
959 -function checkConsentAndShowChat() {
960 - var consentStatus = cmplz_has_consent('marketing');
961 - var consentType = complianz.consenttype;
962 -
963 - //console.log('Checking consent:', {status: consentStatus,type: consentType});
964 -
965 - let $widget = $('#floating-chatbot-button');
966 - let $chatbot = $('#floating-chatbot');
967 - let $preChat = $('#pre-chat-message');
968 -
969 - if (consentStatus === true) {
970 - //console.log('Consent granted - showing widget');
971 - $widget
972 - .removeClass('no-consent')
973 - .css('display', 'flex')
974 - .removeClass('hidden')
975 - .fadeTo(500, 1);
976 - $chatbot.removeClass('no-consent');
977 -
978 - // Show pre-chat message if not dismissed
979 - checkPreChatDismissal();
980 - } else {
981 - //console.log('No consent - hiding widget');
982 - $widget
983 - .addClass('no-consent')
984 - .fadeTo(500, 0, function() {
985 - $(this)
986 - .css('display', 'none')
987 - .addClass('hidden');
988 - });
989 - $chatbot.addClass('no-consent');
990 -
991 - // Hide pre-chat message when no consent
992 - $preChat.hide();
993 - }
994 -}
995 -
996 761 // Function to dismiss pre-chat message for 24 hours
997 762 function handlePreChatDismissal() {
998 763 $('#pre-chat-message').fadeOut(200);
999 764 $.ajax({
@@ -1017,8 +782,47 @@
1017 782 e.stopPropagation();
1018 783 handlePreChatDismissal();
1019 784 });
1020 785
786 + // Function for Complianz logic
787 + var applyComplianzLogic = mxchatChat.complianz_toggle;
788 + if (applyComplianzLogic) {
789 + function checkConsentAndShowChat() {
790 + var consentStatus = typeof cmplz_has_consent === "function" && cmplz_has_consent('marketing');
791 + var consentType = typeof complianz !== 'undefined' ? complianz.consenttype : null;
792 +
793 + // Show the chatbot by default
794 + showChatWidget();
795 +
796 + if (consentType === 'optin' && !consentStatus) {
797 + // For opt-in, hide only if user explicitly denies consent
798 + hideChatWidget();
799 + } else if (consentType === 'optout' && consentStatus === false) {
800 + // For opt-out, hide only if user explicitly denies consent
801 + hideChatWidget();
802 + } else {
803 + // Keep showing the chatbot
804 + showChatWidget();
805 + }
806 +
807 + checkPreChatDismissal(); // Ensure we check dismissal after consent is handled
808 + }
809 +
810 + // Initial check when the page loads
811 + checkConsentAndShowChat();
812 +
813 + // Listen for changes in consent status
814 + $(document).on('cmplz_status_change', function(event, category) {
815 + if (category === 'marketing') {
816 + checkConsentAndShowChat();
817 + }
818 + });
819 + } else {
820 + // If Complianz is not toggled on, always show the chatbot
821 + showChatWidget();
822 + checkPreChatDismissal(); // Always check pre-chat dismissal when consent logic is not applied
823 + }
824 +
1021 825 // Toggle chatbot visibility on floating button click
1022 826 $(document).on('click', '#floating-chatbot-button', function() {
1023 827 var chatbot = $('#floating-chatbot');
1024 828 if (chatbot.hasClass('hidden')) {
@@ -1023,15 +827,16 @@
1023 827 var chatbot = $('#floating-chatbot');
1024 828 if (chatbot.hasClass('hidden')) {
1025 829 chatbot.removeClass('hidden').addClass('visible');
1026 830 $(this).addClass('hidden');
1027 - $('#chat-notification-badge').hide(); // Hide notification when opening chat
1028 831 disableScroll();
832 + // Hide the pre-chat message without dismissing it
1029 833 $('#pre-chat-message').fadeOut(250);
1030 834 } else {
1031 835 chatbot.removeClass('visible').addClass('hidden');
1032 836 $(this).removeClass('hidden');
1033 837 enableScroll();
838 + // Show the pre-chat message again if it hasn't been dismissed
1034 839 checkPreChatDismissal();
1035 840 }
1036 841 });
1037 842
@@ -1070,10 +875,12 @@
1070 875 $(':root').css('--vh', vh + 'px');
1071 876 }
1072 877
1073 878 // Set the height when the page loads
879 + $(document).ready(function() {
880 + setFullHeight();
881 + });
1074 882
1075 -
1076 883 // Set the height on resize and orientation change events
1077 884 $(window).on('resize orientationchange', function() {
1078 885 setFullHeight();
1079 886 });
@@ -1110,27 +917,24 @@
1110 917
1111 918
1112 919 // Event listener for Add to Cart button
1113 920 $(document).on('click', '.mxchat-add-to-cart-button', function() {
1114 - var productId = $(this).data('product-id');
1115 - // Add a special prefix to indicate this is from button
1116 - appendMessage("user", "add to cart");
1117 - sendMessageToChatbot("!addtocart"); // Special command to indicate button click
921 + var productId = $(this).data('product-id'); // Get product ID from data attribute
922 +
923 + // Simulate user message first for proper ordering
924 + appendMessage("user", "add to cart"); // Display the user's "add to cart" message first
925 +
926 +
927 + // Use existing function to send the "add to cart" command to the chatbot
928 + sendMessageToChatbot("add to cart"); // Triggers the chatbot response as though user typed it
1118 929 });
1119 930
1120 931
1121 -if (document.getElementById('pdf-upload-btn')) {
1122 - document.getElementById('pdf-upload-btn').addEventListener('click', function() {
1123 - document.getElementById('pdf-upload').click();
1124 - });
1125 -}
932 +// PDF Upload button click handler
933 +document.getElementById('pdf-upload-btn').addEventListener('click', function() {
934 + document.getElementById('pdf-upload').click();
935 +});
1126 936
1127 -if (document.getElementById('word-upload-btn')) {
1128 - document.getElementById('word-upload-btn').addEventListener('click', function() {
1129 - document.getElementById('word-upload').click();
1130 - });
1131 -}
1132 -
1133 937 // PDF file input change handler
1134 938 document.getElementById('pdf-upload').addEventListener('change', async function(e) {
1135 939 const file = e.target.files[0];
1136 940
@@ -1203,76 +1007,8 @@
1203 1007 this.value = ''; // Reset file input
1204 1008 }
1205 1009 });
1206 1010
1207 -// Word file input change handler
1208 -document.getElementById('word-upload').addEventListener('change', async function(e) {
1209 - const file = e.target.files[0];
1210 -
1211 - if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1212 - alert('Please select a valid Word document (.docx).');
1213 - return;
1214 - }
1215 -
1216 - if (!sessionId) {
1217 - console.error('No session ID found');
1218 - alert('Error: No session ID found');
1219 - return;
1220 - }
1221 -
1222 - // Disable buttons and show loading state
1223 - const uploadBtn = document.getElementById('word-upload-btn');
1224 - const sendBtn = document.getElementById('send-button');
1225 - const originalBtnContent = uploadBtn.innerHTML;
1226 -
1227 - try {
1228 - const formData = new FormData();
1229 - formData.append('action', 'mxchat_upload_word');
1230 - formData.append('word_file', file);
1231 - formData.append('session_id', sessionId);
1232 - formData.append('nonce', mxchatChat.nonce);
1233 -
1234 - uploadBtn.disabled = true;
1235 - sendBtn.disabled = true;
1236 - uploadBtn.innerHTML = `<svg class="spinner" viewBox="0 0 50 50">
1237 - <circle cx="25" cy="25" r="20" fill="none" stroke-width="5"></circle>
1238 - </svg>`;
1239 -
1240 - const response = await fetch(mxchatChat.ajax_url, {
1241 - method: 'POST',
1242 - body: formData
1243 - });
1244 -
1245 - const data = await response.json();
1246 -
1247 - if (data.success) {
1248 - // Hide popular questions if they exist
1249 - const popularQuestionsContainer = document.getElementById('mxchat-popular-questions');
1250 - if (popularQuestionsContainer) {
1251 - popularQuestionsContainer.style.display = 'none';
1252 - }
1253 -
1254 - // Show the active Word document name
1255 - showActiveWord(data.data.filename);
1256 -
1257 - appendMessage('bot', data.data.message);
1258 - scrollToBottom();
1259 - activeWordFile = data.data.filename;
1260 - } else {
1261 - console.error('Upload failed:', data.data);
1262 - alert('Failed to upload Word document. Please try again.');
1263 - }
1264 - } catch (error) {
1265 - console.error('Upload error:', error);
1266 - alert('Error uploading file. Please try again.');
1267 - } finally {
1268 - uploadBtn.disabled = false;
1269 - sendBtn.disabled = false;
1270 - uploadBtn.innerHTML = originalBtnContent;
1271 - this.value = ''; // Reset file input
1272 - }
1273 -});
1274 -
1275 1011 // Function to show active PDF name in toolbar
1276 1012 function showActivePdf(filename) {
1277 1013 const container = document.getElementById('active-pdf-container');
1278 1014 const nameElement = document.getElementById('active-pdf-name');
@@ -1285,22 +1021,8 @@
1285 1021 nameElement.textContent = filename;
1286 1022 container.style.display = 'flex';
1287 1023 }
1288 1024
1289 -// Function to show active Word document name in toolbar
1290 -function showActiveWord(filename) {
1291 - const container = document.getElementById('active-word-container');
1292 - const nameElement = document.getElementById('active-word-name');
1293 -
1294 - if (!container || !nameElement) {
1295 - console.error('Word document container elements not found');
1296 - return;
1297 - }
1298 -
1299 - nameElement.textContent = filename;
1300 - container.style.display = 'flex';
1301 -}
1302 -
1303 1025 // Function to remove active PDF
1304 1026 function removeActivePdf() {
1305 1027 const container = document.getElementById('active-pdf-container');
1306 1028 const nameElement = document.getElementById('active-pdf-name');
@@ -1331,41 +1053,9 @@
1331 1053 console.error('Error removing PDF:', error);
1332 1054 });
1333 1055 }
1334 1056
1335 -// Function to remove active Word document
1336 -function removeActiveWord() {
1337 - const container = document.getElementById('active-word-container');
1338 - const nameElement = document.getElementById('active-word-name');
1339 -
1340 - if (!container || !nameElement || !activeWordFile) return;
1341 -
1342 - fetch(mxchatChat.ajax_url, {
1343 - method: 'POST',
1344 - headers: {
1345 - 'Content-Type': 'application/x-www-form-urlencoded',
1346 - },
1347 - body: new URLSearchParams({
1348 - 'action': 'mxchat_remove_word',
1349 - 'session_id': sessionId,
1350 - 'nonce': mxchatChat.nonce
1351 - })
1352 - })
1353 - .then(response => response.json())
1354 - .then(data => {
1355 - if (data.success) {
1356 - container.style.display = 'none';
1357 - nameElement.textContent = '';
1358 - activeWordFile = null;
1359 - appendMessage('bot', 'Word document removed.');
1360 - }
1361 - })
1362 - .catch(error => {
1363 - console.error('Error removing Word document:', error);
1364 - });
1365 -}
1366 -
1367 -// Add remove button click handlers
1057 +// Add remove button click handler
1368 1058 document.getElementById('remove-pdf-btn')?.addEventListener('click', function(e) {
1369 1059 e.preventDefault();
1370 1060 e.stopPropagation();
1371 1061 removeActivePdf();
@@ -1370,19 +1060,12 @@
1370 1060 e.stopPropagation();
1371 1061 removeActivePdf();
1372 1062 });
1373 1063
1374 -document.getElementById('remove-word-btn')?.addEventListener('click', function(e) {
1375 - e.preventDefault();
1376 - e.stopPropagation();
1377 - removeActiveWord();
1378 -});
1379 -
1380 -// Check initial document status
1381 -function checkInitialDocumentStatus() {
1064 +// Check initial PDF status on load
1065 +function checkInitialPdfStatus() {
1382 1066 if (!sessionId) return;
1383 1067
1384 - // Check PDF status
1385 1068 fetch(mxchatChat.ajax_url, {
1386 1069 method: 'POST',
1387 1070 headers: {
1388 1071 'Content-Type': 'application/x-www-form-urlencoded',
@@ -1402,31 +1085,8 @@
1402 1085 })
1403 1086 .catch(error => {
1404 1087 console.error('Error checking PDF status:', error);
1405 1088 });
1406 -
1407 - // Check Word document status
1408 - fetch(mxchatChat.ajax_url, {
1409 - method: 'POST',
1410 - headers: {
1411 - 'Content-Type': 'application/x-www-form-urlencoded',
1412 - },
1413 - body: new URLSearchParams({
1414 - 'action': 'mxchat_check_word_status',
1415 - 'session_id': sessionId,
1416 - 'nonce': mxchatChat.nonce
1417 - })
1418 - })
1419 - .then(response => response.json())
1420 - .then(data => {
1421 - if (data.success && data.data.filename) {
1422 - showActiveWord(data.data.filename);
1423 - activeWordFile = data.data.filename;
1424 - }
1425 - })
1426 - .catch(error => {
1427 - console.error('Error checking Word document status:', error);
1428 - });
1429 1089 }
1430 1090
1431 1091 // Apply toolbar settings
1432 1092 if (mxchatChat.chat_toolbar_toggle === 'on') {
@@ -1436,9 +1096,9 @@
1436 1096 }
1437 1097
1438 1098 // Initialize on page load
1439 1099 document.addEventListener('DOMContentLoaded', function() {
1440 - checkInitialDocumentStatus();
1100 + checkInitialPdfStatus();
1441 1101 });
1442 1102
1443 1103 // Style all toolbar elements
1444 1104 const toolbarElements = [
@@ -1443,11 +1103,9 @@
1443 1103 // Style all toolbar elements
1444 1104 const toolbarElements = [
1445 1105 '#mxchat-chatbot .toolbar-btn svg',
1446 1106 '#mxchat-chatbot .active-pdf-name',
1447 - '#mxchat-chatbot .active-word-name',
1448 - '#mxchat-chatbot .remove-pdf-btn svg',
1449 - '#mxchat-chatbot .remove-word-btn svg'
1107 + '#mxchat-chatbot .remove-pdf-btn svg'
1450 1108 ];
1451 1109 $(toolbarElements.join(', ')).css({
1452 1110 'fill': toolbarIconColor,
1453 1111 'color': toolbarIconColor
@@ -1454,122 +1112,10 @@
1454 1112 });
1455 1113
1456 1114
1457 1115
1458 -// Ensure essential elements are defined
1459 -const emailForm = document.getElementById('email-collection-form');
1460 -const emailBlocker = document.getElementById('email-blocker');
1461 -const chatbotWrapper = document.getElementById('chat-container');
1462 -
1463 -if (emailForm && emailBlocker && chatbotWrapper) {
1464 - // Check if email exists for the current session
1465 - function checkSessionAndEmail() {
1466 - const sessionId = getChatSession();
1467 - //console.log("[DEBUG JS] checkSessionAndEmail -> sessionId:", sessionId);
1468 -
1469 - fetch(mxchatChat.ajax_url, {
1470 - method: 'POST',
1471 - headers: {
1472 - 'Content-Type': 'application/x-www-form-urlencoded',
1473 - },
1474 - body: new URLSearchParams({
1475 - action: 'mxchat_check_email_provided',
1476 - session_id: sessionId,
1477 - nonce: mxchatChat.nonce,
1478 - }),
1479 - })
1480 - .then((response) => response.json())
1481 - .then((data) => {
1482 - //console.log("[DEBUG JS] mxchat_check_email_provided response:", data);
1483 -
1484 - if (data.success) {
1485 - if (data.data.logged_in) {
1486 - //console.log("[DEBUG JS] User is logged in. Hiding email form.");
1487 - emailBlocker.style.display = 'none';
1488 - chatbotWrapper.style.display = 'flex';
1489 - } else if (data.data.email) {
1490 - //console.log("[DEBUG JS] Email found for session. Hiding email form.");
1491 - emailBlocker.style.display = 'none';
1492 - chatbotWrapper.style.display = 'flex';
1493 - } else {
1494 - //console.log("[DEBUG JS] No email provided. Showing email form.");
1495 - emailBlocker.style.display = 'flex';
1496 - chatbotWrapper.style.display = 'none';
1497 - }
1498 - } else {
1499 - //console.log("[DEBUG JS] Error or no data received. Showing email form.");
1500 - emailBlocker.style.display = 'flex';
1501 - chatbotWrapper.style.display = 'none';
1502 - }
1503 - })
1504 - .catch((error) => {
1505 - // console.error("[DEBUG JS] Fetch error -> forcing email form visible:", error);
1506 - emailBlocker.style.display = 'flex';
1507 - chatbotWrapper.style.display = 'none';
1508 - });
1509 -}
1510 -
1511 -
1512 -
1513 - // Handle email form submission
1514 - emailForm.addEventListener('submit', function (event) {
1515 - event.preventDefault();
1516 - const userEmail = document.getElementById('user-email').value;
1517 - const sessionId = getChatSession();
1518 -
1519 - if (userEmail) {
1520 - fetch(mxchatChat.ajax_url, {
1521 - method: 'POST',
1522 - headers: {
1523 - 'Content-Type': 'application/x-www-form-urlencoded',
1524 - },
1525 - body: new URLSearchParams({
1526 - action: 'mxchat_handle_save_email_and_response',
1527 - email: userEmail,
1528 - session_id: sessionId,
1529 - nonce: mxchatChat.nonce,
1530 - }),
1531 - })
1532 - .then((response) => response.json())
1533 - .then((data) => {
1534 - //console.log('Backend response:', data);
1535 - if (data.success) {
1536 - //console.log('Email saved successfully:', userEmail);
1537 - emailBlocker.style.display = 'none';
1538 - chatbotWrapper.style.display = 'flex';
1539 -
1540 - // Optionally handle bot response
1541 - if (data.message) {
1542 - appendMessage('bot', data.message);
1543 - scrollToBottom();
1544 - }
1545 - } else {
1546 - console.error('Error saving email:', data.message || 'Unknown error');
1547 - }
1548 - })
1549 - .catch((error) => {
1550 - console.error('AJAX error:', error);
1551 - });
1552 - }
1553 - });
1554 -
1555 - // Check session and email status on page load
1556 - checkSessionAndEmail();
1557 -} else {
1558 - console.error('Essential elements for email handling are missing.');
1559 -}
1560 -
1561 -
1562 -// Initialize when document is ready
1563 -$(document).ready(function() {
1564 - setFullHeight();
1565 - initializeChatVisibility();
1566 - loadChatHistory();
1567 -
1568 1116 });
1569 1117
1570 -});
1571 -
1572 1118 // Event listener for copy button
1573 1119 document.addEventListener("click", (e) => {
1574 1120 if (e.target.classList.contains("mxchat-copy-button")) {
1575 1121 const copyButton = e.target;
@@ -1590,7 +1136,4 @@
1590 1136 });
1591 1137 }
1592 1138 }
1593 1139 });
1594 -
1595 -
1596 -