PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.0.8
MxChat – AI Chatbot & Content Generation for WordPress v2.0.8
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 +104 -54 2.0.32.0.8 View file →
@@ -256,24 +256,49 @@
256 256 sendMessageToChatbot(question);
257 257 });
258 258
259 259
260 -// Use the linkTarget in your linkify function
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 + // Apply the same color as the bot message font color
266 + return `<h${level} class="chat-heading" style="color: inherit;">${content}</h${level}>`;
267 + });
268 +}
269 +
270 +// Update the linkify function to handle both URLs and markdown
271 +// Modified linkify function that only processes URLs in user content
261 272 function linkify(inputText) {
262 - // Check for already linked URLs and skip them
263 - // We use negative lookaheads to skip anything already in an <a> tag
264 - var markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g;
265 - var replacedText = inputText.replace(markdownLinkPattern, '<a href="$2" target="' + linkTarget + '">$1</a>');
273 + if (!inputText) return '';
274 +
275 + // Process markdown headers
276 + let processedText = formatMarkdownHeaders(inputText);
277 +
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 + });
266 285
267 - // Replace standalone URLs not already in an <a> tag
268 - var urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim;
269 - replacedText = replacedText.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 + });
270 292
271 - // Replace "www." prefixed URLs not already in an <a> tag
272 - var wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim;
273 - replacedText = replacedText.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 + });
274 299
275 - return replacedText;
300 + return processedText;
276 301 }
277 302
278 303
279 304 function scrollElementToTop(element) {
@@ -379,10 +404,10 @@
379 404 if (response.data && response.data.filename) {
380 405 showActivePdf(response.data.filename);
381 406 activePdfFile = response.data.filename;
382 407 }
383 -
384 - // Add redirect check here
408 +
409 + // Add redirect check here
385 410 if (response.redirect_url) {
386 411 let responseText = response.text || '';
387 412 if (responseText) {
388 413 replaceLastMessage("bot", responseText);
@@ -392,8 +417,9 @@
392 417 }, 1500);
393 418 return;
394 419 }
395 420
421 +
396 422 // Check for live agent response
397 423 if (response.success && response.data && response.data.status === 'waiting_for_agent') {
398 424 updateChatModeIndicator('agent');
399 425 return;
@@ -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');