| @@ -256,48 +256,24 @@ | ||
| 256 | 256 | sendMessageToChatbot(question); |
| 257 | 257 | }); |
| 258 | 258 | |
| 259 | 259 | |
| 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 | - return `<h${level} class="chat-heading">${content}</h${level}>`; | |
| 266 | - }); | |
| 267 | -} | |
| 268 | - | |
| 269 | -// Update the linkify function to handle both URLs and markdown | |
| 270 | -// Modified linkify function that only processes URLs in user content | |
| 260 | +// Use the linkTarget in your linkify function | |
| 271 | 261 | function linkify(inputText) { |
| 272 | - if (!inputText) return ''; | |
| 273 | - | |
| 274 | - // Process markdown headers | |
| 275 | - let processedText = formatMarkdownHeaders(inputText); | |
| 276 | - | |
| 277 | - // Process markdown links | |
| 278 | - const markdownLinkPattern = /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g; | |
| 279 | - processedText = processedText.replace(markdownLinkPattern, (match, text, url) => { | |
| 280 | - const safeUrl = encodeURI(url); | |
| 281 | - const safeText = sanitizeUserInput(text); | |
| 282 | - return `<a href="${safeUrl}" target="${linkTarget}">${safeText}</a>`; | |
| 283 | - }); | |
| 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>'); | |
| 284 | 266 | |
| 285 | - // Process standalone URLs | |
| 286 | - const urlPattern = /(^|[^">])(https?:\/\/[^\s<]+)/gim; | |
| 287 | - processedText = processedText.replace(urlPattern, (match, prefix, url) => { | |
| 288 | - const safeUrl = encodeURI(url); | |
| 289 | - return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`; | |
| 290 | - }); | |
| 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>'); | |
| 291 | 270 | |
| 292 | - // Process www. URLs | |
| 293 | - const wwwPattern = /(^|[^">])(www\.[\S]+(\b|$))(?![^<]*<\/a>)/gim; | |
| 294 | - processedText = processedText.replace(wwwPattern, (match, prefix, url) => { | |
| 295 | - const safeUrl = encodeURI(`http://${url}`); | |
| 296 | - return `${prefix}<a href="${safeUrl}" target="${linkTarget}">${url}</a>`; | |
| 297 | - }); | |
| 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>'); | |
| 298 | 274 | |
| 299 | - return processedText; | |
| 275 | + return replacedText; | |
| 300 | 276 | } |
| 301 | 277 | |
| 302 | 278 | |
| 303 | 279 | function scrollElementToTop(element) { |
| @@ -403,10 +379,10 @@ | ||
| 403 | 379 | if (response.data && response.data.filename) { |
| 404 | 380 | showActivePdf(response.data.filename); |
| 405 | 381 | activePdfFile = response.data.filename; |
| 406 | 382 | } |
| 407 | - | |
| 408 | - // Add redirect check here | |
| 383 | + | |
| 384 | + // Add redirect check here | |
| 409 | 385 | if (response.redirect_url) { |
| 410 | 386 | let responseText = response.text || ''; |
| 411 | 387 | if (responseText) { |
| 412 | 388 | replaceLastMessage("bot", responseText); |
| @@ -416,9 +392,8 @@ | ||
| 416 | 392 | }, 1500); |
| 417 | 393 | return; |
| 418 | 394 | } |
| 419 | 395 | |
| 420 | - | |
| 421 | 396 | // Check for live agent response |
| 422 | 397 | if (response.success && response.data && response.data.status === 'waiting_for_agent') { |
| 423 | 398 | updateChatModeIndicator('agent'); |
| 424 | 399 | return; |
| @@ -467,17 +442,11 @@ | ||
| 467 | 442 | } |
| 468 | 443 | }); |
| 469 | 444 | } |
| 470 | 445 | |
| 471 | -// Sanitize only user input | |
| 472 | -function sanitizeUserInput(text) { | |
| 473 | - const div = document.createElement('div'); | |
| 474 | - div.textContent = text; | |
| 475 | - return div.innerHTML; | |
| 476 | -} | |
| 446 | +function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) { | |
| 447 | + //console.log("Appending message. Sender:", sender, "Content:", messageText); | |
| 477 | 448 | |
| 478 | -// Modified appendMessage function that only sanitizes user content | |
| 479 | -function appendMessage(sender, messageText = '', messageHtml = '', images = [], isTemporary = false) { | |
| 480 | 449 | try { |
| 481 | 450 | // Determine styles based on sender type |
| 482 | 451 | let messageClass, bgColor, fontColor; |
| 483 | 452 | |
| @@ -484,52 +453,42 @@ | ||
| 484 | 453 | if (sender === "user") { |
| 485 | 454 | messageClass = "user-message"; |
| 486 | 455 | bgColor = userMessageBgColor; |
| 487 | 456 | fontColor = userMessageFontColor; |
| 488 | - // Only sanitize user input | |
| 489 | - messageText = sanitizeUserInput(messageText); | |
| 490 | - } else if (sender === "agent") { | |
| 491 | - messageClass = "agent-message"; | |
| 492 | - bgColor = liveAgentMessageBgColor; | |
| 493 | - fontColor = liveAgentMessageFontColor; | |
| 494 | - } else { | |
| 457 | + } else if (sender === "agent") { | |
| 458 | + messageClass = "agent-message"; | |
| 459 | + bgColor = liveAgentMessageBgColor; | |
| 460 | + fontColor = liveAgentMessageFontColor; | |
| 461 | + } else { | |
| 495 | 462 | messageClass = "bot-message"; |
| 496 | 463 | bgColor = botMessageBgColor; |
| 497 | 464 | fontColor = botMessageFontColor; |
| 498 | 465 | } |
| 499 | 466 | |
| 500 | - const messageDiv = $('<div>') | |
| 501 | - .addClass(messageClass) | |
| 502 | - .css({ | |
| 503 | - 'background': bgColor, | |
| 504 | - 'color': fontColor, | |
| 505 | - 'margin-bottom': '1em' | |
| 506 | - }); | |
| 467 | + const messageDiv = $('<div>') | |
| 468 | + .addClass(messageClass) | |
| 469 | + .css({ | |
| 470 | + 'background': bgColor, | |
| 471 | + 'color': fontColor, | |
| 472 | + }); | |
| 507 | 473 | |
| 508 | - // Process the message content based on sender | |
| 509 | - let fullMessage; | |
| 510 | - if (sender === "user") { | |
| 511 | - // For user messages, apply linkify after sanitization | |
| 512 | - fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText)))); | |
| 513 | - } else { | |
| 514 | - // For bot/agent messages, preserve HTML | |
| 515 | - fullMessage = messageText; | |
| 516 | - } | |
| 474 | + // Add CSS for paragraphs | |
| 475 | + messageDiv.css({ | |
| 476 | + 'margin-bottom': '1em' | |
| 477 | + }); | |
| 517 | 478 | |
| 479 | + // Format and process the message content | |
| 480 | + let fullMessage = linkify(formatBoldText(convertNewlinesToBreaks(formatCodeBlocks(messageText)))); | |
| 481 | + | |
| 518 | 482 | // Add images if provided |
| 519 | 483 | if (images && images.length > 0) { |
| 520 | 484 | fullMessage += '<div class="image-gallery">'; |
| 521 | 485 | images.forEach(img => { |
| 522 | - // Ensure image URLs and titles are properly escaped | |
| 523 | - const safeTitle = sanitizeUserInput(img.title); | |
| 524 | - const safeUrl = encodeURI(img.image_url); | |
| 525 | - const safeThumbnail = encodeURI(img.thumbnail_url); | |
| 526 | - | |
| 527 | 486 | fullMessage += ` |
| 528 | 487 | <div style="margin-bottom: 10px;"> |
| 529 | - <strong>${safeTitle}</strong><br> | |
| 530 | - <a href="${safeUrl}" target="_blank"> | |
| 531 | - <img src="${safeThumbnail}" alt="${safeTitle}" style="max-width: 100px; height: auto; margin: 5px;" /> | |
| 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;" /> | |
| 532 | 491 | </a> |
| 533 | 492 | </div>`; |
| 534 | 493 | }); |
| 535 | 494 | fullMessage += '</div>'; |
| @@ -535,20 +494,23 @@ | ||
| 535 | 494 | fullMessage += '</div>'; |
| 536 | 495 | } |
| 537 | 496 | |
| 538 | 497 | // Append HTML content if provided |
| 539 | - if (messageHtml && sender !== "user") { | |
| 498 | + if (messageHtml) { | |
| 540 | 499 | fullMessage += '<br><br>' + messageHtml; |
| 541 | 500 | } |
| 542 | 501 | |
| 543 | 502 | messageDiv.html(fullMessage); |
| 544 | 503 | |
| 504 | + // Add a class for temporary messages if needed | |
| 545 | 505 | if (isTemporary) { |
| 546 | 506 | messageDiv.addClass('temporary-message'); |
| 547 | 507 | } |
| 548 | 508 | |
| 549 | - messageDiv.hide().appendTo('#chat-box').fadeIn(300, function() { | |
| 509 | + // Append the message to the chat box | |
| 510 | + messageDiv.hide().appendTo('#chat-box').fadeIn(300, function () { | |
| 550 | 511 | if (sender === "bot") { |
| 512 | + // After bot's message is displayed, scroll the last user message to the top | |
| 551 | 513 | const lastUserMessage = $('#chat-box').find('.user-message').last(); |
| 552 | 514 | if (lastUserMessage.length) { |
| 553 | 515 | scrollElementToTop(lastUserMessage); |
| 554 | 516 | } |
| @@ -555,13 +517,13 @@ | ||
| 555 | 517 | } |
| 556 | 518 | }); |
| 557 | 519 | |
| 558 | 520 | if (messageText.id) { |
| 559 | - lastSeenMessageId = messageText.id; | |
| 560 | - hideNotification(); | |
| 561 | - } | |
| 521 | + lastSeenMessageId = messageText.id; | |
| 522 | + hideNotification(); | |
| 523 | + } | |
| 562 | 524 | } catch (error) { |
| 563 | - console.error("Error rendering message:", error); | |
| 525 | + console.error("Error rendering message with images:", error); | |
| 564 | 526 | } |
| 565 | 527 | } |
| 566 | 528 | |
| 567 | 529 | |