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