PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.24
MxChat – AI Chatbot & Content Generation for WordPress v3.2.24
3.2.25 3.2.24 3.2.23 3.2.22 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 All 156 releases
← All changes | js/chat-script.js +76 -58 3.2.23 → 3.2.24 View file →
@@ -1306,11 +1306,26 @@
1306 1306 let testingDataReceived = false;
1307 1307 let streamingStarted = false;
1308 1308 // Server-pushed html to append as its OWN bot bubble once the stream
1309 1309 // finishes (e.g. the consent-safe YouTube embed, plan 03ba33). Rendering is
1310 - // deferred to [DONE] so the embed always lands BELOW the streamed text.
1310 + // deferred to the end of the stream so the embed always lands BELOW the
1311 + // streamed text.
1311 1312 let pendingAppendHtml = '';
1312 1313
1314 + // Render the stashed appendix as its own bot bubble below the streamed
1315 + // text — mirrors how it is saved in the transcript, so history replays
1316 + // identically. Called from BOTH ends of a stream: the [DONE] line and
1317 + // the stream simply closing (the OpenAI Responses path and Gemini end
1318 + // without a [DONE] line). The stash is cleared, so a stream that sends
1319 + // [DONE] and then closes renders it once. Any future appendix-type
1320 + // event belongs in here, not in one of the two branches.
1321 + function flushAppendHtml() {
1322 + if (pendingAppendHtml) {
1323 + appendMessage("bot", "", pendingAppendHtml, [], false, botId);
1324 + pendingAppendHtml = '';
1325 + }
1326 + }
1327 +
1313 1328 // Abortable stream: a fresh controller per turn, keyed by bot instance.
1314 1329 // The Stop control (send button swapped in place) aborts both the read
1315 1330 // loop and the underlying request.
1316 1331 var streamControl = { controller: new AbortController(), aborted: false };
@@ -1391,8 +1406,10 @@
1391 1406
1392 1407 // Re-enable chat input when stream ends with content
1393 1408 enableChatInput(botId);
1394 1409
1410 + flushAppendHtml();
1411 +
1395 1412 // Scroll the user's last message to the top now that the
1396 1413 // bot's full reply has rendered (gives max reading room).
1397 1414 var $chatBoxDone = getElement(botId, 'chat-box');
1398 1415 var $lastUserMsgDone = $chatBoxDone.find('.user-message').last();
@@ -1423,16 +1440,9 @@
1423 1440
1424 1441 // Re-enable chat input after streaming completes
1425 1442 enableChatInput(botId);
1426 1443
1427 - // Render any server-pushed appendix html (e.g. the
1428 - // YouTube embed) as its own bot bubble below the
1429 - // streamed text — mirrors how it is saved in the
1430 - // transcript, so history replays identically.
1431 - if (pendingAppendHtml) {
1432 - appendMessage("bot", "", pendingAppendHtml, [], false, botId);
1433 - pendingAppendHtml = '';
1434 - }
1444 + flushAppendHtml();
1435 1445
1436 1446 // Scroll the user's last message to the top now
1437 1447 // that the bot's full reply has rendered.
1438 1448 var $chatBoxStreamDone = getElement(botId, 'chat-box');
@@ -2251,8 +2261,56 @@
2251 2261 // Error rendering message - silently continue
2252 2262 }
2253 2263 }
2254 2264
2265 +// Link clicks in bot and agent messages: the click is recorded, the browser
2266 +// follows the anchor itself. Navigation must not wait for the tracking
2267 +// request: a window.open() made from that request's callback is outside the
2268 +// visitor's tap, and Safari's pop-up blocker drops it without a message.
2269 +
2270 +// Give a tracked anchor the target it should open in, and noopener on a new tab.
2271 +function prepareTrackedLink($link) {
2272 + if (linkTarget === '_blank' && $link.attr('target') !== '_blank') {
2273 + $link.attr('target', '_blank');
2274 + }
2275 + if ($link.attr('target') === '_blank') {
2276 + var rel = $link.attr('rel') || '';
2277 + if (!/(^|\s)noopener(\s|$)/.test(rel)) {
2278 + $link.attr('rel', $.trim(rel + ' noopener'));
2279 + }
2280 + }
2281 +}
2282 +
2283 +// Record one click, fire-and-forget. sendBeacon survives the page unloading
2284 +// when the link opens in the same tab.
2285 +function trackLinkClick(href, messageContext, botId) {
2286 + var fields = {
2287 + action: 'mxchat_track_url_click',
2288 + session_id: getChatSession(botId),
2289 + url: href,
2290 + message_context: messageContext,
2291 + nonce: mxchatChat.nonce
2292 + };
2293 + try {
2294 + if (navigator.sendBeacon && typeof FormData !== 'undefined') {
2295 + var body = new FormData();
2296 + $.each(fields, function(name, value) {
2297 + body.append(name, value == null ? '' : value);
2298 + });
2299 + if (navigator.sendBeacon(mxchatChat.ajax_url, body)) {
2300 + return;
2301 + }
2302 + }
2303 + } catch (error) {
2304 + // Fall through to the plain request
2305 + }
2306 + $.ajax({
2307 + url: mxchatChat.ajax_url,
2308 + type: 'POST',
2309 + data: fields
2310 + });
2311 +}
2312 +
2255 2313 // Helper function to attach link tracking with proper event handling
2256 2314 function attachLinkTracking(messageDiv, messageText, botId) {
2257 2315 botId = botId || 'default';
2258 2316 // Use a slight delay to ensure DOM is ready
@@ -2270,11 +2328,13 @@
2270 2328 if (originalHref && (originalHref.startsWith('http://') || originalHref.startsWith('https://'))) {
2271 2329 // Remove any existing click handlers first
2272 2330 $link.off('click.tracking');
2273 2331
2332 + // The anchor carries its own target: the browser follows it
2333 + prepareTrackedLink($link);
2334 +
2274 2335 // Add new click handler with namespace
2275 2336 $link.on('click.tracking', function(e) {
2276 - e.preventDefault();
2277 2337 e.stopPropagation();
2278 2338
2279 2339 const messageContext = typeof messageText === 'string'
2280 2340 ? messageText.substring(0, 200)
@@ -2279,30 +2339,10 @@
2279 2339 const messageContext = typeof messageText === 'string'
2280 2340 ? messageText.substring(0, 200)
2281 2341 : '';
2282 2342
2283 - // Track the click
2284 - $.ajax({
2285 - url: mxchatChat.ajax_url,
2286 - type: 'POST',
2287 - data: {
2288 - action: 'mxchat_track_url_click',
2289 - session_id: getChatSession(botId),
2290 - url: originalHref,
2291 - message_context: messageContext,
2292 - nonce: mxchatChat.nonce
2293 - },
2294 - complete: function() {
2295 - // Always redirect, even if tracking fails
2296 - if ($link.attr('target') === '_blank' || linkTarget === '_blank') {
2297 - window.open(originalHref, '_blank');
2298 - } else {
2299 - window.location.href = originalHref;
2300 - }
2301 - }
2302 - });
2303 -
2304 - return false; // Extra insurance to prevent default
2343 + prepareTrackedLink($link);
2344 + trackLinkClick(originalHref, messageContext, botId);
2305 2345 });
2306 2346 }
2307 2347 });
2308 2348 }, 100); // Small delay to ensure DOM is ready
@@ -4550,14 +4590,8 @@
4550 4590 if (messageDiv.length > 0) {
4551 4591 const originalHref = $link.attr('href');
4552 4592
4553 4593 if (originalHref && (originalHref.startsWith('http://') || originalHref.startsWith('https://'))) {
4554 - e.preventDefault();
4555 - e.stopPropagation();
4556 -
4557 - // Mark as tracked
4558 - $link.attr('data-tracked', 'true');
4559 -
4560 4594 // Get bot ID from the chat box context
4561 4595 var botId = getBotIdFromElement(this);
4562 4596
4563 4597 // Get message context from the message div
@@ -4562,28 +4596,12 @@
4562 4596
4563 4597 // Get message context from the message div
4564 4598 const messageText = messageDiv.text().substring(0, 200);
4565 4599
4566 - $.ajax({
4567 - url: mxchatChat.ajax_url,
4568 - type: 'POST',
4569 - data: {
4570 - action: 'mxchat_track_url_click',
4571 - session_id: getChatSession(botId),
4572 - url: originalHref,
4573 - message_context: messageText,
4574 - nonce: mxchatChat.nonce
4575 - },
4576 - complete: function() {
4577 - if ($link.attr('target') === '_blank' || linkTarget === '_blank') {
4578 - window.open(originalHref, '_blank');
4579 - } else {
4580 - window.location.href = originalHref;
4581 - }
4582 - }
4583 - });
4584 -
4585 - return false;
4600 + // Same path as attachLinkTracking(): set the target, record the
4601 + // click, and let the browser follow the anchor.
4602 + prepareTrackedLink($link);
4603 + trackLinkClick(originalHref, messageText, botId);
4586 4604 }
4587 4605 }
4588 4606 });
4589 4607