PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.1.7
MxChat – AI Chatbot & Content Generation for WordPress v3.1.7
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 +43 -116 3.2.33.1.7 View file →
@@ -60,10 +60,10 @@
60 60 return Object.keys(this.instances);
61 61 },
62 62
63 63 // Session management per bot
64 - // Returns existing session ID from cookie or localStorage (with in-memory fallback),
65 - // or null if none exists. Does NOT create a new session — use ensureSession() for that.
64 + // Returns existing session ID from cookie or localStorage, or null if none exists.
65 + // Does NOT create a new session — use ensureSession() for that.
66 66 getChatSession: function(botId) {
67 67 var cookieName = 'mxchat_session_id_' + botId;
68 68 var storageKey = 'mxchat_session_id_' + botId;
69 69 var sessionId = getCookie(cookieName);
@@ -72,21 +72,8 @@
72 72 if (!sessionId) {
73 73 try { sessionId = localStorage.getItem(storageKey); } catch (e) {}
74 74 }
75 75
76 - // Fallback to in-memory instance when cookie AND localStorage are both blocked
77 - // (Safari ITP, strict tracking prevention, cross-origin iframes with partitioned
78 - // storage). Without this, ensureSession() can generate and store an ID that
79 - // getChatSession() then can't read back, causing null session_ids on send.
80 - if (!sessionId && this.instances[botId] && this.instances[botId].sessionId) {
81 - sessionId = this.instances[botId].sessionId;
82 - }
83 -
84 - // Guard against stored sentinel values that indicate earlier broken writes.
85 - if (sessionId === 'null' || sessionId === 'undefined') {
86 - sessionId = null;
87 - }
88 -
89 76 // Re-sync cookie from localStorage if cookie was lost
90 77 if (sessionId && !getCookie(cookieName)) {
91 78 document.cookie = cookieName + "=" + sessionId + "; path=/; max-age=86400; SameSite=Lax";
92 79 }
@@ -118,10 +105,12 @@
118 105 // Now that we have a session, do the deferred work
119 106 refreshNonceIfNeeded();
120 107 trackOriginatingPage();
121 108
122 - // Note: loadChatHistory is handled by showChatContainerForBot with loader UI,
123 - // so we do NOT call it here to avoid a race condition.
109 + var chatPersistenceEnabled = typeof mxchatChat !== 'undefined' && mxchatChat.chat_persistence_toggle === 'on';
110 + if (chatPersistenceEnabled && mxchatChat.email_collection_enabled !== 'on') {
111 + loadChatHistory(botId);
112 + }
124 113
125 114 return instance.sessionId;
126 115 },
127 116
@@ -612,23 +601,13 @@
612 601
613 602 // Get instance for session start timestamp (used when persistence is OFF)
614 603 var instance = MxChatInstances.get(botId);
615 604
616 - // Guarantee a non-null session_id before the AJAX leaves. ensureSession() is idempotent
617 - // and returns the guaranteed-present session id from the in-memory instance even when
618 - // cookie/localStorage writes are silently blocked by the browser.
619 - var sessionId = MxChatInstances.ensureSession(botId);
620 - if (!sessionId || sessionId === 'null' || sessionId === 'undefined') {
621 - // Last-resort generation to ensure we never POST a null marker.
622 - sessionId = generateSessionId();
623 - MxChatInstances.setChatSession(botId, sessionId);
624 - }
625 -
626 605 // Prepare AJAX data
627 606 const ajaxData = {
628 607 action: 'mxchat_handle_chat_request',
629 608 message: message,
630 - session_id: sessionId,
609 + session_id: getChatSession(botId),
631 610 nonce: mxchatChat.nonce,
632 611 current_page_url: window.location.href,
633 612 current_page_title: document.title,
634 613 bot_id: botId,
@@ -861,22 +840,12 @@
861 840
862 841 // Get instance for session start timestamp (used when persistence is OFF)
863 842 var instance = MxChatInstances.get(botId);
864 843
865 - // Guarantee a non-null session_id before the fetch. FormData.append() stringifies any
866 - // non-string value via String(), so passing `null` would POST the literal string "null"
867 - // and land in the transcripts table as a ghost session. ensureSession() always returns
868 - // a real string even when cookies/localStorage are blocked.
869 - var streamSessionId = MxChatInstances.ensureSession(botId);
870 - if (!streamSessionId || streamSessionId === 'null' || streamSessionId === 'undefined') {
871 - streamSessionId = generateSessionId();
872 - MxChatInstances.setChatSession(botId, streamSessionId);
873 - }
874 -
875 844 const formData = new FormData();
876 845 formData.append('action', 'mxchat_stream_chat');
877 846 formData.append('message', message);
878 - formData.append('session_id', streamSessionId);
847 + formData.append('session_id', getChatSession(botId));
879 848 formData.append('nonce', mxchatChat.nonce);
880 849 formData.append('current_page_url', window.location.href);
881 850 formData.append('current_page_title', document.title);
882 851 formData.append('bot_id', botId);
@@ -2323,19 +2292,9 @@
2323 2292 var content = message.content;
2324 2293 content = content.replace(/\\'/g, "'").replace(/\\"/g, '"');
2325 2294 content = decodeHTMLEntities(content);
2326 2295
2327 - // Skip linkify for messages containing structured HTML
2328 - // (forms, product cards, galleries, etc.) to avoid
2329 - // markdown formatting corrupting HTML attributes
2330 - // (e.g. underscores in name="field_name" becoming <em> tags)
2331 - if (content.includes("mxchat-product-card") ||
2332 - content.includes("mxchat-image-gallery") ||
2333 - content.includes("mxchat-featured-products") ||
2334 - content.includes("<form") ||
2335 - content.includes("<input") ||
2336 - content.includes("<select") ||
2337 - content.includes("<textarea")) {
2296 + if (content.includes("mxchat-product-card") || content.includes("mxchat-image-gallery")) {
2338 2297 messageElement.html(content);
2339 2298 } else {
2340 2299 var formattedContent = linkify(content);
2341 2300 messageElement.html(formattedContent);
@@ -2669,12 +2628,8 @@
2669 2628 var instance = MxChatInstances.get(botId);
2670 2629 if (emailBlocker && !instance.emailCheckDone) {
2671 2630 instance.emailCheckDone = true;
2672 2631 resolveEmailState(botId);
2673 - } else if (!emailBlocker) {
2674 - // No email collection — still route through showChatContainerForBot
2675 - // so the loader is shown while chat history loads
2676 - showChatContainerForBot(botId);
2677 2632 }
2678 2633 } else {
2679 2634 $chatbot.removeClass('visible').addClass('hidden');
2680 2635 $(this).removeClass('hidden');
@@ -2896,59 +2851,8 @@
2896 2851 });
2897 2852
2898 2853
2899 2854 // ====================================
2900 -// INIT LOADER & CHAT CONTAINER HELPERS
2901 -// ====================================
2902 -// These must be outside the email collection block so they're always available
2903 -// (used by persistence loading even when email collection is off)
2904 -
2905 -function showInitLoader(botId) {
2906 - var loader = getElementDOM(botId, 'mxchat-init-loader');
2907 - if (loader) loader.style.display = 'flex';
2908 -}
2909 -
2910 -function hideInitLoader(botId) {
2911 - var loader = getElementDOM(botId, 'mxchat-init-loader');
2912 - if (loader) loader.style.display = 'none';
2913 -}
2914 -
2915 -function showEmailFormForBot(botId) {
2916 - hideInitLoader(botId);
2917 - var emailBlocker = getElementDOM(botId, 'email-blocker');
2918 - var chatContainer = getElementDOM(botId, 'chat-container');
2919 - if (emailBlocker) emailBlocker.style.display = 'flex';
2920 - if (chatContainer) chatContainer.style.display = 'none';
2921 -}
2922 -
2923 -function showChatContainerForBot(botId) {
2924 - var emailBlocker = getElementDOM(botId, 'email-blocker');
2925 - var chatContainer = getElementDOM(botId, 'chat-container');
2926 - if (emailBlocker) emailBlocker.style.display = 'none';
2927 -
2928 - var instance = MxChatInstances.get(botId);
2929 - var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
2930 -
2931 - // If persistence is on and history hasn't loaded yet, show loader
2932 - // while history loads to prevent flash of empty chat
2933 - if (chatPersistenceEnabled && !instance.chatHistoryLoaded) {
2934 - if (chatContainer) chatContainer.style.display = 'none';
2935 - showInitLoader(botId);
2936 - loadChatHistory(botId, function() {
2937 - hideInitLoader(botId);
2938 - if (chatContainer) chatContainer.style.display = 'flex';
2939 - scrollToBottom(botId, true);
2940 - });
2941 - } else {
2942 - hideInitLoader(botId);
2943 - if (chatContainer) chatContainer.style.display = 'flex';
2944 - if (typeof loadChatHistory === 'function') {
2945 - loadChatHistory(botId);
2946 - }
2947 - }
2948 -}
2949 -
2950 -// ====================================
2951 2855 // EMAIL COLLECTION SETUP - MULTI-INSTANCE VERSION
2952 2856 // ====================================
2953 2857 // Only run email collection setup if it's enabled
2954 2858 if (mxchatChat && mxchatChat.email_collection_enabled === 'on') {
@@ -2984,8 +2888,40 @@
2984 2888 `;
2985 2889 document.head.appendChild(style);
2986 2890 }
2987 2891
2892 + // Helper functions for email collection (multi-instance aware)
2893 + function showEmailFormForBot(botId) {
2894 + var emailBlocker = getElementDOM(botId, 'email-blocker');
2895 + var chatContainer = getElementDOM(botId, 'chat-container');
2896 + if (emailBlocker) emailBlocker.style.display = 'flex';
2897 + if (chatContainer) chatContainer.style.display = 'none';
2898 + }
2899 +
2900 + function showChatContainerForBot(botId) {
2901 + var emailBlocker = getElementDOM(botId, 'email-blocker');
2902 + var chatContainer = getElementDOM(botId, 'chat-container');
2903 + if (emailBlocker) emailBlocker.style.display = 'none';
2904 +
2905 + var instance = MxChatInstances.get(botId);
2906 + var chatPersistenceEnabled = mxchatChat.chat_persistence_toggle === 'on';
2907 +
2908 + // If persistence is on and history hasn't loaded yet, keep container
2909 + // hidden until history loads to prevent flash of empty chat
2910 + if (chatPersistenceEnabled && !instance.chatHistoryLoaded) {
2911 + if (chatContainer) chatContainer.style.display = 'none';
2912 + loadChatHistory(botId, function() {
2913 + if (chatContainer) chatContainer.style.display = 'flex';
2914 + scrollToBottom(botId, true);
2915 + });
2916 + } else {
2917 + if (chatContainer) chatContainer.style.display = 'flex';
2918 + if (typeof loadChatHistory === 'function') {
2919 + loadChatHistory(botId);
2920 + }
2921 + }
2922 + }
2923 +
2988 2924 function isValidEmailAddress(email) {
2989 2925 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
2990 2926 return emailRegex.test(email.trim()) && email.length <= 254;
2991 2927 }
@@ -3123,14 +3059,13 @@
3123 3059
3124 3060 function checkSessionAndEmailForBot(botId) {
3125 3061 const sessionId = MxChatInstances.ensureSession(botId);
3126 3062
3127 - // Hide both panels while we check — show loader instead
3063 + // Hide both panels while we check — prevents flash of wrong state
3128 3064 var emailBlocker = getElementDOM(botId, 'email-blocker');
3129 3065 var chatContainer = getElementDOM(botId, 'chat-container');
3130 3066 if (emailBlocker) emailBlocker.style.display = 'none';
3131 3067 if (chatContainer) chatContainer.style.display = 'none';
3132 - showInitLoader(botId);
3133 3068
3134 3069 fetch(mxchatChat.ajax_url, {
3135 3070 method: 'POST',
3136 3071 headers: {
@@ -3310,8 +3245,9 @@
3310 3245 $('.mxchat-chatbot-wrapper').each(function() {
3311 3246 var botId = $(this).data('bot-id') || 'default';
3312 3247 var emailBlocker = getElementDOM(botId, 'email-blocker');
3313 3248
3249 + // Only check if email blocker exists for this bot
3314 3250 if (emailBlocker) {
3315 3251 if (isEmbeddedBot(botId)) {
3316 3252 // Embedded bots are always visible — check now
3317 3253 resolveEmailState(botId);
@@ -3316,15 +3252,8 @@
3316 3252 // Embedded bots are always visible — check now
3317 3253 resolveEmailState(botId);
3318 3254 }
3319 3255 // Floating bots: handled in the widget open handler
3320 - } else if (isEmbeddedBot(botId)) {
3321 - // Embedded bot, no email collection — load history with loader
3322 - var chatPersistenceEnabled = typeof mxchatChat !== 'undefined' && mxchatChat.chat_persistence_toggle === 'on';
3323 - if (chatPersistenceEnabled) {
3324 - MxChatInstances.ensureSession(botId);
3325 - showChatContainerForBot(botId);
3326 - }
3327 3256 }
3328 3257 });
3329 3258 }
3330 3259
@@ -3349,10 +3278,8 @@
3349 3278 var instance = MxChatInstances.get(botId);
3350 3279 if (emailBlocker && !instance.emailCheckDone) {
3351 3280 instance.emailCheckDone = true;
3352 3281 resolveEmailState(botId);
3353 - } else if (!emailBlocker) {
3354 - showChatContainerForBot(botId);
3355 3282 }
3356 3283 }
3357 3284 });
3358 3285