this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Safeguard for unknown HTML cases in status content

1. Mark up code blocks first because anything inside code blocks cannot be "enhanced".
2. Set default rejects for elements that don't need to be "enhanced". Not a complete list but best-effort. Probably allowlist works better than blocklist, but some content doesn't even start with any parent nodes.

+67 -17
+67 -17
src/utils/enhance-content.js
··· 29 29 node.replaceWith(...nodes); 30 30 }); 31 31 32 - // INLINE CODE 33 - // =========== 34 - // Convert `code` to <code>code</code> 35 - textNodes = extractTextNodes(dom); 36 - textNodes.forEach((node) => { 37 - let html = node.nodeValue.replace(/</g, '&lt;').replace(/>/g, '&gt;'); 38 - if (/`[^`]+`/g.test(html)) { 39 - html = html.replaceAll(/(`[^]+?`)/g, '<code>$1</code>'); 40 - } 41 - fauxDiv.innerHTML = html; 42 - const nodes = Array.from(fauxDiv.childNodes); 43 - node.replaceWith(...nodes); 44 - }); 45 - 46 32 // CODE BLOCKS 47 33 // =========== 48 34 // Convert ```code``` to <pre><code>code</code></pre> ··· 57 43 block.replaceWith(pre); 58 44 }); 59 45 46 + // INLINE CODE 47 + // =========== 48 + // Convert `code` to <code>code</code> 49 + textNodes = extractTextNodes(dom); 50 + textNodes.forEach((node) => { 51 + let html = node.nodeValue.replace(/</g, '&lt;').replace(/>/g, '&gt;'); 52 + if (/`[^`]+`/g.test(html)) { 53 + html = html.replaceAll(/(`[^]+?`)/g, '<code>$1</code>'); 54 + } 55 + fauxDiv.innerHTML = html; 56 + const nodes = Array.from(fauxDiv.childNodes); 57 + node.replaceWith(...nodes); 58 + }); 59 + 60 60 // TWITTER USERNAMES 61 61 // ================= 62 62 // Convert @username@twitter.com to <a href="https://twitter.com/username">@username@twitter.com</a> 63 - textNodes = extractTextNodes(dom); 63 + textNodes = extractTextNodes(dom, { 64 + rejectFilter: ['A'], 65 + }); 64 66 textNodes.forEach((node) => { 65 67 let html = node.nodeValue.replace(/</g, '&lt;').replace(/>/g, '&gt;'); 66 68 if (/@[a-zA-Z0-9_]+@twitter\.com/g.test(html)) { ··· 83 85 return enhancedContent; 84 86 } 85 87 86 - function extractTextNodes(dom) { 88 + const defaultRejectFilter = [ 89 + // Document metadata 90 + 'STYLE', 91 + // Image and multimedia 92 + 'IMG', 93 + 'VIDEO', 94 + 'AUDIO', 95 + 'AREA', 96 + 'MAP', 97 + 'TRACK', 98 + // Embedded content 99 + 'EMBED', 100 + 'IFRAME', 101 + 'OBJECT', 102 + 'PICTURE', 103 + 'PORTAL', 104 + 'SOURCE', 105 + // SVG and MathML 106 + 'SVG', 107 + 'MATH', 108 + // Scripting 109 + 'CANVAS', 110 + 'NOSCRIPT', 111 + 'SCRIPT', 112 + // Forms 113 + 'INPUT', 114 + 'OPTION', 115 + 'TEXTAREA', 116 + // Web Components 117 + 'SLOT', 118 + 'TEMPLATE', 119 + ]; 120 + const defaultRejectFilterMap = Object.fromEntries( 121 + defaultRejectFilter.map((nodeName) => [nodeName, true]), 122 + ); 123 + function extractTextNodes(dom, opts = {}) { 87 124 const textNodes = []; 88 125 const walk = document.createTreeWalker( 89 126 dom, 90 127 NodeFilter.SHOW_TEXT, 91 - null, 128 + { 129 + acceptNode(node) { 130 + if (defaultRejectFilterMap[node.parentNode.nodeName]) { 131 + return NodeFilter.FILTER_REJECT; 132 + } 133 + if ( 134 + opts.rejectFilter && 135 + opts.rejectFilter.includes(node.parentNode.nodeName) 136 + ) { 137 + return NodeFilter.FILTER_REJECT; 138 + } 139 + return NodeFilter.FILTER_ACCEPT; 140 + }, 141 + }, 92 142 false, 93 143 ); 94 144 let node;