1function escapePreviewHtml(value) { 2 const element = document.createElement('div'); 3 element.textContent = value == null ? '' : String(value); 4 return element.innerHTML; 5} 6 7function truncateDescriptionPreview(html, maxWords) { 8 const source = document.createElement('div'); 9 source.innerHTML = html || ''; 10 11 source.querySelectorAll('*').forEach(function (element) { 12 const allowedTags = ['A', 'P', 'BR', 'STRONG', 'B', 'EM', 'I', 'U', 'UL', 'OL', 'LI', 'H1', 'H2']; 13 if (!allowedTags.includes(element.tagName)) { 14 element.replaceWith(...element.childNodes); 15 return; 16 } 17 18 if (element.tagName === 'A') { 19 const href = element.getAttribute('href') || ''; 20 [...element.attributes].forEach(attribute => element.removeAttribute(attribute.name)); 21 if (/^(https?:\/\/|mailto:|\/|\.\/|\.\.\/)/i.test(href)) { 22 element.setAttribute('href', href); 23 element.setAttribute('target', '_blank'); 24 element.setAttribute('rel', 'noopener noreferrer'); 25 } else { 26 element.replaceWith(...element.childNodes); 27 } 28 } else { 29 [...element.attributes].forEach(attribute => element.removeAttribute(attribute.name)); 30 } 31 }); 32 33 let wordsLeft = maxWords; 34 const walker = document.createTreeWalker(source, NodeFilter.SHOW_TEXT); 35 const textNodes = []; 36 while (walker.nextNode()) textNodes.push(walker.currentNode); 37 38 for (const node of textNodes) { 39 if (wordsLeft <= 0) { 40 node.remove(); 41 continue; 42 } 43 const words = node.textContent.trim().split(/\s+/).filter(Boolean); 44 if (words.length > wordsLeft) { 45 node.textContent = words.slice(0, wordsLeft).join(' ') + 'â¦'; 46 wordsLeft = 0; 47 } else { 48 wordsLeft -= words.length; 49 } 50 } 51 52 return source.innerHTML; 53}
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.