PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.7
MxChat – AI Chatbot & Content Generation for WordPress v2.0.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 +101 -51 2.0.42.0.7 View file →
@@ -261,28 +261,42 @@
261 261 function formatMarkdownHeaders(text) {
262 262 // Handle h1 to h6 headers
263 263 return text.replace(/^(#{1,6})\s(.+)$/gm, function(match, hashes, content) {
264 264 const level = hashes.length;
265 - return `<h${level} class="chat-heading">${content}</h${level}>`;
265 + // Apply the same color as the bot message font color
266 + return `<h${level} class="chat-heading" style="color: inherit;">${content}</h${level}>`;
266 267 });
267 268 }
268 269
269 270 // Update the linkify function to handle both URLs and markdown
271 +// Modified linkify function that only processes URLs in user content
270 272 function linkify(inputText) {
271 - // First process markdown headers
273 + if (!inputText) return '';
274 +
275 + // Process markdown headers
272 276 let processedText = formatMarkdownHeaders(inputText);
273 277
274 - // Then process links as before
275 - var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
276 - processedText = processedText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
278 + // Process markdown links
279 + const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
280 + processedText = processedText.replace(markdownLinkPattern, (match, text, url) => {
281 + const safeUrl = encodeURI(url);
282 + const safeText = sanitizeUserInput(text);
283 + return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`;
284 + });
277 285
278 - // Replace standalone URLs not already in an <a> tag
279 - var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
280 - processedText = processedText.replace(urlPattern, '$1<a href="$2" target="' + linkTarget + '">$2</a>');
286 + // Process standalone URLs
287 + const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
288 + processedText = processedText.replace(urlPattern, (match, prefix, url) => {
289 + const safeUrl = encodeURI(url);
290 + return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
291 + });
281 292
282 - // Replace "www." prefixed URLs not already in an <a> tag
283 - var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
284 - processedText = processedText.replace(wwwPattern, '$1<a href="http://$2" target="' + linkTarget + '">$2</a>');
293 + // Process www. URLs
294 + const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
295 + processedText = processedText.replace(wwwPattern, (match, prefix, url) => {
296 + const safeUrl = encodeURI(`http://${url}`);
297 + return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`;
298 + });
285 299
286 300 return processedText;
287 301 }
288 302
@@ -390,8 +404,20 @@
390 404 if (response.data && response.data.filename) {
391 405 showActivePdf(response.data.filename);
392 406 activePdfFile = response.data.filename;
393 407 }
408 +
409 + // Add redirect check here
410 + if (response.redirect_url) {
411 + let responseText = response.text || '';
412 + if (responseText) {
413 + replaceLastMessage("bot", responseText);
414 + }
415 + setTimeout(() => {
416 + window.location.href = response.redirect_url;
417 + }, 1500);
418 + return;
419 + }
394 420
395 421
396 422 // Check for live agent response
397 423 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
@@ -442,11 +468,17 @@
442 468 }
443 469 });
444 470 }
445 471
472 +// Sanitize only user input
473 +function sanitizeUserInput(text) {
474 + const div = document.createElement('div');
475 + div.textContent = text;
476 + return div.innerHTML;
477 +}
478 +
479 +// Modified appendMessage function that only sanitizes user content
446 480 function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) {
447 - //console.log("Appending message. Sender:", sender, "Content:", messageText);
448 -
449 481 try {
450 482 // Determine styles based on sender type
451 483 let messageClass, bgColor, fontColor;
452 484
@@ -453,42 +485,52 @@
453 485 if (sender === "user") {
454 486 messageClass = "user-message";
455 487 bgColor = userMessageBgColor;
456 488 fontColor = userMessageFontColor;
457 - } else if (sender === "agent") {
458 - messageClass = "agent-message";
459 - bgColor = liveAgentMessageBgColor;
460 - fontColor = liveAgentMessageFontColor;
461 - } else {
489 + // Only sanitize user input
490 + messageText = sanitizeUserInput(messageText);
491 + } else if (sender === "agent") {
492 + messageClass = "agent-message";
493 + bgColor = liveAgentMessageBgColor;
494 + fontColor = liveAgentMessageFontColor;
495 + } else {
462 496 messageClass = "bot-message";
463 497 bgColor = botMessageBgColor;
464 498 fontColor = botMessageFontColor;
465 499 }
466 500
467 - const messageDiv = $('<div>')
468 - .addClass(messageClass)
469 - .css({
470 - 'background': bgColor,
471 - 'color': fontColor,
472 - });
501 + const messageDiv = $('<div>')
502 + .addClass(messageClass)
503 + .css({
504 + 'background': bgColor,
505 + 'color': fontColor,
506 + 'margin-bottom': '1em'
507 + });
473 508
474 - // Add CSS for paragraphs
475 - messageDiv.css({
476 - 'margin-bottom': '1em'
477 - });
509 + // Process the message content based on sender
510 + let fullMessage;
511 + if (sender === "user") {
512 + // For user messages, apply linkify after sanitization
513 + fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
514 + } else {
515 + // For bot/agent messages, preserve HTML
516 + fullMessage = messageText;
517 + }
478 518
479 - // Format and process the message content
480 - let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText))));
481 -
482 519 // Add images if provided
483 520 if (images && images.length > 0) {
484 521 fullMessage += '<div class="image-gallery">';
485 522 images.forEach(img => {
523 + // Ensure image URLs and titles are properly escaped
524 + const safeTitle = sanitizeUserInput(img.title);
525 + const safeUrl = encodeURI(img.image_url);
526 + const safeThumbnail = encodeURI(img.thumbnail_url);
527 +
486 528 fullMessage += `
487 529 <div style="margin-bottom: 10px;">
488 - <strong>${img.title}</strong><br>
489 - <a href="${img.image_url}" target="_blank">
490 - <img src="${img.thumbnail_url}" alt="${img.title}" style="max-width: 100px; height: auto; margin: 5px;" />
530 + <strong>${safeTitle}</strong><br>
531 + <a href="${safeUrl}" target="_blank">
532 + <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" />
491 533 </a>
492 534 </div>`;
493 535 });
494 536 fullMessage += '</div>';
@@ -494,23 +536,20 @@
494 536 fullMessage += '</div>';
495 537 }
496 538
497 539 // Append HTML content if provided
498 - if (messageHtml) {
540 + if (messageHtml && sender !== "user") {
499 541 fullMessage += '<br><br>' + messageHtml;
500 542 }
501 543
502 544 messageDiv.html(fullMessage);
503 545
504 - // Add a class for temporary messages if needed
505 546 if (isTemporary) {
506 547 messageDiv.addClass('temporary-message');
507 548 }
508 549
509 - // Append the message to the chat box
510 - messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () {
550 + messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() {
511 551 if (sender === "bot") {
512 - // After bot's message is displayed, scroll the last user message to the top
513 552 const lastUserMessage = $('#chat-box').find('.user-message').last();
514 553 if (lastUserMessage.length) {
515 554 scrollElementToTop(lastUserMessage);
516 555 }
@@ -517,13 +556,13 @@
517 556 }
518 557 });
519 558
520 559 if (messageText.id) {
521 - lastSeenMessageId = messageText.id;
522 - hideNotification();
523 - }
560 + lastSeenMessageId = messageText.id;
561 + hideNotification();
562 + }
524 563 } catch (error) {
525 - console.error("Error rendering message with images:", error);
564 + console.error("Error rendering message:", error);
526 565 }
527 566 }
528 567
529 568
@@ -1091,10 +1130,18 @@
1091 1130 document.getElementById('word-upload').click();
1092 1131 });
1093 1132 }
1094 1133
1134 +function addSafeEventListener(elementId, eventType, handler) {
1135 + const element = document.getElementById(elementId);
1136 + if (element) {
1137 + element.addEventListener(eventType, handler);
1138 + }
1139 +}
1140 +
1141 +
1095 1142 // PDF file input change handler
1096 -document.getElementById('pdf-upload').addEventListener('change', async function(e) {
1143 +addSafeEventListener('pdf-upload', 'change', async function(e) {
1097 1144 const file = e.target.files[0];
1098 1145
1099 1146 if (!file || file.type !== 'application/pdf') {
1100 1147 alert('Please select a valid PDF file.');
@@ -1166,9 +1213,9 @@
1166 1213 }
1167 1214 });
1168 1215
1169 1216 // Word file input change handler
1170 -document.getElementById('word-upload').addEventListener('change', async function(e) {
1217 +addSafeEventListener('word-upload', 'change', async function(e) {
1171 1218 const file = e.target.files[0];
1172 1219
1173 1220 if (!file || file.type !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
1174 1221 alert('Please select a valid Word document (.docx).');
@@ -1401,21 +1448,24 @@
1401 1448 document.addEventListener('DOMContentLoaded', function() {
1402 1449 checkInitialDocumentStatus();
1403 1450 });
1404 1451
1405 -// Style all toolbar elements
1406 1452 const toolbarElements = [
1407 1453 '#mxchat-chatbot .toolbar-btn svg',
1408 1454 '#mxchat-chatbot .active-pdf-name',
1409 1455 '#mxchat-chatbot .active-word-name',
1410 1456 '#mxchat-chatbot .remove-pdf-btn svg',
1411 - '#mxchat-chatbot .remove-word-btn svg'
1457 + '#mxchat-chatbot .remove-word-btn svg',
1458 + '#mxchat-chatbot .toolbar-perplexity svg'
1412 1459 ];
1413 -$(toolbarElements.join(', ')).css({
1414 - 'fill': toolbarIconColor,
1415 - 'color': toolbarIconColor
1460 +
1461 +toolbarElements.forEach(selector => {
1462 + $(selector).css({
1463 + 'fill': toolbarIconColor,
1464 + 'stroke': toolbarIconColor,
1465 + 'color': toolbarIconColor
1466 + });
1416 1467 });
1417 -
1418 1468
1419 1469
1420 1470 // Ensure essential elements are defined
1421 1471 const emailForm = document.getElementById('email-collection-form');