/** * Message Content Processing Utilities * Handles mention badges, URL links, and image previews */ /** * Process message content to handle mentions, URLs, and images * @param {string} content - Raw message content * @returns {string} - Processed HTML content */ function processMessageContent(content) { if (!content) return ''; // First escape HTML to prevent XSS let processed = escapeHtml(content); // Process in order: // 1. Convert @[Username] mentions to badges processed = processMentions(processed); // 2. Convert URLs to links (and images to thumbnails) processed = processUrls(processed); return processed; } /** * Convert @[Username] mentions to styled badges * @param {string} text - HTML-escaped text * @returns {string} - Text with mention badges */ function processMentions(text) { // Match @[Username] pattern // Note: text is already HTML-escaped, so we match escaped brackets const mentionPattern = /@\[([^\]]+)\]/g; return text.replace(mentionPattern, (_match, username) => { // Create badge similar to Android Meshcore app return `@${username}`; }); } /** * Convert URLs to clickable links and images to thumbnails * @param {string} text - HTML-escaped text * @returns {string} - Text with links and image thumbnails */ function processUrls(text) { // URL regex pattern (handles http:// and https://) const urlPattern = /(https?:\/\/[^\s<>"{}|\\^`\[\]]+)/g; return text.replace(urlPattern, (url) => { // Check if URL is an image if (isImageUrl(url)) { return createImageThumbnail(url); } else { return createLink(url); } }); } /** * Check if URL points to an image * @param {string} url - URL to check * @returns {boolean} - True if URL is an image */ function isImageUrl(url) { const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp']; const urlLower = url.toLowerCase(); return imageExtensions.some(ext => urlLower.endsWith(ext)); } /** * Create a clickable link * @param {string} url - URL to link to * @returns {string} - HTML link element */ function createLink(url) { return ``; } /** * Create an image thumbnail with click-to-expand * @param {string} url - Image URL * @returns {string} - HTML image thumbnail */ function createImageThumbnail(url) { // Escape URL for use in HTML attributes const escapedUrl = escapeHtmlAttribute(url); return `
`; } /** * Show image in modal * @param {string} url - Image URL to display */ function showImageModal(url) { // Create modal if it doesn't exist let modal = document.getElementById('imagePreviewModal'); if (!modal) { modal = createImageModal(); document.body.appendChild(modal); } // Set image source const img = modal.querySelector('#imagePreviewImg'); if (img) { img.src = url; } // Show modal using Bootstrap const bsModal = new bootstrap.Modal(modal); bsModal.show(); } /** * Create image preview modal element * @returns {HTMLElement} - Modal element */ function createImageModal() { const modal = document.createElement('div'); modal.id = 'imagePreviewModal'; modal.className = 'modal fade'; modal.tabIndex = -1; modal.setAttribute('aria-labelledby', 'imagePreviewModalLabel'); modal.setAttribute('aria-hidden', 'true'); modal.innerHTML = `