1(function (root) { 2 function escapeHtml(text) { 3 return String(text || '') 4 .replace(/&/g, '&') 5 .replace(/</g, '<') 6 .replace(/>/g, '>') 7 .replace(/"/g, '"') 8 .replace(/'/g, '''); 9 } 10 11 function isSafeHref(href, baseOrigin) { 12 var value = String(href || '').trim(); 13 if (!value) { 14 return false; 15 } 16 17 if (/^[a-z][a-z0-9+.-]*:/i.test(value) && !/^https?:/i.test(value)) { 18 return false; 19 } 20 21 if (value.indexOf('//') === 0) { 22 return false; 23 } 24 25 if (value.charAt(0) === '/' && value.charAt(1) !== '/') { 26 return true; 27 } 28 29 try { 30 var parsed = new URL(value, baseOrigin || 'https://example.test'); 31 return parsed.protocol === 'http:' || parsed.protocol === 'https:'; 32 } catch (error) { 33 return false; 34 } 35 } 36 37 function linkAttributes(href) { 38 var value = String(href || '').trim(); 39 if (value.charAt(0) === '/' && value.charAt(1) !== '/') { 40 return ''; 41 } 42 43 return ' target="_blank" rel="noopener noreferrer"'; 44 } 45 46 function formatAssistantMessage(text, baseOrigin) { 47 var safe = escapeHtml(text); 48 49 safe = safe.replace(/\[([^\]]+)\]\(([^)]+)\)/g, function (match, label, url) { 50 var href = String(url || '').trim(); 51 if (!isSafeHref(href, baseOrigin)) { 52 return escapeHtml(match); 53 } 54 55 return ( 56 '<a class="tm-chatbot__bubble-link" href="' + 57 escapeHtml(href) + 58 '"' + 59 linkAttributes(href) + 60 '>' + 61 label + 62 '</a>' 63 ); 64 }); 65 66 safe = safe.replace(/(^|[\s(])((https?:\/\/)[^\s<]+[^\s<.,);:!?])/gi, function (_, prefix, url) { 67 if (!isSafeHref(url, baseOrigin)) { 68 return prefix + url; 69 } 70 71 return ( 72 prefix + 73 '<a class="tm-chatbot__bubble-link" href="' + 74 escapeHtml(url) + 75 '"' + 76 linkAttributes(url) + 77 '>' + 78 url + 79 '</a>' 80 ); 81 }); 82 83 safe = safe.replace(/(^|[\s>:])(\/[a-z0-9][a-z0-9/_-]{6,}[a-z0-9]\/?)/gi, function (_, prefix, path) { 84 if (!isSafeHref(path, baseOrigin)) { 85 return prefix + path; 86 } 87 88 return ( 89 prefix + 90 '<a class="tm-chatbot__bubble-link" href="' + 91 escapeHtml(path) + 92 '"' + 93 linkAttributes(path) + 94 '>' + 95 path + 96 '</a>' 97 ); 98 }); 99 100 safe = safe.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>'); 101 safe = safe.replace(/(^|[^\s*])\*([^*\n]+)\*(?!\*)/g, '$1<em>$2</em>'); 102 103 return safe; 104 } 105 106 var api = { 107 escapeHtml: escapeHtml, 108 isSafeHref: isSafeHref, 109 formatAssistantMessage: formatAssistantMessage 110 }; 111 112 root.TmChatbotMessageFormat = api; 113 114 if (typeof module !== 'undefined' && module.exports) { 115 module.exports = api; 116 } 117})(typeof window !== 'undefined' ? window : globalThis);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.