Skip to content

Instantly share code, notes, and snippets.

@harmakit
Created December 14, 2023 17:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save harmakit/dfb54d833e20e05c9aac9ef0241742e8 to your computer and use it in GitHub Desktop.
Save harmakit/dfb54d833e20e05c9aac9ef0241742e8 to your computer and use it in GitHub Desktop.
Obsidian Clipper Bookmarklet – Safari (both desktop and mobile)
0. Install "Obsidian Advanced URI" plugin and (optionally) "Local Images Plus" to save images from clipped pages locally
1. Create bookmark with from any url
2. Edit address to:
```
javascript:(function()%7B%2F*%20Optional%20vault%20name%20*%2F%0Aconst%20vault%20%3D%20%22%22%3B%0A%0A%2F*%20Optional%20folder%20name%20such%20as%20%22Clippings%2F%22%20*%2F%0Aconst%20folder%20%3D%20%22%2B%2F%D0%A1%D0%BE%D1%85%D1%80%D0%B0%D0%BD%D0%B5%D0%BD%D0%BD%D0%BE%D0%B5%2F%22%3B%0A%0A%2F*%20Optional%20tags%20%20*%2F%0Alet%20tags%20%3D%20%22clippings%22%3B%0A%0A%0A%0A%2F*%0A%20*%0A%20*%20Embed%20turndown%406.0.0%20to%20work%20around%20content%20security%20policies%20breaking%20import%20statements%0A%20*%0A%20*%2F%0A%0Afunction%20extend%20(destination)%20%7B%0A%20%20for%20(var%20i%20%3D%201%3B%20i%20%3C%20arguments.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20var%20source%20%3D%20arguments%5Bi%5D%3B%0A%20%20%20%20for%20(var%20key%20in%20source)%20%7B%0A%20%20%20%20%20%20if%20(source.hasOwnProperty(key))%20destination%5Bkey%5D%20%3D%20source%5Bkey%5D%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20return%20destination%0A%7D%0A%0Afunction%20repeat%20(character%2C%20count)%20%7B%0A%20%20return%20Array(count%20%2B%201).join(character)%0A%7D%0A%0Avar%20blockElements%20%3D%20%5B%0A%20%20'address'%2C%20'article'%2C%20'aside'%2C%20'audio'%2C%20'blockquote'%2C%20'body'%2C%20'canvas'%2C%0A%20%20'center'%2C%20'dd'%2C%20'dir'%2C%20'div'%2C%20'dl'%2C%20'dt'%2C%20'fieldset'%2C%20'figcaption'%2C%0A%20%20'figure'%2C%20'footer'%2C%20'form'%2C%20'frameset'%2C%20'h1'%2C%20'h2'%2C%20'h3'%2C%20'h4'%2C%20'h5'%2C%20'h6'%2C%0A%20%20'header'%2C%20'hgroup'%2C%20'hr'%2C%20'html'%2C%20'isindex'%2C%20'li'%2C%20'main'%2C%20'menu'%2C%20'nav'%2C%0A%20%20'noframes'%2C%20'noscript'%2C%20'ol'%2C%20'output'%2C%20'p'%2C%20'pre'%2C%20'section'%2C%20'table'%2C%0A%20%20'tbody'%2C%20'td'%2C%20'tfoot'%2C%20'th'%2C%20'thead'%2C%20'tr'%2C%20'ul'%0A%5D%3B%0A%0Afunction%20isBlock%20(node)%20%7B%0A%20%20return%20blockElements.indexOf(node.nodeName.toLowerCase())%20!%3D%3D%20-1%0A%7D%0A%0Avar%20voidElements%20%3D%20%5B%0A%20%20'area'%2C%20'base'%2C%20'br'%2C%20'col'%2C%20'command'%2C%20'embed'%2C%20'hr'%2C%20'img'%2C%20'input'%2C%0A%20%20'keygen'%2C%20'link'%2C%20'meta'%2C%20'param'%2C%20'source'%2C%20'track'%2C%20'wbr'%0A%5D%3B%0A%0Afunction%20isVoid%20(node)%20%7B%0A%20%20return%20voidElements.indexOf(node.nodeName.toLowerCase())%20!%3D%3D%20-1%0A%7D%0A%0Avar%20voidSelector%20%3D%20voidElements.join()%3B%0Afunction%20hasVoid%20(node)%20%7B%0A%20%20return%20node.querySelector%20%26%26%20node.querySelector(voidSelector)%0A%7D%0A%0Avar%20rules%20%3D%20%7B%7D%3B%0A%0Arules.paragraph%20%3D%20%7B%0A%20%20filter%3A%20'p'%2C%0A%0A%20%20replacement%3A%20function%20(content)%20%7B%0A%20%20%20%20return%20'%5Cn%5Cn'%20%2B%20content%20%2B%20'%5Cn%5Cn'%0A%20%20%7D%0A%7D%3B%0A%0Arules.lineBreak%20%3D%20%7B%0A%20%20filter%3A%20'br'%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20return%20options.br%20%2B%20'%5Cn'%0A%20%20%7D%0A%7D%3B%0A%0Arules.heading%20%3D%20%7B%0A%20%20filter%3A%20%5B'h1'%2C%20'h2'%2C%20'h3'%2C%20'h4'%2C%20'h5'%2C%20'h6'%5D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20var%20hLevel%20%3D%20Number(node.nodeName.charAt(1))%3B%0A%0A%20%20%20%20if%20(options.headingStyle%20%3D%3D%3D%20'setext'%20%26%26%20hLevel%20%3C%203)%20%7B%0A%20%20%20%20%20%20var%20underline%20%3D%20repeat((hLevel%20%3D%3D%3D%201%20%3F%20'%3D'%20%3A%20'-')%2C%20content.length)%3B%0A%20%20%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20'%5Cn%5Cn'%20%2B%20content%20%2B%20'%5Cn'%20%2B%20underline%20%2B%20'%5Cn%5Cn'%0A%20%20%20%20%20%20)%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20'%5Cn%5Cn'%20%2B%20repeat('%23'%2C%20hLevel)%20%2B%20'%20'%20%2B%20content%20%2B%20'%5Cn%5Cn'%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%3B%0A%0Arules.blockquote%20%3D%20%7B%0A%20%20filter%3A%20'blockquote'%2C%0A%0A%20%20replacement%3A%20function%20(content)%20%7B%0A%20%20%20%20content%20%3D%20content.replace(%2F%5E%5Cn%2B%7C%5Cn%2B%24%2Fg%2C%20'')%3B%0A%20%20%20%20content%20%3D%20content.replace(%2F%5E%2Fgm%2C%20'%3E%20')%3B%0A%20%20%20%20return%20'%5Cn%5Cn'%20%2B%20content%20%2B%20'%5Cn%5Cn'%0A%20%20%7D%0A%7D%3B%0A%0Arules.list%20%3D%20%7B%0A%20%20filter%3A%20%5B'ul'%2C%20'ol'%5D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20var%20parent%20%3D%20node.parentNode%3B%0A%20%20%20%20if%20(parent.nodeName%20%3D%3D%3D%20'LI'%20%26%26%20parent.lastElementChild%20%3D%3D%3D%20node)%20%7B%0A%20%20%20%20%20%20return%20'%5Cn'%20%2B%20content%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20return%20'%5Cn%5Cn'%20%2B%20content%20%2B%20'%5Cn%5Cn'%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%3B%0A%0Arules.listItem%20%3D%20%7B%0A%20%20filter%3A%20'li'%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20content%20%3D%20content%0A%20%20%20%20%20%20.replace(%2F%5E%5Cn%2B%2F%2C%20'')%20%2F%2F%20remove%20leading%20newlines%0A%20%20%20%20%20%20.replace(%2F%5Cn%2B%24%2F%2C%20'%5Cn')%20%2F%2F%20replace%20trailing%20newlines%20with%20just%20a%20single%20one%0A%20%20%20%20%20%20.replace(%2F%5Cn%2Fgm%2C%20'%5Cn%20%20%20%20')%3B%20%2F%2F%20indent%0A%20%20%20%20var%20prefix%20%3D%20options.bulletListMarker%20%2B%20'%20%20%20'%3B%0A%20%20%20%20var%20parent%20%3D%20node.parentNode%3B%0A%20%20%20%20if%20(parent.nodeName%20%3D%3D%3D%20'OL')%20%7B%0A%20%20%20%20%20%20var%20start%20%3D%20parent.getAttribute('start')%3B%0A%20%20%20%20%20%20var%20index%20%3D%20Array.prototype.indexOf.call(parent.children%2C%20node)%3B%0A%20%20%20%20%20%20prefix%20%3D%20(start%20%3F%20Number(start)%20%2B%20index%20%3A%20index%20%2B%201)%20%2B%20'.%20%20'%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20prefix%20%2B%20content%20%2B%20(node.nextSibling%20%26%26%20!%2F%5Cn%24%2F.test(content)%20%3F%20'%5Cn'%20%3A%20'')%0A%20%20%20%20)%0A%20%20%7D%0A%7D%3B%0A%0Arules.indentedCodeBlock%20%3D%20%7B%0A%20%20filter%3A%20function%20(node%2C%20options)%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20options.codeBlockStyle%20%3D%3D%3D%20'indented'%20%26%26%0A%20%20%20%20%20%20node.nodeName%20%3D%3D%3D%20'PRE'%20%26%26%0A%20%20%20%20%20%20node.firstChild%20%26%26%0A%20%20%20%20%20%20node.firstChild.nodeName%20%3D%3D%3D%20'CODE'%0A%20%20%20%20)%0A%20%20%7D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20'%5Cn%5Cn%20%20%20%20'%20%2B%0A%20%20%20%20%20%20node.firstChild.textContent.replace(%2F%5Cn%2Fg%2C%20'%5Cn%20%20%20%20')%20%2B%0A%20%20%20%20%20%20'%5Cn%5Cn'%0A%20%20%20%20)%0A%20%20%7D%0A%7D%3B%0A%0Arules.fencedCodeBlock%20%3D%20%7B%0A%20%20filter%3A%20function%20(node%2C%20options)%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20options.codeBlockStyle%20%3D%3D%3D%20'fenced'%20%26%26%0A%20%20%20%20%20%20node.nodeName%20%3D%3D%3D%20'PRE'%20%26%26%0A%20%20%20%20%20%20node.firstChild%20%26%26%0A%20%20%20%20%20%20node.firstChild.nodeName%20%3D%3D%3D%20'CODE'%0A%20%20%20%20)%0A%20%20%7D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20var%20className%20%3D%20node.firstChild.className%20%7C%7C%20''%3B%0A%20%20%20%20var%20language%20%3D%20(className.match(%2Flanguage-(%5CS%2B)%2F)%20%7C%7C%20%5Bnull%2C%20''%5D)%5B1%5D%3B%0A%20%20%20%20var%20code%20%3D%20node.firstChild.textContent%3B%0A%0A%20%20%20%20var%20fenceChar%20%3D%20options.fence.charAt(0)%3B%0A%20%20%20%20var%20fenceSize%20%3D%203%3B%0A%20%20%20%20var%20fenceInCodeRegex%20%3D%20new%20RegExp('%5E'%20%2B%20fenceChar%20%2B%20'%7B3%2C%7D'%2C%20'gm')%3B%0A%0A%20%20%20%20var%20match%3B%0A%20%20%20%20while%20((match%20%3D%20fenceInCodeRegex.exec(code)))%20%7B%0A%20%20%20%20%20%20if%20(match%5B0%5D.length%20%3E%3D%20fenceSize)%20%7B%0A%20%20%20%20%20%20%20%20fenceSize%20%3D%20match%5B0%5D.length%20%2B%201%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20var%20fence%20%3D%20repeat(fenceChar%2C%20fenceSize)%3B%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20'%5Cn%5Cn'%20%2B%20fence%20%2B%20language%20%2B%20'%5Cn'%20%2B%0A%20%20%20%20%20%20code.replace(%2F%5Cn%24%2F%2C%20'')%20%2B%0A%20%20%20%20%20%20'%5Cn'%20%2B%20fence%20%2B%20'%5Cn%5Cn'%0A%20%20%20%20)%0A%20%20%7D%0A%7D%3B%0A%0Arules.horizontalRule%20%3D%20%7B%0A%20%20filter%3A%20'hr'%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20return%20'%5Cn%5Cn'%20%2B%20options.hr%20%2B%20'%5Cn%5Cn'%0A%20%20%7D%0A%7D%3B%0A%0Arules.inlineLink%20%3D%20%7B%0A%20%20filter%3A%20function%20(node%2C%20options)%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20options.linkStyle%20%3D%3D%3D%20'inlined'%20%26%26%0A%20%20%20%20%20%20node.nodeName%20%3D%3D%3D%20'A'%20%26%26%0A%20%20%20%20%20%20node.getAttribute('href')%0A%20%20%20%20)%0A%20%20%7D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20var%20href%20%3D%20node.getAttribute('href')%3B%0A%20%20%20%20var%20title%20%3D%20node.title%20%3F%20'%20%22'%20%2B%20node.title%20%2B%20'%22'%20%3A%20''%3B%0A%20%20%20%20return%20'%5B'%20%2B%20content%20%2B%20'%5D('%20%2B%20href%20%2B%20title%20%2B%20')'%0A%20%20%7D%0A%7D%3B%0A%0Arules.referenceLink%20%3D%20%7B%0A%20%20filter%3A%20function%20(node%2C%20options)%20%7B%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20options.linkStyle%20%3D%3D%3D%20'referenced'%20%26%26%0A%20%20%20%20%20%20node.nodeName%20%3D%3D%3D%20'A'%20%26%26%0A%20%20%20%20%20%20node.getAttribute('href')%0A%20%20%20%20)%0A%20%20%7D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20var%20href%20%3D%20node.getAttribute('href')%3B%0A%20%20%20%20var%20title%20%3D%20node.title%20%3F%20'%20%22'%20%2B%20node.title%20%2B%20'%22'%20%3A%20''%3B%0A%20%20%20%20var%20replacement%3B%0A%20%20%20%20var%20reference%3B%0A%0A%20%20%20%20switch%20(options.linkReferenceStyle)%20%7B%0A%20%20%20%20%20%20case%20'collapsed'%3A%0A%20%20%20%20%20%20%20%20replacement%20%3D%20'%5B'%20%2B%20content%20%2B%20'%5D%5B%5D'%3B%0A%20%20%20%20%20%20%20%20reference%20%3D%20'%5B'%20%2B%20content%20%2B%20'%5D%3A%20'%20%2B%20href%20%2B%20title%3B%0A%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20case%20'shortcut'%3A%0A%20%20%20%20%20%20%20%20replacement%20%3D%20'%5B'%20%2B%20content%20%2B%20'%5D'%3B%0A%20%20%20%20%20%20%20%20reference%20%3D%20'%5B'%20%2B%20content%20%2B%20'%5D%3A%20'%20%2B%20href%20%2B%20title%3B%0A%20%20%20%20%20%20%20%20break%0A%20%20%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20var%20id%20%3D%20this.references.length%20%2B%201%3B%0A%20%20%20%20%20%20%20%20replacement%20%3D%20'%5B'%20%2B%20content%20%2B%20'%5D%5B'%20%2B%20id%20%2B%20'%5D'%3B%0A%20%20%20%20%20%20%20%20reference%20%3D%20'%5B'%20%2B%20id%20%2B%20'%5D%3A%20'%20%2B%20href%20%2B%20title%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20this.references.push(reference)%3B%0A%20%20%20%20return%20replacement%0A%20%20%7D%2C%0A%0A%20%20references%3A%20%5B%5D%2C%0A%0A%20%20append%3A%20function%20(options)%20%7B%0A%20%20%20%20var%20references%20%3D%20''%3B%0A%20%20%20%20if%20(this.references.length)%20%7B%0A%20%20%20%20%20%20references%20%3D%20'%5Cn%5Cn'%20%2B%20this.references.join('%5Cn')%20%2B%20'%5Cn%5Cn'%3B%0A%20%20%20%20%20%20this.references%20%3D%20%5B%5D%3B%20%2F%2F%20Reset%20references%0A%20%20%20%20%7D%0A%20%20%20%20return%20references%0A%20%20%7D%0A%7D%3B%0A%0Arules.emphasis%20%3D%20%7B%0A%20%20filter%3A%20%5B'em'%2C%20'i'%5D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20if%20(!content.trim())%20return%20''%0A%20%20%20%20return%20options.emDelimiter%20%2B%20content%20%2B%20options.emDelimiter%0A%20%20%7D%0A%7D%3B%0A%0Arules.strong%20%3D%20%7B%0A%20%20filter%3A%20%5B'strong'%2C%20'b'%5D%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node%2C%20options)%20%7B%0A%20%20%20%20if%20(!content.trim())%20return%20''%0A%20%20%20%20return%20options.strongDelimiter%20%2B%20content%20%2B%20options.strongDelimiter%0A%20%20%7D%0A%7D%3B%0A%0Arules.code%20%3D%20%7B%0A%20%20filter%3A%20function%20(node)%20%7B%0A%20%20%20%20var%20hasSiblings%20%3D%20node.previousSibling%20%7C%7C%20node.nextSibling%3B%0A%20%20%20%20var%20isCodeBlock%20%3D%20node.parentNode.nodeName%20%3D%3D%3D%20'PRE'%20%26%26%20!hasSiblings%3B%0A%0A%20%20%20%20return%20node.nodeName%20%3D%3D%3D%20'CODE'%20%26%26%20!isCodeBlock%0A%20%20%7D%2C%0A%0A%20%20replacement%3A%20function%20(content)%20%7B%0A%20%20%20%20if%20(!content.trim())%20return%20''%0A%0A%20%20%20%20var%20delimiter%20%3D%20'%60'%3B%0A%20%20%20%20var%20leadingSpace%20%3D%20''%3B%0A%20%20%20%20var%20trailingSpace%20%3D%20''%3B%0A%20%20%20%20var%20matches%20%3D%20content.match(%2F%60%2B%2Fgm)%3B%0A%20%20%20%20if%20(matches)%20%7B%0A%20%20%20%20%20%20if%20(%2F%5E%60%2F.test(content))%20leadingSpace%20%3D%20'%20'%3B%0A%20%20%20%20%20%20if%20(%2F%60%24%2F.test(content))%20trailingSpace%20%3D%20'%20'%3B%0A%20%20%20%20%20%20while%20(matches.indexOf(delimiter)%20!%3D%3D%20-1)%20delimiter%20%3D%20delimiter%20%2B%20'%60'%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20delimiter%20%2B%20leadingSpace%20%2B%20content%20%2B%20trailingSpace%20%2B%20delimiter%0A%20%20%7D%0A%7D%3B%0A%0Arules.image%20%3D%20%7B%0A%20%20filter%3A%20'img'%2C%0A%0A%20%20replacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20var%20alt%20%3D%20node.alt%20%7C%7C%20''%3B%0A%20%20%20%20var%20src%20%3D%20node.getAttribute('src')%20%7C%7C%20''%3B%0A%20%20%20%20var%20title%20%3D%20node.title%20%7C%7C%20''%3B%0A%20%20%20%20var%20titlePart%20%3D%20title%20%3F%20'%20%22'%20%2B%20title%20%2B%20'%22'%20%3A%20''%3B%0A%20%20%20%20return%20src%20%3F%20'!%5B'%20%2B%20alt%20%2B%20'%5D'%20%2B%20'('%20%2B%20src%20%2B%20titlePart%20%2B%20')'%20%3A%20''%0A%20%20%7D%0A%7D%3B%0A%0A%2F**%0A%20*%20Manages%20a%20collection%20of%20rules%20used%20to%20convert%20HTML%20to%20Markdown%0A%20*%2F%0A%0Afunction%20Rules%20(options)%20%7B%0A%20%20this.options%20%3D%20options%3B%0A%20%20this._keep%20%3D%20%5B%5D%3B%0A%20%20this._remove%20%3D%20%5B%5D%3B%0A%0A%20%20this.blankRule%20%3D%20%7B%0A%20%20%20%20replacement%3A%20options.blankReplacement%0A%20%20%7D%3B%0A%0A%20%20this.keepReplacement%20%3D%20options.keepReplacement%3B%0A%0A%20%20this.defaultRule%20%3D%20%7B%0A%20%20%20%20replacement%3A%20options.defaultReplacement%0A%20%20%7D%3B%0A%0A%20%20this.array%20%3D%20%5B%5D%3B%0A%20%20for%20(var%20key%20in%20options.rules)%20this.array.push(options.rules%5Bkey%5D)%3B%0A%7D%0A%0ARules.prototype%20%3D%20%7B%0A%20%20add%3A%20function%20(key%2C%20rule)%20%7B%0A%20%20%20%20this.array.unshift(rule)%3B%0A%20%20%7D%2C%0A%0A%20%20keep%3A%20function%20(filter)%20%7B%0A%20%20%20%20this._keep.unshift(%7B%0A%20%20%20%20%20%20filter%3A%20filter%2C%0A%20%20%20%20%20%20replacement%3A%20this.keepReplacement%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20remove%3A%20function%20(filter)%20%7B%0A%20%20%20%20this._remove.unshift(%7B%0A%20%20%20%20%20%20filter%3A%20filter%2C%0A%20%20%20%20%20%20replacement%3A%20function%20()%20%7B%0A%20%20%20%20%20%20%20%20return%20''%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20forNode%3A%20function%20(node)%20%7B%0A%20%20%20%20if%20(node.isBlank)%20return%20this.blankRule%0A%20%20%20%20var%20rule%3B%0A%0A%20%20%20%20if%20((rule%20%3D%20findRule(this.array%2C%20node%2C%20this.options)))%20return%20rule%0A%20%20%20%20if%20((rule%20%3D%20findRule(this._keep%2C%20node%2C%20this.options)))%20return%20rule%0A%20%20%20%20if%20((rule%20%3D%20findRule(this._remove%2C%20node%2C%20this.options)))%20return%20rule%0A%0A%20%20%20%20return%20this.defaultRule%0A%20%20%7D%2C%0A%0A%20%20forEach%3A%20function%20(fn)%20%7B%0A%20%20%20%20for%20(var%20i%20%3D%200%3B%20i%20%3C%20this.array.length%3B%20i%2B%2B)%20fn(this.array%5Bi%5D%2C%20i)%3B%0A%20%20%7D%0A%7D%3B%0A%0Afunction%20findRule%20(rules%2C%20node%2C%20options)%20%7B%0A%20%20for%20(var%20i%20%3D%200%3B%20i%20%3C%20rules.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20var%20rule%20%3D%20rules%5Bi%5D%3B%0A%20%20%20%20if%20(filterValue(rule%2C%20node%2C%20options))%20return%20rule%0A%20%20%7D%0A%20%20return%20void%200%0A%7D%0A%0Afunction%20filterValue%20(rule%2C%20node%2C%20options)%20%7B%0A%20%20var%20filter%20%3D%20rule.filter%3B%0A%20%20if%20(typeof%20filter%20%3D%3D%3D%20'string')%20%7B%0A%20%20%20%20if%20(filter%20%3D%3D%3D%20node.nodeName.toLowerCase())%20return%20true%0A%20%20%7D%20else%20if%20(Array.isArray(filter))%20%7B%0A%20%20%20%20if%20(filter.indexOf(node.nodeName.toLowerCase())%20%3E%20-1)%20return%20true%0A%20%20%7D%20else%20if%20(typeof%20filter%20%3D%3D%3D%20'function')%20%7B%0A%20%20%20%20if%20(filter.call(rule%2C%20node%2C%20options))%20return%20true%0A%20%20%7D%20else%20%7B%0A%20%20%20%20throw%20new%20TypeError('%60filter%60%20needs%20to%20be%20a%20string%2C%20array%2C%20or%20function')%0A%20%20%7D%0A%7D%0A%0A%2F**%0A%20*%20The%20collapseWhitespace%20function%20is%20adapted%20from%20collapse-whitespace%0A%20*%20by%20Luc%20Thevenard.%0A%20*%0A%20*%20The%20MIT%20License%20(MIT)%0A%20*%0A%20*%20Copyright%20(c)%202014%20Luc%20Thevenard%20%3Clucthevenard%40gmail.com%3E%0A%20*%0A%20*%20Permission%20is%20hereby%20granted%2C%20free%20of%20charge%2C%20to%20any%20person%20obtaining%20a%20copy%0A%20*%20of%20this%20software%20and%20associated%20documentation%20files%20(the%20%22Software%22)%2C%20to%20deal%0A%20*%20in%20the%20Software%20without%20restriction%2C%20including%20without%20limitation%20the%20rights%0A%20*%20to%20use%2C%20copy%2C%20modify%2C%20merge%2C%20publish%2C%20distribute%2C%20sublicense%2C%20and%2For%20sell%0A%20*%20copies%20of%20the%20Software%2C%20and%20to%20permit%20persons%20to%20whom%20the%20Software%20is%0A%20*%20furnished%20to%20do%20so%2C%20subject%20to%20the%20following%20conditions%3A%0A%20*%0A%20*%20The%20above%20copyright%20notice%20and%20this%20permission%20notice%20shall%20be%20included%20in%0A%20*%20all%20copies%20or%20substantial%20portions%20of%20the%20Software.%0A%20*%0A%20*%20THE%20SOFTWARE%20IS%20PROVIDED%20%22AS%20IS%22%2C%20WITHOUT%20WARRANTY%20OF%20ANY%20KIND%2C%20EXPRESS%20OR%0A%20*%20IMPLIED%2C%20INCLUDING%20BUT%20NOT%20LIMITED%20TO%20THE%20WARRANTIES%20OF%20MERCHANTABILITY%2C%0A%20*%20FITNESS%20FOR%20A%20PARTICULAR%20PURPOSE%20AND%20NONINFRINGEMENT.%20IN%20NO%20EVENT%20SHALL%20THE%0A%20*%20AUTHORS%20OR%20COPYRIGHT%20HOLDERS%20BE%20LIABLE%20FOR%20ANY%20CLAIM%2C%20DAMAGES%20OR%20OTHER%0A%20*%20LIABILITY%2C%20WHETHER%20IN%20AN%20ACTION%20OF%20CONTRACT%2C%20TORT%20OR%20OTHERWISE%2C%20ARISING%20FROM%2C%0A%20*%20OUT%20OF%20OR%20IN%20CONNECTION%20WITH%20THE%20SOFTWARE%20OR%20THE%20USE%20OR%20OTHER%20DEALINGS%20IN%0A%20*%20THE%20SOFTWARE.%0A%20*%2F%0A%0A%2F**%0A%20*%20collapseWhitespace(options)%20removes%20extraneous%20whitespace%20from%20an%20the%20given%20element.%0A%20*%0A%20*%20%40param%20%7BObject%7D%20options%0A%20*%2F%0Afunction%20collapseWhitespace%20(options)%20%7B%0A%20%20var%20element%20%3D%20options.element%3B%0A%20%20var%20isBlock%20%3D%20options.isBlock%3B%0A%20%20var%20isVoid%20%3D%20options.isVoid%3B%0A%20%20var%20isPre%20%3D%20options.isPre%20%7C%7C%20function%20(node)%20%7B%0A%20%20%20%20return%20node.nodeName%20%3D%3D%3D%20'PRE'%0A%20%20%7D%3B%0A%0A%20%20if%20(!element.firstChild%20%7C%7C%20isPre(element))%20return%0A%0A%20%20var%20prevText%20%3D%20null%3B%0A%20%20var%20prevVoid%20%3D%20false%3B%0A%0A%20%20var%20prev%20%3D%20null%3B%0A%20%20var%20node%20%3D%20next(prev%2C%20element%2C%20isPre)%3B%0A%0A%20%20while%20(node%20!%3D%3D%20element)%20%7B%0A%20%20%20%20if%20(node.nodeType%20%3D%3D%3D%203%20%7C%7C%20node.nodeType%20%3D%3D%3D%204)%20%7B%20%2F%2F%20Node.TEXT_NODE%20or%20Node.CDATA_SECTION_NODE%0A%20%20%20%20%20%20var%20text%20%3D%20node.data.replace(%2F%5B%20%5Cr%5Cn%5Ct%5D%2B%2Fg%2C%20'%20')%3B%0A%0A%20%20%20%20%20%20if%20((!prevText%20%7C%7C%20%2F%20%24%2F.test(prevText.data))%20%26%26%0A%20%20%20%20%20%20%20%20%20%20!prevVoid%20%26%26%20text%5B0%5D%20%3D%3D%3D%20'%20')%20%7B%0A%20%20%20%20%20%20%20%20text%20%3D%20text.substr(1)%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20%60text%60%20might%20be%20empty%20at%20this%20point.%0A%20%20%20%20%20%20if%20(!text)%20%7B%0A%20%20%20%20%20%20%20%20node%20%3D%20remove(node)%3B%0A%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20node.data%20%3D%20text%3B%0A%0A%20%20%20%20%20%20prevText%20%3D%20node%3B%0A%20%20%20%20%7D%20else%20if%20(node.nodeType%20%3D%3D%3D%201)%20%7B%20%2F%2F%20Node.ELEMENT_NODE%0A%20%20%20%20%20%20if%20(isBlock(node)%20%7C%7C%20node.nodeName%20%3D%3D%3D%20'BR')%20%7B%0A%20%20%20%20%20%20%20%20if%20(prevText)%20%7B%0A%20%20%20%20%20%20%20%20%20%20prevText.data%20%3D%20prevText.data.replace(%2F%20%24%2F%2C%20'')%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20prevText%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20prevVoid%20%3D%20false%3B%0A%20%20%20%20%20%20%7D%20else%20if%20(isVoid(node))%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Avoid%20trimming%20space%20around%20non-block%2C%20non-BR%20void%20elements.%0A%20%20%20%20%20%20%20%20prevText%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20prevVoid%20%3D%20true%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20node%20%3D%20remove(node)%3B%0A%20%20%20%20%20%20continue%0A%20%20%20%20%7D%0A%0A%20%20%20%20var%20nextNode%20%3D%20next(prev%2C%20node%2C%20isPre)%3B%0A%20%20%20%20prev%20%3D%20node%3B%0A%20%20%20%20node%20%3D%20nextNode%3B%0A%20%20%7D%0A%0A%20%20if%20(prevText)%20%7B%0A%20%20%20%20prevText.data%20%3D%20prevText.data.replace(%2F%20%24%2F%2C%20'')%3B%0A%20%20%20%20if%20(!prevText.data)%20%7B%0A%20%20%20%20%20%20remove(prevText)%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A%0A%2F**%0A%20*%20remove(node)%20removes%20the%20given%20node%20from%20the%20DOM%20and%20returns%20the%0A%20*%20next%20node%20in%20the%20sequence.%0A%20*%0A%20*%20%40param%20%7BNode%7D%20node%0A%20*%20%40return%20%7BNode%7D%20node%0A%20*%2F%0Afunction%20remove%20(node)%20%7B%0A%20%20var%20next%20%3D%20node.nextSibling%20%7C%7C%20node.parentNode%3B%0A%0A%20%20node.parentNode.removeChild(node)%3B%0A%0A%20%20return%20next%0A%7D%0A%0A%2F**%0A%20*%20next(prev%2C%20current%2C%20isPre)%20returns%20the%20next%20node%20in%20the%20sequence%2C%20given%20the%0A%20*%20current%20and%20previous%20nodes.%0A%20*%0A%20*%20%40param%20%7BNode%7D%20prev%0A%20*%20%40param%20%7BNode%7D%20current%0A%20*%20%40param%20%7BFunction%7D%20isPre%0A%20*%20%40return%20%7BNode%7D%0A%20*%2F%0Afunction%20next%20(prev%2C%20current%2C%20isPre)%20%7B%0A%20%20if%20((prev%20%26%26%20prev.parentNode%20%3D%3D%3D%20current)%20%7C%7C%20isPre(current))%20%7B%0A%20%20%20%20return%20current.nextSibling%20%7C%7C%20current.parentNode%0A%20%20%7D%0A%0A%20%20return%20current.firstChild%20%7C%7C%20current.nextSibling%20%7C%7C%20current.parentNode%0A%7D%0A%0A%2F*%0A%20*%20Set%20up%20window%20for%20Node.js%0A%20*%2F%0A%0Avar%20root%20%3D%20(typeof%20window%20!%3D%3D%20'undefined'%20%3F%20window%20%3A%20%7B%7D)%3B%0A%0A%2F*%0A%20*%20Parsing%20HTML%20strings%0A%20*%2F%0A%0Afunction%20canParseHTMLNatively%20()%20%7B%0A%20%20var%20Parser%20%3D%20root.DOMParser%3B%0A%20%20var%20canParse%20%3D%20false%3B%0A%0A%20%20%2F%2F%20Adapted%20from%20https%3A%2F%2Fgist.github.com%2F1129031%0A%20%20%2F%2F%20Firefox%2FOpera%2FIE%20throw%20errors%20on%20unsupported%20types%0A%20%20try%20%7B%0A%20%20%20%20%2F%2F%20WebKit%20returns%20null%20on%20unsupported%20types%0A%20%20%20%20if%20(new%20Parser().parseFromString(''%2C%20'text%2Fhtml'))%20%7B%0A%20%20%20%20%20%20canParse%20%3D%20true%3B%0A%20%20%20%20%7D%0A%20%20%7D%20catch%20(e)%20%7B%7D%0A%0A%20%20return%20canParse%0A%7D%0A%0Afunction%20createHTMLParser%20()%20%7B%0A%20%20var%20Parser%20%3D%20function%20()%20%7B%7D%3B%0A%0A%20%20%7B%0A%20%20%20%20var%20JSDOM%20%3D%20require('jsdom').JSDOM%3B%0A%20%20%20%20Parser.prototype.parseFromString%20%3D%20function%20(string)%20%7B%0A%20%20%20%20%20%20return%20new%20JSDOM(string).window.document%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%20%20return%20Parser%0A%7D%0A%0Avar%20HTMLParser%20%3D%20canParseHTMLNatively()%20%3F%20root.DOMParser%20%3A%20createHTMLParser()%3B%0A%0Afunction%20RootNode%20(input)%20%7B%0A%20%20var%20root%3B%0A%20%20if%20(typeof%20input%20%3D%3D%3D%20'string')%20%7B%0A%20%20%20%20var%20doc%20%3D%20htmlParser().parseFromString(%0A%20%20%20%20%20%20%2F%2F%20DOM%20parsers%20arrange%20elements%20in%20the%20%3Chead%3E%20and%20%3Cbody%3E.%0A%20%20%20%20%20%20%2F%2F%20Wrapping%20in%20a%20custom%20element%20ensures%20elements%20are%20reliably%20arranged%20in%0A%20%20%20%20%20%20%2F%2F%20a%20single%20element.%0A%20%20%20%20%20%20'%3Cx-turndown%20id%3D%22turndown-root%22%3E'%20%2B%20input%20%2B%20'%3C%2Fx-turndown%3E'%2C%0A%20%20%20%20%20%20'text%2Fhtml'%0A%20%20%20%20)%3B%0A%20%20%20%20root%20%3D%20doc.getElementById('turndown-root')%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20root%20%3D%20input.cloneNode(true)%3B%0A%20%20%7D%0A%20%20collapseWhitespace(%7B%0A%20%20%20%20element%3A%20root%2C%0A%20%20%20%20isBlock%3A%20isBlock%2C%0A%20%20%20%20isVoid%3A%20isVoid%0A%20%20%7D)%3B%0A%0A%20%20return%20root%0A%7D%0A%0Avar%20_htmlParser%3B%0Afunction%20htmlParser%20()%20%7B%0A%20%20_htmlParser%20%3D%20_htmlParser%20%7C%7C%20new%20HTMLParser()%3B%0A%20%20return%20_htmlParser%0A%7D%0A%0Afunction%20Node%20(node)%20%7B%0A%20%20node.isBlock%20%3D%20isBlock(node)%3B%0A%20%20node.isCode%20%3D%20node.nodeName.toLowerCase()%20%3D%3D%3D%20'code'%20%7C%7C%20node.parentNode.isCode%3B%0A%20%20node.isBlank%20%3D%20isBlank(node)%3B%0A%20%20node.flankingWhitespace%20%3D%20flankingWhitespace(node)%3B%0A%20%20return%20node%0A%7D%0A%0Afunction%20isBlank%20(node)%20%7B%0A%20%20return%20(%0A%20%20%20%20%5B'A'%2C%20'TH'%2C%20'TD'%2C%20'IFRAME'%2C%20'SCRIPT'%2C%20'AUDIO'%2C%20'VIDEO'%5D.indexOf(node.nodeName)%20%3D%3D%3D%20-1%20%26%26%0A%20%20%20%20%2F%5E%5Cs*%24%2Fi.test(node.textContent)%20%26%26%0A%20%20%20%20!isVoid(node)%20%26%26%0A%20%20%20%20!hasVoid(node)%0A%20%20)%0A%7D%0A%0Afunction%20flankingWhitespace%20(node)%20%7B%0A%20%20var%20leading%20%3D%20''%3B%0A%20%20var%20trailing%20%3D%20''%3B%0A%0A%20%20if%20(!node.isBlock)%20%7B%0A%20%20%20%20var%20hasLeading%20%3D%20%2F%5E%5Cs%2F.test(node.textContent)%3B%0A%20%20%20%20var%20hasTrailing%20%3D%20%2F%5Cs%24%2F.test(node.textContent)%3B%0A%20%20%20%20var%20blankWithSpaces%20%3D%20node.isBlank%20%26%26%20hasLeading%20%26%26%20hasTrailing%3B%0A%0A%20%20%20%20if%20(hasLeading%20%26%26%20!isFlankedByWhitespace('left'%2C%20node))%20%7B%0A%20%20%20%20%20%20leading%20%3D%20'%20'%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(!blankWithSpaces%20%26%26%20hasTrailing%20%26%26%20!isFlankedByWhitespace('right'%2C%20node))%20%7B%0A%20%20%20%20%20%20trailing%20%3D%20'%20'%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%0A%20%20return%20%7B%20leading%3A%20leading%2C%20trailing%3A%20trailing%20%7D%0A%7D%0A%0Afunction%20isFlankedByWhitespace%20(side%2C%20node)%20%7B%0A%20%20var%20sibling%3B%0A%20%20var%20regExp%3B%0A%20%20var%20isFlanked%3B%0A%0A%20%20if%20(side%20%3D%3D%3D%20'left')%20%7B%0A%20%20%20%20sibling%20%3D%20node.previousSibling%3B%0A%20%20%20%20regExp%20%3D%20%2F%20%24%2F%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20sibling%20%3D%20node.nextSibling%3B%0A%20%20%20%20regExp%20%3D%20%2F%5E%20%2F%3B%0A%20%20%7D%0A%0A%20%20if%20(sibling)%20%7B%0A%20%20%20%20if%20(sibling.nodeType%20%3D%3D%3D%203)%20%7B%0A%20%20%20%20%20%20isFlanked%20%3D%20regExp.test(sibling.nodeValue)%3B%0A%20%20%20%20%7D%20else%20if%20(sibling.nodeType%20%3D%3D%3D%201%20%26%26%20!isBlock(sibling))%20%7B%0A%20%20%20%20%20%20isFlanked%20%3D%20regExp.test(sibling.textContent)%3B%0A%20%20%20%20%7D%0A%20%20%7D%0A%20%20return%20isFlanked%0A%7D%0A%0Avar%20reduce%20%3D%20Array.prototype.reduce%3B%0Avar%20leadingNewLinesRegExp%20%3D%20%2F%5E%5Cn*%2F%3B%0Avar%20trailingNewLinesRegExp%20%3D%20%2F%5Cn*%24%2F%3B%0Avar%20escapes%20%3D%20%5B%0A%20%20%5B%2F%5C%5C%2Fg%2C%20'%5C%5C%5C%5C'%5D%2C%0A%20%20%5B%2F%5C*%2Fg%2C%20'%5C%5C*'%5D%2C%0A%20%20%5B%2F%5E-%2Fg%2C%20'%5C%5C-'%5D%2C%0A%20%20%5B%2F%5E%5C%2B%20%2Fg%2C%20'%5C%5C%2B%20'%5D%2C%0A%20%20%5B%2F%5E(%3D%2B)%2Fg%2C%20'%5C%5C%241'%5D%2C%0A%20%20%5B%2F%5E(%23%7B1%2C6%7D)%20%2Fg%2C%20'%5C%5C%241%20'%5D%2C%0A%20%20%5B%2F%60%2Fg%2C%20'%5C%5C%60'%5D%2C%0A%20%20%5B%2F%5E~~~%2Fg%2C%20'%5C%5C~~~'%5D%2C%0A%20%20%5B%2F%5C%5B%2Fg%2C%20'%5C%5C%5B'%5D%2C%0A%20%20%5B%2F%5C%5D%2Fg%2C%20'%5C%5C%5D'%5D%2C%0A%20%20%5B%2F%5E%3E%2Fg%2C%20'%5C%5C%3E'%5D%2C%0A%20%20%5B%2F_%2Fg%2C%20'%5C%5C_'%5D%2C%0A%20%20%5B%2F%5E(%5Cd%2B)%5C.%20%2Fg%2C%20'%241%5C%5C.%20'%5D%0A%5D%3B%0A%0Afunction%20TurndownService%20(options)%20%7B%0A%20%20if%20(!(this%20instanceof%20TurndownService))%20return%20new%20TurndownService(options)%0A%0A%20%20var%20defaults%20%3D%20%7B%0A%20%20%20%20rules%3A%20rules%2C%0A%20%20%20%20headingStyle%3A%20'setext'%2C%0A%20%20%20%20hr%3A%20'*%20*%20*'%2C%0A%20%20%20%20bulletListMarker%3A%20'*'%2C%0A%20%20%20%20codeBlockStyle%3A%20'indented'%2C%0A%20%20%20%20fence%3A%20'%60%60%60'%2C%0A%20%20%20%20emDelimiter%3A%20'_'%2C%0A%20%20%20%20strongDelimiter%3A%20'**'%2C%0A%20%20%20%20linkStyle%3A%20'inlined'%2C%0A%20%20%20%20linkReferenceStyle%3A%20'full'%2C%0A%20%20%20%20br%3A%20'%20%20'%2C%0A%20%20%20%20blankReplacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20%20%20return%20node.isBlock%20%3F%20'%5Cn%5Cn'%20%3A%20''%0A%20%20%20%20%7D%2C%0A%20%20%20%20keepReplacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20%20%20return%20node.isBlock%20%3F%20'%5Cn%5Cn'%20%2B%20node.outerHTML%20%2B%20'%5Cn%5Cn'%20%3A%20node.outerHTML%0A%20%20%20%20%7D%2C%0A%20%20%20%20defaultReplacement%3A%20function%20(content%2C%20node)%20%7B%0A%20%20%20%20%20%20return%20node.isBlock%20%3F%20'%5Cn%5Cn'%20%2B%20content%20%2B%20'%5Cn%5Cn'%20%3A%20content%0A%20%20%20%20%7D%0A%20%20%7D%3B%0A%20%20this.options%20%3D%20extend(%7B%7D%2C%20defaults%2C%20options)%3B%0A%20%20this.rules%20%3D%20new%20Rules(this.options)%3B%0A%7D%0A%0ATurndownService.prototype%20%3D%20%7B%0A%20%20%2F**%0A%20%20%20*%20The%20entry%20point%20for%20converting%20a%20string%20or%20DOM%20node%20to%20Markdown%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BString%7CHTMLElement%7D%20input%20The%20string%20or%20DOM%20node%20to%20convert%0A%20%20%20*%20%40returns%20A%20Markdown%20representation%20of%20the%20input%0A%20%20%20*%20%40type%20String%0A%20%20%20*%2F%0A%0A%20%20turndown%3A%20function%20(input)%20%7B%0A%20%20%20%20if%20(!canConvert(input))%20%7B%0A%20%20%20%20%20%20throw%20new%20TypeError(%0A%20%20%20%20%20%20%20%20input%20%2B%20'%20is%20not%20a%20string%2C%20or%20an%20element%2Fdocument%2Ffragment%20node.'%0A%20%20%20%20%20%20)%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(input%20%3D%3D%3D%20'')%20return%20''%0A%0A%20%20%20%20var%20output%20%3D%20process.call(this%2C%20new%20RootNode(input))%3B%0A%20%20%20%20return%20postProcess.call(this%2C%20output)%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Add%20one%20or%20more%20plugins%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BFunction%7CArray%7D%20plugin%20The%20plugin%20or%20array%20of%20plugins%20to%20add%0A%20%20%20*%20%40returns%20The%20Turndown%20instance%20for%20chaining%0A%20%20%20*%20%40type%20Object%0A%20%20%20*%2F%0A%0A%20%20use%3A%20function%20(plugin)%20%7B%0A%20%20%20%20if%20(Array.isArray(plugin))%20%7B%0A%20%20%20%20%20%20for%20(var%20i%20%3D%200%3B%20i%20%3C%20plugin.length%3B%20i%2B%2B)%20this.use(plugin%5Bi%5D)%3B%0A%20%20%20%20%7D%20else%20if%20(typeof%20plugin%20%3D%3D%3D%20'function')%20%7B%0A%20%20%20%20%20%20plugin(this)%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20throw%20new%20TypeError('plugin%20must%20be%20a%20Function%20or%20an%20Array%20of%20Functions')%0A%20%20%20%20%7D%0A%20%20%20%20return%20this%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Adds%20a%20rule%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BString%7D%20key%20The%20unique%20key%20of%20the%20rule%0A%20%20%20*%20%40param%20%7BObject%7D%20rule%20The%20rule%0A%20%20%20*%20%40returns%20The%20Turndown%20instance%20for%20chaining%0A%20%20%20*%20%40type%20Object%0A%20%20%20*%2F%0A%0A%20%20addRule%3A%20function%20(key%2C%20rule)%20%7B%0A%20%20%20%20this.rules.add(key%2C%20rule)%3B%0A%20%20%20%20return%20this%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Keep%20a%20node%20(as%20HTML)%20that%20matches%20the%20filter%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BString%7CArray%7CFunction%7D%20filter%20The%20unique%20key%20of%20the%20rule%0A%20%20%20*%20%40returns%20The%20Turndown%20instance%20for%20chaining%0A%20%20%20*%20%40type%20Object%0A%20%20%20*%2F%0A%0A%20%20keep%3A%20function%20(filter)%20%7B%0A%20%20%20%20this.rules.keep(filter)%3B%0A%20%20%20%20return%20this%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Remove%20a%20node%20that%20matches%20the%20filter%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BString%7CArray%7CFunction%7D%20filter%20The%20unique%20key%20of%20the%20rule%0A%20%20%20*%20%40returns%20The%20Turndown%20instance%20for%20chaining%0A%20%20%20*%20%40type%20Object%0A%20%20%20*%2F%0A%0A%20%20remove%3A%20function%20(filter)%20%7B%0A%20%20%20%20this.rules.remove(filter)%3B%0A%20%20%20%20return%20this%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Escapes%20Markdown%20syntax%0A%20%20%20*%20%40public%0A%20%20%20*%20%40param%20%7BString%7D%20string%20The%20string%20to%20escape%0A%20%20%20*%20%40returns%20A%20string%20with%20Markdown%20syntax%20escaped%0A%20%20%20*%20%40type%20String%0A%20%20%20*%2F%0A%0A%20%20escape%3A%20function%20(string)%20%7B%0A%20%20%20%20return%20escapes.reduce(function%20(accumulator%2C%20escape)%20%7B%0A%20%20%20%20%20%20return%20accumulator.replace(escape%5B0%5D%2C%20escape%5B1%5D)%0A%20%20%20%20%7D%2C%20string)%0A%20%20%7D%0A%7D%3B%0A%0A%2F**%0A%20*%20Reduces%20a%20DOM%20node%20down%20to%20its%20Markdown%20string%20equivalent%0A%20*%20%40private%0A%20*%20%40param%20%7BHTMLElement%7D%20parentNode%20The%20node%20to%20convert%0A%20*%20%40returns%20A%20Markdown%20representation%20of%20the%20node%0A%20*%20%40type%20String%0A%20*%2F%0A%0Afunction%20process%20(parentNode)%20%7B%0A%20%20var%20self%20%3D%20this%3B%0A%20%20return%20reduce.call(parentNode.childNodes%2C%20function%20(output%2C%20node)%20%7B%0A%20%20%20%20node%20%3D%20new%20Node(node)%3B%0A%0A%20%20%20%20var%20replacement%20%3D%20''%3B%0A%20%20%20%20if%20(node.nodeType%20%3D%3D%3D%203)%20%7B%0A%20%20%20%20%20%20replacement%20%3D%20node.isCode%20%3F%20node.nodeValue%20%3A%20self.escape(node.nodeValue)%3B%0A%20%20%20%20%7D%20else%20if%20(node.nodeType%20%3D%3D%3D%201)%20%7B%0A%20%20%20%20%20%20replacement%20%3D%20replacementForNode.call(self%2C%20node)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20join(output%2C%20replacement)%0A%20%20%7D%2C%20'')%0A%7D%0A%0A%2F**%0A%20*%20Appends%20strings%20as%20each%20rule%20requires%20and%20trims%20the%20output%0A%20*%20%40private%0A%20*%20%40param%20%7BString%7D%20output%20The%20conversion%20output%0A%20*%20%40returns%20A%20trimmed%20version%20of%20the%20ouput%0A%20*%20%40type%20String%0A%20*%2F%0A%0Afunction%20postProcess%20(output)%20%7B%0A%20%20var%20self%20%3D%20this%3B%0A%20%20this.rules.forEach(function%20(rule)%20%7B%0A%20%20%20%20if%20(typeof%20rule.append%20%3D%3D%3D%20'function')%20%7B%0A%20%20%20%20%20%20output%20%3D%20join(output%2C%20rule.append(self.options))%3B%0A%20%20%20%20%7D%0A%20%20%7D)%3B%0A%0A%20%20return%20output.replace(%2F%5E%5B%5Ct%5Cr%5Cn%5D%2B%2F%2C%20'').replace(%2F%5B%5Ct%5Cr%5Cn%5Cs%5D%2B%24%2F%2C%20'')%0A%7D%0A%0A%2F**%0A%20*%20Converts%20an%20element%20node%20to%20its%20Markdown%20equivalent%0A%20*%20%40private%0A%20*%20%40param%20%7BHTMLElement%7D%20node%20The%20node%20to%20convert%0A%20*%20%40returns%20A%20Markdown%20representation%20of%20the%20node%0A%20*%20%40type%20String%0A%20*%2F%0A%0Afunction%20replacementForNode%20(node)%20%7B%0A%20%20var%20rule%20%3D%20this.rules.forNode(node)%3B%0A%20%20var%20content%20%3D%20process.call(this%2C%20node)%3B%0A%20%20var%20whitespace%20%3D%20node.flankingWhitespace%3B%0A%20%20if%20(whitespace.leading%20%7C%7C%20whitespace.trailing)%20content%20%3D%20content.trim()%3B%0A%20%20return%20(%0A%20%20%20%20whitespace.leading%20%2B%0A%20%20%20%20rule.replacement(content%2C%20node%2C%20this.options)%20%2B%0A%20%20%20%20whitespace.trailing%0A%20%20)%0A%7D%0A%0A%2F**%0A%20*%20Determines%20the%20new%20lines%20between%20the%20current%20output%20and%20the%20replacement%0A%20*%20%40private%0A%20*%20%40param%20%7BString%7D%20output%20The%20current%20conversion%20output%0A%20*%20%40param%20%7BString%7D%20replacement%20The%20string%20to%20append%20to%20the%20output%0A%20*%20%40returns%20The%20whitespace%20to%20separate%20the%20current%20output%20and%20the%20replacement%0A%20*%20%40type%20String%0A%20*%2F%0A%0Afunction%20separatingNewlines%20(output%2C%20replacement)%20%7B%0A%20%20var%20newlines%20%3D%20%5B%0A%20%20%20%20output.match(trailingNewLinesRegExp)%5B0%5D%2C%0A%20%20%20%20replacement.match(leadingNewLinesRegExp)%5B0%5D%0A%20%20%5D.sort()%3B%0A%20%20var%20maxNewlines%20%3D%20newlines%5Bnewlines.length%20-%201%5D%3B%0A%20%20return%20maxNewlines.length%20%3C%202%20%3F%20maxNewlines%20%3A%20'%5Cn%5Cn'%0A%7D%0A%0Afunction%20join%20(string1%2C%20string2)%20%7B%0A%20%20var%20separator%20%3D%20separatingNewlines(string1%2C%20string2)%3B%0A%0A%20%20%2F%2F%20Remove%20trailing%2Fleading%20newlines%20and%20replace%20with%20separator%0A%20%20string1%20%3D%20string1.replace(trailingNewLinesRegExp%2C%20'')%3B%0A%20%20string2%20%3D%20string2.replace(leadingNewLinesRegExp%2C%20'')%3B%0A%0A%20%20return%20string1%20%2B%20separator%20%2B%20string2%0A%7D%0A%0A%2F**%0A%20*%20Determines%20whether%20an%20input%20can%20be%20converted%0A%20*%20%40private%0A%20*%20%40param%20%7BString%7CHTMLElement%7D%20input%20Describe%20this%20parameter%0A%20*%20%40returns%20Describe%20what%20it%20returns%0A%20*%20%40type%20String%7CObject%7CArray%7CBoolean%7CNumber%0A%20*%2F%0A%0Afunction%20canConvert%20(input)%20%7B%0A%20%20return%20(%0A%20%20%20%20input%20!%3D%20null%20%26%26%20(%0A%20%20%20%20%20%20typeof%20input%20%3D%3D%3D%20'string'%20%7C%7C%0A%20%20%20%20%20%20(input.nodeType%20%26%26%20(%0A%20%20%20%20%20%20%20%20input.nodeType%20%3D%3D%3D%201%20%7C%7C%20input.nodeType%20%3D%3D%3D%209%20%7C%7C%20input.nodeType%20%3D%3D%3D%2011%0A%20%20%20%20%20%20))%0A%20%20%20%20)%0A%20%20)%0A%7D%0A%0A%0A%2F*%0A%20*%0A%20*%20Embed%20%40tehshrike%2Freadability%400.2.0%20to%20work%20around%20content%20security%20policies%20breaking%20import%20statements%0A%20*%0A%20*%2F%0A%0A%0A%2F*%0A%20*%20Copyright%20(c)%202010%20Arc90%20Inc%0A%20*%0A%20*%20Licensed%20under%20the%20Apache%20License%2C%20Version%202.0%20(the%20%22License%22)%3B%0A%20*%20you%20may%20not%20use%20this%20file%20except%20in%20compliance%20with%20the%20License.%0A%20*%20You%20may%20obtain%20a%20copy%20of%20the%20License%20at%0A%20*%0A%20*%20%20%20%20%20http%3A%2F%2Fwww.apache.org%2Flicenses%2FLICENSE-2.0%0A%20*%0A%20*%20Unless%20required%20by%20applicable%20law%20or%20agreed%20to%20in%20writing%2C%20software%0A%20*%20distributed%20under%20the%20License%20is%20distributed%20on%20an%20%22AS%20IS%22%20BASIS%2C%0A%20*%20WITHOUT%20WARRANTIES%20OR%20CONDITIONS%20OF%20ANY%20KIND%2C%20either%20express%20or%20implied.%0A%20*%20See%20the%20License%20for%20the%20specific%20language%20governing%20permissions%20and%0A%20*%20limitations%20under%20the%20License.%0A%20*%2F%0A%0A%2F*%0A%20*%20This%20code%20is%20heavily%20based%20on%20Arc90's%20readability.js%20(1.7.1)%20script%0A%20*%20available%20at%3A%20http%3A%2F%2Fcode.google.com%2Fp%2Farc90labs-readability%0A%20*%2F%0A%0A%2F**%0A%20*%20Public%20constructor.%0A%20*%20%40param%20%7BHTMLDocument%7D%20doc%20%20%20%20%20The%20document%20to%20parse.%0A%20*%20%40param%20%7BObject%7D%20%20%20%20%20%20%20options%20The%20options%20object.%0A%20*%2F%0Afunction%20Readability(doc%2C%20options)%20%7B%0A%20%20%2F%2F%20In%20some%20older%20versions%2C%20people%20passed%20a%20URI%20as%20the%20first%20argument.%20Cope%3A%0A%20%20if%20(options%20%26%26%20options.documentElement)%20%7B%0A%20%20%20%20doc%20%3D%20options%3B%0A%20%20%20%20options%20%3D%20arguments%5B2%5D%3B%0A%20%20%7D%20else%20if%20(!doc%20%7C%7C%20!doc.documentElement)%20%7B%0A%20%20%20%20throw%20new%20Error(%22First%20argument%20to%20Readability%20constructor%20should%20be%20a%20document%20object.%22)%3B%0A%20%20%7D%0A%20%20options%20%3D%20options%20%7C%7C%20%7B%7D%3B%0A%0A%20%20this._doc%20%3D%20doc%3B%0A%20%20this._docJSDOMParser%20%3D%20this._doc.firstChild.__JSDOMParser__%3B%0A%20%20this._articleTitle%20%3D%20null%3B%0A%20%20this._articleByline%20%3D%20null%3B%0A%20%20this._articleDir%20%3D%20null%3B%0A%20%20this._articleSiteName%20%3D%20null%3B%0A%20%20this._attempts%20%3D%20%5B%5D%3B%0A%0A%20%20%2F%2F%20Configurable%20options%0A%20%20this._debug%20%3D%20!!options.debug%3B%0A%20%20this._maxElemsToParse%20%3D%20options.maxElemsToParse%20%7C%7C%20this.DEFAULT_MAX_ELEMS_TO_PARSE%3B%0A%20%20this._nbTopCandidates%20%3D%20options.nbTopCandidates%20%7C%7C%20this.DEFAULT_N_TOP_CANDIDATES%3B%0A%20%20this._charThreshold%20%3D%20options.charThreshold%20%7C%7C%20this.DEFAULT_CHAR_THRESHOLD%3B%0A%20%20this._classesToPreserve%20%3D%20this.CLASSES_TO_PRESERVE.concat(options.classesToPreserve%20%7C%7C%20%5B%5D)%3B%0A%20%20this._keepClasses%20%3D%20!!options.keepClasses%3B%0A%0A%20%20%2F%2F%20Start%20with%20all%20flags%20set%0A%20%20this._flags%20%3D%20this.FLAG_STRIP_UNLIKELYS%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%20this.FLAG_WEIGHT_CLASSES%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%20this.FLAG_CLEAN_CONDITIONALLY%3B%0A%0A%20%20let%20logEl%3B%0A%0A%20%20%2F%2F%20Control%20whether%20log%20messages%20are%20sent%20to%20the%20console%0A%20%20if%20(this._debug)%20%7B%0A%20%20%20%20logEl%20%3D%20function(e)%20%7B%0A%20%20%20%20%20%20const%20rv%20%3D%20e.nodeName%20%2B%20%22%20%22%3B%0A%20%20%20%20%20%20if%20(e.nodeType%20%3D%3D%20e.TEXT_NODE)%20%7B%0A%20%20%20%20%20%20%20%20return%20rv%20%2B%20'(%22'%20%2B%20e.textContent%20%2B%20'%22)'%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20const%20classDesc%20%3D%20e.className%20%26%26%20(%22.%22%20%2B%20e.className.replace(%2F%20%2Fg%2C%20%22.%22))%3B%0A%20%20%20%20%20%20let%20elDesc%20%3D%20%22%22%3B%0A%20%20%20%20%20%20if%20(e.id)%20%7B%0A%20%20%20%20%20%20%20%20elDesc%20%3D%20%22(%23%22%20%2B%20e.id%20%2B%20classDesc%20%2B%20%22)%22%3B%0A%20%20%20%20%20%20%7D%20else%20if%20(classDesc)%20%7B%0A%20%20%20%20%20%20%20%20elDesc%20%3D%20%22(%22%20%2B%20classDesc%20%2B%20%22)%22%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20return%20rv%20%2B%20elDesc%3B%0A%20%20%20%20%7D%3B%0A%20%20%20%20this.log%20%3D%20function()%20%7B%0A%20%20%20%20%20%20if%20(typeof%20dump%20!%3D%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20const%20msg%20%3D%20Array.prototype.map.call(arguments%2C%20x%20%3D%3E%20(x%20%26%26%20x.nodeName)%20%3F%20logEl(x)%20%3A%20x).join(%22%20%22)%3B%0A%20%20%20%20%20%20%20%20dump(%22Reader%3A%20(Readability)%20%22%20%2B%20msg%20%2B%20%22%5Cn%22)%3B%0A%20%20%20%20%20%20%7D%20else%20if%20(typeof%20console%20!%3D%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20const%20args%20%3D%20%5B%20%22Reader%3A%20(Readability)%20%22%20%5D.concat(arguments)%3B%0A%20%20%20%20%20%20%20%20console.log.apply(console%2C%20args)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%3B%0A%20%20%7D%20else%20%7B%0A%20%20%20%20this.log%20%3D%20function()%20%7B%7D%3B%0A%20%20%7D%0A%7D%0A%0AReadability.prototype%20%3D%20%7B%0A%20%20FLAG_STRIP_UNLIKELYS%3A%200x1%2C%0A%20%20FLAG_WEIGHT_CLASSES%3A%200x2%2C%0A%20%20FLAG_CLEAN_CONDITIONALLY%3A%200x4%2C%0A%0A%20%20%2F%2F%20https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FAPI%2FNode%2FnodeType%0A%20%20ELEMENT_NODE%3A%201%2C%0A%20%20TEXT_NODE%3A%203%2C%0A%0A%20%20%2F%2F%20Max%20number%20of%20nodes%20supported%20by%20this%20parser.%20Default%3A%200%20(no%20limit)%0A%20%20DEFAULT_MAX_ELEMS_TO_PARSE%3A%200%2C%0A%0A%20%20%2F%2F%20The%20number%20of%20top%20candidates%20to%20consider%20when%20analysing%20how%0A%20%20%2F%2F%20tight%20the%20competition%20is%20among%20candidates.%0A%20%20DEFAULT_N_TOP_CANDIDATES%3A%205%2C%0A%0A%20%20%2F%2F%20Element%20tags%20to%20score%20by%20default.%0A%20%20DEFAULT_TAGS_TO_SCORE%3A%20%22section%2Ch2%2Ch3%2Ch4%2Ch5%2Ch6%2Cp%2Ctd%2Cpre%22.toUpperCase().split(%22%2C%22)%2C%0A%0A%20%20%2F%2F%20The%20default%20number%20of%20chars%20an%20article%20must%20have%20in%20order%20to%20return%20a%20result%0A%20%20DEFAULT_CHAR_THRESHOLD%3A%20500%2C%0A%0A%20%20%2F%2F%20All%20of%20the%20regular%20expressions%20in%20use%20within%20readability.%0A%20%20%2F%2F%20Defined%20up%20here%20so%20we%20don't%20instantiate%20them%20repeatedly%20in%20loops.%0A%20%20REGEXPS%3A%20%7B%0A%20%20%20%20%2F%2F%20NOTE%3A%20These%20two%20regular%20expressions%20are%20duplicated%20in%0A%20%20%20%20%2F%2F%20Readability-readerable.js.%20Please%20keep%20both%20copies%20in%20sync.%0A%20%20%20%20unlikelyCandidates%3A%20%2F-ad-%7Cai2html%7Cbanner%7Cbreadcrumbs%7Ccombx%7Ccomment%7Ccommunity%7Ccover-wrap%7Cdisqus%7Cextra%7Cfooter%7Cgdpr%7Cheader%7Clegends%7Cmenu%7Crelated%7Cremark%7Creplies%7Crss%7Cshoutbox%7Csidebar%7Cskyscraper%7Csocial%7Csponsor%7Csupplemental%7Cad-break%7Cagegate%7Cpagination%7Cpager%7Cpopup%7Cyom-remote%2Fi%2C%0A%20%20%20%20okMaybeItsACandidate%3A%20%2Fand%7Carticle%7Cbody%7Ccolumn%7Ccontent%7Cmain%7Cshadow%2Fi%2C%0A%0A%20%20%20%20positive%3A%20%2Farticle%7Cbody%7Ccontent%7Centry%7Chentry%7Ch-entry%7Cmain%7Cpage%7Cpagination%7Cpost%7Ctext%7Cblog%7Cstory%2Fi%2C%0A%20%20%20%20negative%3A%20%2Fhidden%7C%5Ehid%24%7C%20hid%24%7C%20hid%20%7C%5Ehid%20%7Cbanner%7Ccombx%7Ccomment%7Ccom-%7Ccontact%7Cfoot%7Cfooter%7Cfootnote%7Cgdpr%7Cmasthead%7Cmedia%7Cmeta%7Coutbrain%7Cpromo%7Crelated%7Cscroll%7Cshare%7Cshoutbox%7Csidebar%7Cskyscraper%7Csponsor%7Cshopping%7Ctags%7Ctool%7Cwidget%2Fi%2C%0A%20%20%20%20extraneous%3A%20%2Fprint%7Carchive%7Ccomment%7Cdiscuss%7Ce%5B%5C-%5D%3Fmail%7Cshare%7Creply%7Call%7Clogin%7Csign%7Csingle%7Cutility%2Fi%2C%0A%20%20%20%20byline%3A%20%2Fbyline%7Cauthor%7Cdateline%7Cwrittenby%7Cp-author%2Fi%2C%0A%20%20%20%20replaceFonts%3A%20%2F%3C(%5C%2F%3F)font%5B%5E%3E%5D*%3E%2Fgi%2C%0A%20%20%20%20normalize%3A%20%2F%5Cs%7B2%2C%7D%2Fg%2C%0A%20%20%20%20videos%3A%20%2F%5C%2F%5C%2F(www%5C.)%3F((dailymotion%7Cyoutube%7Cyoutube-nocookie%7Cplayer%5C.vimeo%7Cv%5C.qq)%5C.com%7C(archive%7Cupload%5C.wikimedia)%5C.org%7Cplayer%5C.twitch%5C.tv)%2Fi%2C%0A%20%20%20%20shareElements%3A%20%2F(%5Cb%7C_)(share%7Csharedaddy)(%5Cb%7C_)%2Fi%2C%0A%20%20%20%20nextLink%3A%20%2F(next%7Cweiter%7Ccontinue%7C%3E(%5B%5E%5C%7C%5D%7C%24)%7C%C2%BB(%5B%5E%5C%7C%5D%7C%24))%2Fi%2C%0A%20%20%20%20prevLink%3A%20%2F(prev%7Cearl%7Cold%7Cnew%7C%3C%7C%C2%AB)%2Fi%2C%0A%20%20%20%20whitespace%3A%20%2F%5E%5Cs*%24%2F%2C%0A%20%20%20%20hasContent%3A%20%2F%5CS%24%2F%2C%0A%20%20%20%20srcsetUrl%3A%20%2F(%5CS%2B)(%5Cs%2B%5B%5Cd.%5D%2B%5Bxw%5D)%3F(%5Cs*(%3F%3A%2C%7C%24))%2Fg%2C%0A%20%20%20%20b64DataUrl%3A%20%2F%5Edata%3A%5Cs*(%5B%5E%5Cs%3B%2C%5D%2B)%5Cs*%3B%5Cs*base64%5Cs*%2C%2Fi%2C%0A%20%20%7D%2C%0A%0A%20%20DIV_TO_P_ELEMS%3A%20%5B%20%22A%22%2C%20%22BLOCKQUOTE%22%2C%20%22DL%22%2C%20%22DIV%22%2C%20%22IMG%22%2C%20%22OL%22%2C%20%22P%22%2C%20%22PRE%22%2C%20%22TABLE%22%2C%20%22UL%22%2C%20%22SELECT%22%20%5D%2C%0A%0A%20%20ALTER_TO_DIV_EXCEPTIONS%3A%20%5B%20%22DIV%22%2C%20%22ARTICLE%22%2C%20%22SECTION%22%2C%20%22P%22%20%5D%2C%0A%0A%20%20PRESENTATIONAL_ATTRIBUTES%3A%20%5B%20%22align%22%2C%20%22background%22%2C%20%22bgcolor%22%2C%20%22border%22%2C%20%22cellpadding%22%2C%20%22cellspacing%22%2C%20%22frame%22%2C%20%22hspace%22%2C%20%22rules%22%2C%20%22style%22%2C%20%22valign%22%2C%20%22vspace%22%20%5D%2C%0A%0A%20%20DEPRECATED_SIZE_ATTRIBUTE_ELEMS%3A%20%5B%20%22TABLE%22%2C%20%22TH%22%2C%20%22TD%22%2C%20%22HR%22%2C%20%22PRE%22%20%5D%2C%0A%0A%20%20%2F%2F%20The%20commented%20out%20elements%20qualify%20as%20phrasing%20content%20but%20tend%20to%20be%0A%20%20%2F%2F%20removed%20by%20readability%20when%20put%20into%20paragraphs%2C%20so%20we%20ignore%20them%20here.%0A%20%20PHRASING_ELEMS%3A%20%5B%0A%20%20%20%20%2F%2F%20%22CANVAS%22%2C%20%22IFRAME%22%2C%20%22SVG%22%2C%20%22VIDEO%22%2C%0A%20%20%20%20%22ABBR%22%2C%20%22AUDIO%22%2C%20%22B%22%2C%20%22BDO%22%2C%20%22BR%22%2C%20%22BUTTON%22%2C%20%22CITE%22%2C%20%22CODE%22%2C%20%22DATA%22%2C%0A%20%20%20%20%22DATALIST%22%2C%20%22DFN%22%2C%20%22EM%22%2C%20%22EMBED%22%2C%20%22I%22%2C%20%22IMG%22%2C%20%22INPUT%22%2C%20%22KBD%22%2C%20%22LABEL%22%2C%0A%20%20%20%20%22MARK%22%2C%20%22MATH%22%2C%20%22METER%22%2C%20%22NOSCRIPT%22%2C%20%22OBJECT%22%2C%20%22OUTPUT%22%2C%20%22PROGRESS%22%2C%20%22Q%22%2C%0A%20%20%20%20%22RUBY%22%2C%20%22SAMP%22%2C%20%22SCRIPT%22%2C%20%22SELECT%22%2C%20%22SMALL%22%2C%20%22SPAN%22%2C%20%22STRONG%22%2C%20%22SUB%22%2C%0A%20%20%20%20%22SUP%22%2C%20%22TEXTAREA%22%2C%20%22TIME%22%2C%20%22VAR%22%2C%20%22WBR%22%2C%0A%20%20%5D%2C%0A%0A%20%20%2F%2F%20These%20are%20the%20classes%20that%20readability%20sets%20itself.%0A%20%20CLASSES_TO_PRESERVE%3A%20%5B%20%22page%22%20%5D%2C%0A%0A%20%20%2F%2F%20These%20are%20the%20list%20of%20HTML%20entities%20that%20need%20to%20be%20escaped.%0A%20%20HTML_ESCAPE_MAP%3A%20%7B%0A%20%20%20%20lt%3A%20%22%3C%22%2C%0A%20%20%20%20gt%3A%20%22%3E%22%2C%0A%20%20%20%20amp%3A%20%22%26%22%2C%0A%20%20%20%20quot%3A%20'%22'%2C%0A%20%20%20%20apos%3A%20%22'%22%2C%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Run%20any%20post-process%20modifications%20to%20article%20content%20as%20necessary.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20**%2F%0A%20%20_postProcessContent(articleContent)%20%7B%0A%20%20%20%20%2F%2F%20Readability%20cannot%20open%20relative%20uris%20so%20we%20convert%20them%20to%20absolute%20uris.%0A%20%20%20%20this._fixRelativeUris(articleContent)%3B%0A%0A%20%20%20%20if%20(!this._keepClasses)%20%7B%0A%20%20%20%20%20%20%2F%2F%20Remove%20classes.%0A%20%20%20%20%20%20this._cleanClasses(articleContent)%3B%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Iterates%20over%20a%20NodeList%2C%20calls%20%60filterFn%60%20for%20each%20node%20and%20removes%20node%0A%20%20%20*%20if%20function%20returned%20%60true%60.%0A%20%20%20*%0A%20%20%20*%20If%20function%20is%20not%20passed%2C%20removes%20all%20the%20nodes%20in%20node%20list.%0A%20%20%20*%0A%20%20%20*%20%40param%20NodeList%20nodeList%20The%20nodes%20to%20operate%20on%0A%20%20%20*%20%40param%20Function%20filterFn%20the%20function%20to%20use%20as%20a%20filter%0A%20%20%20*%20%40return%20void%0A%20%20%20*%2F%0A%20%20_removeNodes(nodeList%2C%20filterFn)%20%7B%0A%20%20%20%20%2F%2F%20Avoid%20ever%20operating%20on%20live%20node%20lists.%0A%20%20%20%20if%20(this._docJSDOMParser%20%26%26%20nodeList._isLiveNodeList)%20%7B%0A%20%20%20%20%20%20throw%20new%20Error(%22Do%20not%20pass%20live%20node%20lists%20to%20_removeNodes%22)%3B%0A%20%20%20%20%7D%0A%20%20%20%20for%20(let%20i%20%3D%20nodeList.length%20-%201%3B%20i%20%3E%3D%200%3B%20i--)%20%7B%0A%20%20%20%20%20%20const%20node%20%3D%20nodeList%5Bi%5D%3B%0A%20%20%20%20%20%20const%20parentNode%20%3D%20node.parentNode%3B%0A%20%20%20%20%20%20if%20(parentNode)%20%7B%0A%20%20%20%20%20%20%20%20if%20(!filterFn%20%7C%7C%20filterFn.call(this%2C%20node%2C%20i%2C%20nodeList))%20%7B%0A%20%20%20%20%20%20%20%20%20%20parentNode.removeChild(node)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Iterates%20over%20a%20NodeList%2C%20and%20calls%20_setNodeTag%20for%20each%20node.%0A%20%20%20*%0A%20%20%20*%20%40param%20NodeList%20nodeList%20The%20nodes%20to%20operate%20on%0A%20%20%20*%20%40param%20String%20newTagName%20the%20new%20tag%20name%20to%20use%0A%20%20%20*%20%40return%20void%0A%20%20%20*%2F%0A%20%20_replaceNodeTags(nodeList%2C%20newTagName)%20%7B%0A%20%20%20%20%2F%2F%20Avoid%20ever%20operating%20on%20live%20node%20lists.%0A%20%20%20%20if%20(this._docJSDOMParser%20%26%26%20nodeList._isLiveNodeList)%20%7B%0A%20%20%20%20%20%20throw%20new%20Error(%22Do%20not%20pass%20live%20node%20lists%20to%20_replaceNodeTags%22)%3B%0A%20%20%20%20%7D%0A%20%20%20%20for%20(let%20i%20%3D%20nodeList.length%20-%201%3B%20i%20%3E%3D%200%3B%20i--)%20%7B%0A%20%20%20%20%20%20const%20node%20%3D%20nodeList%5Bi%5D%3B%0A%20%20%20%20%20%20this._setNodeTag(node%2C%20newTagName)%3B%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Iterate%20over%20a%20NodeList%2C%20which%20doesn't%20natively%20fully%20implement%20the%20Array%0A%20%20%20*%20interface.%0A%20%20%20*%0A%20%20%20*%20For%20convenience%2C%20the%20current%20object%20context%20is%20applied%20to%20the%20provided%0A%20%20%20*%20iterate%20function.%0A%20%20%20*%0A%20%20%20*%20%40param%20%20NodeList%20nodeList%20The%20NodeList.%0A%20%20%20*%20%40param%20%20Function%20fn%20%20%20%20%20%20%20The%20iterate%20function.%0A%20%20%20*%20%40return%20void%0A%20%20%20*%2F%0A%20%20_forEachNode(nodeList%2C%20fn)%20%7B%0A%20%20%20%20Array.prototype.forEach.call(nodeList%2C%20fn%2C%20this)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Iterate%20over%20a%20NodeList%2C%20return%20true%20if%20any%20of%20the%20provided%20iterate%0A%20%20%20*%20function%20calls%20returns%20true%2C%20false%20otherwise.%0A%20%20%20*%0A%20%20%20*%20For%20convenience%2C%20the%20current%20object%20context%20is%20applied%20to%20the%0A%20%20%20*%20provided%20iterate%20function.%0A%20%20%20*%0A%20%20%20*%20%40param%20%20NodeList%20nodeList%20The%20NodeList.%0A%20%20%20*%20%40param%20%20Function%20fn%20%20%20%20%20%20%20The%20iterate%20function.%0A%20%20%20*%20%40return%20Boolean%0A%20%20%20*%2F%0A%20%20_someNode(nodeList%2C%20fn)%20%7B%0A%20%20%20%20return%20Array.prototype.some.call(nodeList%2C%20fn%2C%20this)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Iterate%20over%20a%20NodeList%2C%20return%20true%20if%20all%20of%20the%20provided%20iterate%0A%20%20%20*%20function%20calls%20return%20true%2C%20false%20otherwise.%0A%20%20%20*%0A%20%20%20*%20For%20convenience%2C%20the%20current%20object%20context%20is%20applied%20to%20the%0A%20%20%20*%20provided%20iterate%20function.%0A%20%20%20*%0A%20%20%20*%20%40param%20%20NodeList%20nodeList%20The%20NodeList.%0A%20%20%20*%20%40param%20%20Function%20fn%20%20%20%20%20%20%20The%20iterate%20function.%0A%20%20%20*%20%40return%20Boolean%0A%20%20%20*%2F%0A%20%20_everyNode(nodeList%2C%20fn)%20%7B%0A%20%20%20%20return%20Array.prototype.every.call(nodeList%2C%20fn%2C%20this)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Concat%20all%20nodelists%20passed%20as%20arguments.%0A%20%20%20*%0A%20%20%20*%20%40return%20...NodeList%0A%20%20%20*%20%40return%20Array%0A%20%20%20*%2F%0A%20%20_concatNodeLists()%20%7B%0A%20%20%20%20const%20slice%20%3D%20Array.prototype.slice%3B%0A%20%20%20%20const%20args%20%3D%20slice.call(arguments)%3B%0A%20%20%20%20const%20nodeLists%20%3D%20args.map(list%20%3D%3E%20slice.call(list))%3B%0A%20%20%20%20return%20Array.prototype.concat.apply(%5B%5D%2C%20nodeLists)%3B%0A%20%20%7D%2C%0A%0A%20%20_getAllNodesWithTag(node%2C%20tagNames)%20%7B%0A%20%20%20%20if%20(node.querySelectorAll)%20%7B%0A%20%20%20%20%20%20return%20node.querySelectorAll(tagNames.join(%22%2C%22))%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20%5B%5D.concat.apply(%5B%5D%2C%20tagNames.map(tag%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20collection%20%3D%20node.getElementsByTagName(tag)%3B%0A%20%20%20%20%20%20return%20Array.isArray(collection)%20%3F%20collection%20%3A%20Array.from(collection)%3B%0A%20%20%20%20%7D))%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Removes%20the%20class%3D%22%22%20attribute%20from%20every%20element%20in%20the%20given%0A%20%20%20*%20subtree%2C%20except%20those%20that%20match%20CLASSES_TO_PRESERVE%20and%0A%20%20%20*%20the%20classesToPreserve%20array%20from%20the%20options%20object.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20%20*%2F%0A%20%20_cleanClasses(node)%20%7B%0A%20%20%20%20const%20classesToPreserve%20%3D%20this._classesToPreserve%3B%0A%20%20%20%20const%20className%20%3D%20(node.getAttribute(%22class%22)%20%7C%7C%20%22%22)%0A%20%20%20%20%20%20.split(%2F%5Cs%2B%2F)%0A%20%20%20%20%20%20.filter(cls%20%3D%3E%20classesToPreserve.indexOf(cls)%20!%3D%20-1)%0A%20%20%20%20%20%20.join(%22%20%22)%3B%0A%0A%20%20%20%20if%20(className)%20%7B%0A%20%20%20%20%20%20node.setAttribute(%22class%22%2C%20className)%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20node.removeAttribute(%22class%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20for%20(node%20%3D%20node.firstElementChild%3B%20node%3B%20node%20%3D%20node.nextElementSibling)%20%7B%0A%20%20%20%20%20%20this._cleanClasses(node)%3B%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Converts%20each%20%3Ca%3E%20and%20%3Cimg%3E%20uri%20in%20the%20given%20element%20to%20an%20absolute%20URI%2C%0A%20%20%20*%20ignoring%20%23ref%20URIs.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20%20*%2F%0A%20%20_fixRelativeUris(articleContent)%20%7B%0A%20%20%20%20const%20baseURI%20%3D%20this._doc.baseURI%3B%0A%20%20%20%20const%20documentURI%20%3D%20this._doc.documentURI%3B%0A%20%20%20%20function%20toAbsoluteURI(uri)%20%7B%0A%20%20%20%20%20%20%2F%2F%20Leave%20hash%20links%20alone%20if%20the%20base%20URI%20matches%20the%20document%20URI%3A%0A%20%20%20%20%20%20if%20(baseURI%20%3D%3D%20documentURI%20%26%26%20uri.charAt(0)%20%3D%3D%20%22%23%22)%20%7B%0A%20%20%20%20%20%20%20%20return%20uri%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20Otherwise%2C%20resolve%20against%20base%20URI%3A%0A%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20return%20new%20URL(uri%2C%20baseURI).href%3B%0A%20%20%20%20%20%20%7D%20catch%20(ex)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Something%20went%20wrong%2C%20just%20return%20the%20original%3A%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20return%20uri%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20links%20%3D%20this._getAllNodesWithTag(articleContent%2C%20%5B%20%22a%22%20%5D)%3B%0A%20%20%20%20this._forEachNode(links%2C%20function(link)%20%7B%0A%20%20%20%20%20%20const%20href%20%3D%20link.getAttribute(%22href%22)%3B%0A%20%20%20%20%20%20if%20(href)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Remove%20links%20with%20javascript%3A%20URIs%2C%20since%0A%20%20%20%20%20%20%20%20%2F%2F%20they%20won't%20work%20after%20scripts%20have%20been%20removed%20from%20the%20page.%0A%20%20%20%20%20%20%20%20if%20(href.indexOf(%22javascript%3A%22)%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20if%20the%20link%20only%20contains%20simple%20text%20content%2C%20it%20can%20be%20converted%20to%20a%20text%20node%0A%20%20%20%20%20%20%20%20%20%20if%20(link.childNodes.length%20%3D%3D%3D%201%20%26%26%20link.childNodes%5B0%5D.nodeType%20%3D%3D%3D%20this.TEXT_NODE)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20text%20%3D%20this._doc.createTextNode(link.textContent)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20link.parentNode.replaceChild(text%2C%20link)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20if%20the%20link%20has%20multiple%20children%2C%20they%20should%20all%20be%20preserved%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20container%20%3D%20this._doc.createElement(%22span%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20while%20(link.childNodes.length%20%3E%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20container.appendChild(link.childNodes%5B0%5D)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20link.parentNode.replaceChild(container%2C%20link)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20link.setAttribute(%22href%22%2C%20toAbsoluteURI(href))%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20const%20medias%20%3D%20this._getAllNodesWithTag(articleContent%2C%20%5B%0A%20%20%20%20%20%20%22img%22%2C%20%22picture%22%2C%20%22figure%22%2C%20%22video%22%2C%20%22audio%22%2C%20%22source%22%2C%0A%20%20%20%20%5D)%3B%0A%0A%20%20%20%20this._forEachNode(medias%2C%20function(media)%20%7B%0A%20%20%20%20%20%20const%20src%20%3D%20media.getAttribute(%22src%22)%3B%0A%20%20%20%20%20%20const%20poster%20%3D%20media.getAttribute(%22poster%22)%3B%0A%20%20%20%20%20%20const%20srcset%20%3D%20media.getAttribute(%22srcset%22)%3B%0A%0A%20%20%20%20%20%20if%20(src)%20%7B%0A%20%20%20%20%20%20%20%20media.setAttribute(%22src%22%2C%20toAbsoluteURI(src))%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(poster)%20%7B%0A%20%20%20%20%20%20%20%20media.setAttribute(%22poster%22%2C%20toAbsoluteURI(poster))%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(srcset)%20%7B%0A%20%20%20%20%20%20%20%20const%20newSrcset%20%3D%20srcset.replace(this.REGEXPS.srcsetUrl%2C%20(_%2C%20p1%2C%20p2%2C%20p3)%20%3D%3E%20toAbsoluteURI(p1)%20%2B%20(p2%20%7C%7C%20%22%22)%20%2B%20p3)%3B%0A%0A%20%20%20%20%20%20%20%20media.setAttribute(%22srcset%22%2C%20newSrcset)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Get%20the%20article%20title%20as%20an%20H1.%0A%20%20%20*%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_getArticleTitle()%20%7B%0A%20%20%20%20const%20doc%20%3D%20this._doc%3B%0A%20%20%20%20let%20curTitle%20%3D%20%22%22%3B%0A%20%20%20%20let%20origTitle%20%3D%20%22%22%3B%0A%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20curTitle%20%3D%20origTitle%20%3D%20doc.title.trim()%3B%0A%0A%20%20%20%20%20%20%2F%2F%20If%20they%20had%20an%20element%20with%20id%20%22title%22%20in%20their%20HTML%0A%20%20%20%20%20%20if%20(typeof%20curTitle%20!%3D%3D%20%22string%22)%20%7B%0A%20%20%20%20%20%20%20%20curTitle%20%3D%20origTitle%20%3D%20this._getInnerText(doc.getElementsByTagName(%22title%22)%5B0%5D)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20catch%20(e)%20%7B%20%2F*%20ignore%20exceptions%20setting%20the%20title.%20*%2F%20%7D%0A%0A%20%20%20%20let%20titleHadHierarchicalSeparators%20%3D%20false%3B%0A%20%20%20%20function%20wordCount(str)%20%7B%0A%20%20%20%20%20%20return%20str.split(%2F%5Cs%2B%2F).length%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20If%20there's%20a%20separator%20in%20the%20title%2C%20first%20remove%20the%20final%20part%0A%20%20%20%20if%20((%2F%20%5B%5C%7C%5C-%5C%5C%5C%2F%3E%C2%BB%5D%20%2F).test(curTitle))%20%7B%0A%20%20%20%20%20%20titleHadHierarchicalSeparators%20%3D%20%2F%20%5B%5C%5C%5C%2F%3E%C2%BB%5D%20%2F.test(curTitle)%3B%0A%20%20%20%20%20%20curTitle%20%3D%20origTitle.replace(%2F(.*)%5B%5C%7C%5C-%5C%5C%5C%2F%3E%C2%BB%5D%20.*%2Fgi%2C%20%22%241%22)%3B%0A%0A%20%20%20%20%20%20%2F%2F%20If%20the%20resulting%20title%20is%20too%20short%20(3%20words%20or%20fewer)%2C%20remove%0A%20%20%20%20%20%20%2F%2F%20the%20first%20part%20instead%3A%0A%20%20%20%20%20%20if%20(wordCount(curTitle)%20%3C%203)%20%7B%0A%20%20%20%20%20%20%20%20curTitle%20%3D%20origTitle.replace(%2F%5B%5E%5C%7C%5C-%5C%5C%5C%2F%3E%C2%BB%5D*%5B%5C%7C%5C-%5C%5C%5C%2F%3E%C2%BB%5D(.*)%2Fgi%2C%20%22%241%22)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20if%20(curTitle.indexOf(%22%3A%20%22)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%2F%2F%20Check%20if%20we%20have%20an%20heading%20containing%20this%20exact%20string%2C%20so%20we%0A%20%20%20%20%20%20%2F%2F%20could%20assume%20it's%20the%20full%20title.%0A%20%20%20%20%20%20const%20headings%20%3D%20this._concatNodeLists(%0A%20%20%20%20%20%20%20%20doc.getElementsByTagName(%22h1%22)%2C%0A%20%20%20%20%20%20%20%20doc.getElementsByTagName(%22h2%22)%2C%0A%20%20%20%20%20%20)%3B%0A%20%20%20%20%20%20const%20trimmedTitle%20%3D%20curTitle.trim()%3B%0A%20%20%20%20%20%20const%20match%20%3D%20this._someNode(headings%2C%20heading%20%3D%3E%20heading.textContent.trim()%20%3D%3D%3D%20trimmedTitle)%3B%0A%0A%20%20%20%20%20%20%2F%2F%20If%20we%20don't%2C%20let's%20extract%20the%20title%20out%20of%20the%20original%20title%20string.%0A%20%20%20%20%20%20if%20(!match)%20%7B%0A%20%20%20%20%20%20%20%20curTitle%20%3D%20origTitle.substring(origTitle.lastIndexOf(%22%3A%22)%20%2B%201)%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20If%20the%20title%20is%20now%20too%20short%2C%20try%20the%20first%20colon%20instead%3A%0A%20%20%20%20%20%20%20%20if%20(wordCount(curTitle)%20%3C%203)%20%7B%0A%20%20%20%20%20%20%20%20%20%20curTitle%20%3D%20origTitle.substring(origTitle.indexOf(%22%3A%22)%20%2B%201)%3B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20But%20if%20we%20have%20too%20many%20words%20before%20the%20colon%20there's%20something%20weird%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20with%20the%20titles%20and%20the%20H%20tags%20so%20let's%20just%20use%20the%20original%20title%20instead%0A%20%20%20%20%20%20%20%20%7D%20else%20if%20(wordCount(origTitle.substr(0%2C%20origTitle.indexOf(%22%3A%22)))%20%3E%205)%20%7B%0A%20%20%20%20%20%20%20%20%20%20curTitle%20%3D%20origTitle%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20if%20(curTitle.length%20%3E%20150%20%7C%7C%20curTitle.length%20%3C%2015)%20%7B%0A%20%20%20%20%20%20const%20hOnes%20%3D%20doc.getElementsByTagName(%22h1%22)%3B%0A%0A%20%20%20%20%20%20if%20(hOnes.length%20%3D%3D%3D%201)%20%7B%0A%20%20%20%20%20%20%20%20curTitle%20%3D%20this._getInnerText(hOnes%5B0%5D)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20curTitle%20%3D%20curTitle.trim().replace(this.REGEXPS.normalize%2C%20%22%20%22)%3B%0A%20%20%20%20%2F%2F%20If%20we%20now%20have%204%20words%20or%20fewer%20as%20our%20title%2C%20and%20either%20no%0A%20%20%20%20%2F%2F%20'hierarchical'%20separators%20(%5C%2C%20%2F%2C%20%3E%20or%20%C2%BB)%20were%20found%20in%20the%20original%0A%20%20%20%20%2F%2F%20title%20or%20we%20decreased%20the%20number%20of%20words%20by%20more%20than%201%20word%2C%20use%0A%20%20%20%20%2F%2F%20the%20original%20title.%0A%20%20%20%20const%20curTitleWordCount%20%3D%20wordCount(curTitle)%3B%0A%20%20%20%20if%20(curTitleWordCount%20%3C%3D%204%0A%20%20%20%20%20%20%20%20%26%26%20(!titleHadHierarchicalSeparators%0A%20%20%20%20%20%20%20%20%20%7C%7C%20curTitleWordCount%20!%3D%20wordCount(origTitle.replace(%2F%5B%5C%7C%5C-%5C%5C%5C%2F%3E%C2%BB%5D%2B%2Fg%2C%20%22%22))%20-%201))%20%7B%0A%20%20%20%20%20%20curTitle%20%3D%20origTitle%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20curTitle%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Prepare%20the%20HTML%20document%20for%20readability%20to%20scrape%20it.%0A%20%20%20*%20This%20includes%20things%20like%20stripping%20javascript%2C%20CSS%2C%20and%20handling%20terrible%20markup.%0A%20%20%20*%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_prepDocument()%20%7B%0A%20%20%20%20const%20doc%20%3D%20this._doc%3B%0A%0A%20%20%20%20%2F%2F%20Remove%20all%20style%20tags%20in%20head%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(doc%2C%20%5B%20%22style%22%20%5D))%3B%0A%0A%20%20%20%20if%20(doc.body)%20%7B%0A%20%20%20%20%20%20this._replaceBrs(doc.body)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20this._replaceNodeTags(this._getAllNodesWithTag(doc%2C%20%5B%20%22font%22%20%5D)%2C%20%22SPAN%22)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Finds%20the%20next%20element%2C%20starting%20from%20the%20given%20node%2C%20and%20ignoring%0A%20%20%20*%20whitespace%20in%20between.%20If%20the%20given%20node%20is%20an%20element%2C%20the%20same%20node%20is%0A%20%20%20*%20returned.%0A%20%20%20*%2F%0A%20%20_nextElement(node)%20%7B%0A%20%20%20%20let%20next%20%3D%20node%3B%0A%20%20%20%20while%20(next%0A%20%20%20%20%20%20%20%20%26%26%20(next.nodeType%20!%3D%20this.ELEMENT_NODE)%0A%20%20%20%20%20%20%20%20%26%26%20this.REGEXPS.whitespace.test(next.textContent))%20%7B%0A%20%20%20%20%20%20next%20%3D%20next.nextSibling%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20next%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Replaces%202%20or%20more%20successive%20%3Cbr%3E%20elements%20with%20a%20single%20%3Cp%3E.%0A%20%20%20*%20Whitespace%20between%20%3Cbr%3E%20elements%20are%20ignored.%20For%20example%3A%0A%20%20%20*%20%20%20%3Cdiv%3Efoo%3Cbr%3Ebar%3Cbr%3E%20%3Cbr%3E%3Cbr%3Eabc%3C%2Fdiv%3E%0A%20%20%20*%20will%20become%3A%0A%20%20%20*%20%20%20%3Cdiv%3Efoo%3Cbr%3Ebar%3Cp%3Eabc%3C%2Fp%3E%3C%2Fdiv%3E%0A%20%20%20*%2F%0A%20%20_replaceBrs(elem)%20%7B%0A%20%20%20%20this._forEachNode(this._getAllNodesWithTag(elem%2C%20%5B%20%22br%22%20%5D)%2C%20function(br)%20%7B%0A%20%20%20%20%20%20let%20next%20%3D%20br.nextSibling%3B%0A%0A%20%20%20%20%20%20%2F%2F%20Whether%202%20or%20more%20%3Cbr%3E%20elements%20have%20been%20found%20and%20replaced%20with%20a%0A%20%20%20%20%20%20%2F%2F%20%3Cp%3E%20block.%0A%20%20%20%20%20%20let%20replaced%20%3D%20false%3B%0A%0A%20%20%20%20%20%20%2F%2F%20If%20we%20find%20a%20%3Cbr%3E%20chain%2C%20remove%20the%20%3Cbr%3Es%20until%20we%20hit%20another%20element%0A%20%20%20%20%20%20%2F%2F%20or%20non-whitespace.%20This%20leaves%20behind%20the%20first%20%3Cbr%3E%20in%20the%20chain%0A%20%20%20%20%20%20%2F%2F%20(which%20will%20be%20replaced%20with%20a%20%3Cp%3E%20later).%0A%20%20%20%20%20%20while%20((next%20%3D%20this._nextElement(next))%20%26%26%20(next.tagName%20%3D%3D%20%22BR%22))%20%7B%0A%20%20%20%20%20%20%20%20replaced%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20const%20brSibling%20%3D%20next.nextSibling%3B%0A%20%20%20%20%20%20%20%20next.parentNode.removeChild(next)%3B%0A%20%20%20%20%20%20%20%20next%20%3D%20brSibling%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20If%20we%20removed%20a%20%3Cbr%3E%20chain%2C%20replace%20the%20remaining%20%3Cbr%3E%20with%20a%20%3Cp%3E.%20Add%0A%20%20%20%20%20%20%2F%2F%20all%20sibling%20nodes%20as%20children%20of%20the%20%3Cp%3E%20until%20we%20hit%20another%20%3Cbr%3E%0A%20%20%20%20%20%20%2F%2F%20chain.%0A%20%20%20%20%20%20if%20(replaced)%20%7B%0A%20%20%20%20%20%20%20%20const%20p%20%3D%20this._doc.createElement(%22p%22)%3B%0A%20%20%20%20%20%20%20%20br.parentNode.replaceChild(p%2C%20br)%3B%0A%0A%20%20%20%20%20%20%20%20next%20%3D%20p.nextSibling%3B%0A%20%20%20%20%20%20%20%20while%20(next)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20If%20we've%20hit%20another%20%3Cbr%3E%3Cbr%3E%2C%20we're%20done%20adding%20children%20to%20this%20%3Cp%3E.%0A%20%20%20%20%20%20%20%20%20%20if%20(next.tagName%20%3D%3D%20%22BR%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20nextElem%20%3D%20this._nextElement(next.nextSibling)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(nextElem%20%26%26%20nextElem.tagName%20%3D%3D%20%22BR%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(!this._isPhrasingContent(next))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Otherwise%2C%20make%20this%20node%20a%20child%20of%20the%20new%20%3Cp%3E.%0A%20%20%20%20%20%20%20%20%20%20const%20sibling%20%3D%20next.nextSibling%3B%0A%20%20%20%20%20%20%20%20%20%20p.appendChild(next)%3B%0A%20%20%20%20%20%20%20%20%20%20next%20%3D%20sibling%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20while%20(p.lastChild%20%26%26%20this._isWhitespace(p.lastChild))%20%7B%0A%20%20%20%20%20%20%20%20%20%20p.removeChild(p.lastChild)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(p.parentNode.tagName%20%3D%3D%3D%20%22P%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._setNodeTag(p.parentNode%2C%20%22DIV%22)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20_setNodeTag(node%2C%20tag)%20%7B%0A%20%20%20%20this.log(%22_setNodeTag%22%2C%20node%2C%20tag)%3B%0A%20%20%20%20if%20(this._docJSDOMParser)%20%7B%0A%20%20%20%20%20%20node.localName%20%3D%20tag.toLowerCase()%3B%0A%20%20%20%20%20%20node.tagName%20%3D%20tag.toUpperCase()%3B%0A%20%20%20%20%20%20return%20node%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20replacement%20%3D%20node.ownerDocument.createElement(tag)%3B%0A%20%20%20%20while%20(node.firstChild)%20%7B%0A%20%20%20%20%20%20replacement.appendChild(node.firstChild)%3B%0A%20%20%20%20%7D%0A%20%20%20%20node.parentNode.replaceChild(replacement%2C%20node)%3B%0A%20%20%20%20if%20(node.readability)%20%7B%0A%20%20%20%20%20%20replacement.readability%20%3D%20node.readability%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20node.attributes.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20replacement.setAttribute(node.attributes%5Bi%5D.name%2C%20node.attributes%5Bi%5D.value)%3B%0A%20%20%20%20%20%20%7D%20catch%20(ex)%20%7B%0A%20%20%20%20%20%20%20%20%2F*%20it's%20possible%20for%20setAttribute()%20to%20throw%20if%20the%20attribute%20name%0A%20%20%20%20%20%20%20%20%20*%20isn't%20a%20valid%20XML%20Name.%20Such%20attributes%20can%20however%20be%20parsed%20from%0A%20%20%20%20%20%20%20%20%20*%20source%20in%20HTML%20docs%2C%20see%20https%3A%2F%2Fgithub.com%2Fwhatwg%2Fhtml%2Fissues%2F4275%2C%0A%20%20%20%20%20%20%20%20%20*%20so%20we%20can%20hit%20them%20here%20and%20then%20throw.%20We%20don't%20care%20about%20such%0A%20%20%20%20%20%20%20%20%20*%20attributes%20so%20we%20ignore%20them.%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20return%20replacement%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Prepare%20the%20article%20node%20for%20display.%20Clean%20out%20any%20inline%20styles%2C%0A%20%20%20*%20iframes%2C%20forms%2C%20strip%20extraneous%20%3Cp%3E%20tags%2C%20etc.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_prepArticle(articleContent)%20%7B%0A%20%20%20%20this._cleanStyles(articleContent)%3B%0A%0A%20%20%20%20%2F%2F%20Check%20for%20data%20tables%20before%20we%20continue%2C%20to%20avoid%20removing%20items%20in%0A%20%20%20%20%2F%2F%20those%20tables%2C%20which%20will%20often%20be%20isolated%20even%20though%20they're%0A%20%20%20%20%2F%2F%20visually%20linked%20to%20other%20content-ful%20elements%20(text%2C%20images%2C%20etc.).%0A%20%20%20%20this._markDataTables(articleContent)%3B%0A%0A%20%20%20%20this._fixLazyImages(articleContent)%3B%0A%0A%20%20%20%20%2F%2F%20Clean%20out%20junk%20from%20the%20article%20content%0A%20%20%20%20this._cleanConditionally(articleContent%2C%20%22form%22)%3B%0A%20%20%20%20this._cleanConditionally(articleContent%2C%20%22fieldset%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22object%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22embed%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22h1%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22footer%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22link%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22aside%22)%3B%0A%0A%20%20%20%20%2F%2F%20Clean%20out%20elements%20with%20little%20content%20that%20have%20%22share%22%20in%20their%20id%2Fclass%20combinations%20from%20final%20top%20candidates%2C%0A%20%20%20%20%2F%2F%20which%20means%20we%20don't%20remove%20the%20top%20candidates%20even%20they%20have%20%22share%22.%0A%0A%20%20%20%20const%20shareElementThreshold%20%3D%20this.DEFAULT_CHAR_THRESHOLD%3B%0A%0A%20%20%20%20this._forEachNode(articleContent.children%2C%20function(topCandidate)%20%7B%0A%20%20%20%20%20%20this._cleanMatchedNodes(topCandidate%2C%20function(node%2C%20matchString)%20%7B%0A%20%20%20%20%20%20%20%20return%20this.REGEXPS.shareElements.test(matchString)%20%26%26%20node.textContent.length%20%3C%20shareElementThreshold%3B%0A%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%2F%2F%20If%20there%20is%20only%20one%20h2%20and%20its%20text%20content%20substantially%20equals%20article%20title%2C%0A%20%20%20%20%2F%2F%20they%20are%20probably%20using%20it%20as%20a%20header%20and%20not%20a%20subheader%2C%0A%20%20%20%20%2F%2F%20so%20remove%20it%20since%20we%20already%20extract%20the%20title%20separately.%0A%20%20%20%20const%20h2%20%3D%20articleContent.getElementsByTagName(%22h2%22)%3B%0A%20%20%20%20if%20(h2.length%20%3D%3D%3D%201)%20%7B%0A%20%20%20%20%20%20const%20lengthSimilarRate%20%3D%20(h2%5B0%5D.textContent.length%20-%20this._articleTitle.length)%20%2F%20this._articleTitle.length%3B%0A%20%20%20%20%20%20if%20(Math.abs(lengthSimilarRate)%20%3C%200.5)%20%7B%0A%20%20%20%20%20%20%20%20let%20titlesMatch%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20if%20(lengthSimilarRate%20%3E%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20titlesMatch%20%3D%20h2%5B0%5D.textContent.includes(this._articleTitle)%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20titlesMatch%20%3D%20this._articleTitle.includes(h2%5B0%5D.textContent)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(titlesMatch)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._clean(articleContent%2C%20%22h2%22)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20this._clean(articleContent%2C%20%22iframe%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22input%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22textarea%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22select%22)%3B%0A%20%20%20%20this._clean(articleContent%2C%20%22button%22)%3B%0A%20%20%20%20this._cleanHeaders(articleContent)%3B%0A%0A%20%20%20%20%2F%2F%20Do%20these%20last%20as%20the%20previous%20stuff%20may%20have%20removed%20junk%0A%20%20%20%20%2F%2F%20that%20will%20affect%20these%0A%20%20%20%20this._cleanConditionally(articleContent%2C%20%22table%22)%3B%0A%20%20%20%20this._cleanConditionally(articleContent%2C%20%22ul%22)%3B%0A%20%20%20%20this._cleanConditionally(articleContent%2C%20%22div%22)%3B%0A%0A%20%20%20%20%2F%2F%20Remove%20extra%20paragraphs%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(articleContent%2C%20%5B%20%22p%22%20%5D)%2C%20function(paragraph)%20%7B%0A%20%20%20%20%20%20const%20imgCount%20%3D%20paragraph.getElementsByTagName(%22img%22).length%3B%0A%20%20%20%20%20%20const%20embedCount%20%3D%20paragraph.getElementsByTagName(%22embed%22).length%3B%0A%20%20%20%20%20%20const%20objectCount%20%3D%20paragraph.getElementsByTagName(%22object%22).length%3B%0A%20%20%20%20%20%20%2F%2F%20At%20this%20point%2C%20nasty%20iframes%20have%20been%20removed%2C%20only%20remain%20embedded%20video%20ones.%0A%20%20%20%20%20%20const%20iframeCount%20%3D%20paragraph.getElementsByTagName(%22iframe%22).length%3B%0A%20%20%20%20%20%20const%20totalCount%20%3D%20imgCount%20%2B%20embedCount%20%2B%20objectCount%20%2B%20iframeCount%3B%0A%0A%20%20%20%20%20%20return%20totalCount%20%3D%3D%3D%200%20%26%26%20!this._getInnerText(paragraph%2C%20false)%3B%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20this._forEachNode(this._getAllNodesWithTag(articleContent%2C%20%5B%20%22br%22%20%5D)%2C%20function(br)%20%7B%0A%20%20%20%20%20%20const%20next%20%3D%20this._nextElement(br.nextSibling)%3B%0A%20%20%20%20%20%20if%20(next%20%26%26%20next.tagName%20%3D%3D%20%22P%22)%20%7B%0A%20%20%20%20%20%20%20%20br.parentNode.removeChild(br)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%2F%2F%20Remove%20single-cell%20tables%0A%20%20%20%20this._forEachNode(this._getAllNodesWithTag(articleContent%2C%20%5B%20%22table%22%20%5D)%2C%20function(table)%20%7B%0A%20%20%20%20%20%20const%20tbody%20%3D%20this._hasSingleTagInsideElement(table%2C%20%22TBODY%22)%20%3F%20table.firstElementChild%20%3A%20table%3B%0A%20%20%20%20%20%20if%20(this._hasSingleTagInsideElement(tbody%2C%20%22TR%22))%20%7B%0A%20%20%20%20%20%20%20%20const%20row%20%3D%20tbody.firstElementChild%3B%0A%20%20%20%20%20%20%20%20if%20(this._hasSingleTagInsideElement(row%2C%20%22TD%22))%20%7B%0A%20%20%20%20%20%20%20%20%20%20let%20cell%20%3D%20row.firstElementChild%3B%0A%20%20%20%20%20%20%20%20%20%20cell%20%3D%20this._setNodeTag(cell%2C%20this._everyNode(cell.childNodes%2C%20this._isPhrasingContent)%20%3F%20%22P%22%20%3A%20%22DIV%22)%3B%0A%20%20%20%20%20%20%20%20%20%20table.parentNode.replaceChild(cell%2C%20table)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Initialize%20a%20node%20with%20the%20readability%20object.%20Also%20checks%20the%0A%20%20%20*%20className%2Fid%20for%20special%20names%20to%20add%20to%20its%20score.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20**%2F%0A%20%20_initializeNode(node)%20%7B%0A%20%20%20%20node.readability%20%3D%20%7B%20contentScore%3A%200%20%7D%3B%0A%0A%20%20%20%20switch%20(node.tagName)%20%7B%0A%20%20%20%20%20%20case%20%22DIV%22%3A%0A%20%20%20%20%20%20%20%20node.readability.contentScore%20%2B%3D%205%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%0A%20%20%20%20%20%20case%20%22PRE%22%3A%0A%20%20%20%20%20%20case%20%22TD%22%3A%0A%20%20%20%20%20%20case%20%22BLOCKQUOTE%22%3A%0A%20%20%20%20%20%20%20%20node.readability.contentScore%20%2B%3D%203%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%0A%20%20%20%20%20%20case%20%22ADDRESS%22%3A%0A%20%20%20%20%20%20case%20%22OL%22%3A%0A%20%20%20%20%20%20case%20%22UL%22%3A%0A%20%20%20%20%20%20case%20%22DL%22%3A%0A%20%20%20%20%20%20case%20%22DD%22%3A%0A%20%20%20%20%20%20case%20%22DT%22%3A%0A%20%20%20%20%20%20case%20%22LI%22%3A%0A%20%20%20%20%20%20case%20%22FORM%22%3A%0A%20%20%20%20%20%20%20%20node.readability.contentScore%20-%3D%203%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%0A%20%20%20%20%20%20case%20%22H1%22%3A%0A%20%20%20%20%20%20case%20%22H2%22%3A%0A%20%20%20%20%20%20case%20%22H3%22%3A%0A%20%20%20%20%20%20case%20%22H4%22%3A%0A%20%20%20%20%20%20case%20%22H5%22%3A%0A%20%20%20%20%20%20case%20%22H6%22%3A%0A%20%20%20%20%20%20case%20%22TH%22%3A%0A%20%20%20%20%20%20%20%20node.readability.contentScore%20-%3D%205%3B%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20node.readability.contentScore%20%2B%3D%20this._getClassWeight(node)%3B%0A%20%20%7D%2C%0A%0A%20%20_removeAndGetNext(node)%20%7B%0A%20%20%20%20const%20nextNode%20%3D%20this._getNextNode(node%2C%20true)%3B%0A%20%20%20%20node.parentNode.removeChild(node)%3B%0A%20%20%20%20return%20nextNode%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Traverse%20the%20DOM%20from%20node%20to%20node%2C%20starting%20at%20the%20node%20passed%20in.%0A%20%20%20*%20Pass%20true%20for%20the%20second%20parameter%20to%20indicate%20this%20node%20itself%0A%20%20%20*%20(and%20its%20kids)%20are%20going%20away%2C%20and%20we%20want%20the%20next%20node%20over.%0A%20%20%20*%0A%20%20%20*%20Calling%20this%20in%20a%20loop%20will%20traverse%20the%20DOM%20depth-first.%0A%20%20%20*%2F%0A%20%20_getNextNode(node%2C%20ignoreSelfAndKids)%20%7B%0A%20%20%20%20%2F%2F%20First%20check%20for%20kids%20if%20those%20aren't%20being%20ignored%0A%20%20%20%20if%20(!ignoreSelfAndKids%20%26%26%20node.firstElementChild)%20%7B%0A%20%20%20%20%20%20return%20node.firstElementChild%3B%0A%20%20%20%20%7D%0A%20%20%20%20%2F%2F%20Then%20for%20siblings...%0A%20%20%20%20if%20(node.nextElementSibling)%20%7B%0A%20%20%20%20%20%20return%20node.nextElementSibling%3B%0A%20%20%20%20%7D%0A%20%20%20%20%2F%2F%20And%20finally%2C%20move%20up%20the%20parent%20chain%20*and*%20find%20a%20sibling%0A%20%20%20%20%2F%2F%20(because%20this%20is%20depth-first%20traversal%2C%20we%20will%20have%20already%0A%20%20%20%20%2F%2F%20seen%20the%20parent%20nodes%20themselves).%0A%20%20%20%20do%20%7B%0A%20%20%20%20%20%20node%20%3D%20node.parentNode%3B%0A%20%20%20%20%7D%20while%20(node%20%26%26%20!node.nextElementSibling)%3B%0A%20%20%20%20return%20node%20%26%26%20node.nextElementSibling%3B%0A%20%20%7D%2C%0A%0A%20%20_checkByline(node%2C%20matchString)%20%7B%0A%20%20%20%20if%20(this._articleByline)%20%7B%0A%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(node.getAttribute%20!%3D%3D%20undefined)%20%7B%0A%20%20%20%20%20%20var%20rel%20%3D%20node.getAttribute(%22rel%22)%3B%0A%20%20%20%20%20%20var%20itemprop%20%3D%20node.getAttribute(%22itemprop%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20((rel%20%3D%3D%3D%20%22author%22%20%7C%7C%20(itemprop%20%26%26%20itemprop.indexOf(%22author%22)%20!%3D%3D%20-1)%20%7C%7C%20this.REGEXPS.byline.test(matchString))%20%26%26%20this._isValidByline(node.textContent))%20%7B%0A%20%20%20%20%20%20this._articleByline%20%3D%20node.textContent.trim()%3B%0A%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20false%3B%0A%20%20%7D%2C%0A%0A%20%20_getNodeAncestors(node%2C%20maxDepth)%20%7B%0A%20%20%20%20maxDepth%20%3D%20maxDepth%20%7C%7C%200%3B%0A%20%20%20%20let%20i%20%3D%200%2C%20ancestors%20%3D%20%5B%5D%3B%0A%20%20%20%20while%20(node.parentNode)%20%7B%0A%20%20%20%20%20%20ancestors.push(node.parentNode)%3B%0A%20%20%20%20%20%20if%20(maxDepth%20%26%26%20%2B%2Bi%20%3D%3D%3D%20maxDepth)%20%7B%0A%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20node%20%3D%20node.parentNode%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20ancestors%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%20*%0A%20%20%20*%20grabArticle%20-%20Using%20a%20variety%20of%20metrics%20(content%20score%2C%20classname%2C%20element%20types)%2C%20find%20the%20content%20that%20is%0A%20%20%20*%20%20%20%20%20%20%20%20%20most%20likely%20to%20be%20the%20stuff%20a%20user%20wants%20to%20read.%20Then%20return%20it%20wrapped%20up%20in%20a%20div.%0A%20%20%20*%0A%20%20%20*%20%40param%20page%20a%20document%20to%20run%20upon.%20Needs%20to%20be%20a%20full%20document%2C%20complete%20with%20body.%0A%20%20%20*%20%40return%20Element%0A%20%20**%2F%0A%20%20_grabArticle(page)%20%7B%0A%20%20%20%20this.log(%22****%20grabArticle%20****%22)%3B%0A%20%20%20%20const%20doc%20%3D%20this._doc%3B%0A%20%20%20%20const%20isPaging%20%3D%20(page%20!%3D%3D%20null)%3B%0A%20%20%20%20page%20%3D%20page%20%3F%20page%20%3A%20this._doc.body%3B%0A%0A%20%20%20%20%2F%2F%20We%20can't%20grab%20an%20article%20if%20we%20don't%20have%20a%20page!%0A%20%20%20%20if%20(!page)%20%7B%0A%20%20%20%20%20%20this.log(%22No%20body%20found%20in%20document.%20Abort.%22)%3B%0A%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20pageCacheHtml%20%3D%20page.innerHTML%3B%0A%0A%20%20%20%20while%20(true)%20%7B%0A%20%20%20%20%20%20const%20stripUnlikelyCandidates%20%3D%20this._flagIsActive(this.FLAG_STRIP_UNLIKELYS)%3B%0A%0A%20%20%20%20%20%20%2F%2F%20First%2C%20node%20prepping.%20Trash%20nodes%20that%20look%20cruddy%20(like%20ones%20with%20the%0A%20%20%20%20%20%20%2F%2F%20class%20name%20%22comment%22%2C%20etc)%2C%20and%20turn%20divs%20into%20P%20tags%20where%20they%20have%20been%0A%20%20%20%20%20%20%2F%2F%20used%20inappropriately%20(as%20in%2C%20where%20they%20contain%20no%20other%20block%20level%20elements.)%0A%20%20%20%20%20%20const%20elementsToScore%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20let%20node%20%3D%20this._doc.documentElement%3B%0A%0A%20%20%20%20%20%20while%20(node)%20%7B%0A%20%20%20%20%20%20%20%20const%20matchString%20%3D%20node.className%20%2B%20%22%20%22%20%2B%20node.id%3B%0A%0A%20%20%20%20%20%20%20%20if%20(!this._isProbablyVisible(node))%20%7B%0A%20%20%20%20%20%20%20%20%20%20this.log(%22Removing%20hidden%20node%20-%20%22%20%2B%20matchString)%3B%0A%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._removeAndGetNext(node)%3B%0A%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Check%20to%20see%20if%20this%20node%20is%20a%20byline%2C%20and%20remove%20it%20if%20it%20is.%0A%20%20%20%20%20%20%20%20if%20(this._checkByline(node%2C%20matchString))%20%7B%0A%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._removeAndGetNext(node)%3B%0A%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Remove%20unlikely%20candidates%0A%20%20%20%20%20%20%20%20if%20(stripUnlikelyCandidates)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(this.REGEXPS.unlikelyCandidates.test(matchString)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20!this.REGEXPS.okMaybeItsACandidate.test(matchString)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20!this._hasAncestorTag(node%2C%20%22table%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20node.tagName%20!%3D%3D%20%22BODY%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20node.tagName%20!%3D%3D%20%22A%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.log(%22Removing%20unlikely%20candidate%20-%20%22%20%2B%20matchString)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._removeAndGetNext(node)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(node.getAttribute(%22role%22)%20%3D%3D%20%22complementary%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.log(%22Removing%20complementary%20content%20-%20%22%20%2B%20matchString)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._removeAndGetNext(node)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Remove%20DIV%2C%20SECTION%2C%20and%20HEADER%20nodes%20without%20any%20content(e.g.%20text%2C%20image%2C%20video%2C%20or%20iframe).%0A%20%20%20%20%20%20%20%20if%20((node.tagName%20%3D%3D%3D%20%22DIV%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22SECTION%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22HEADER%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H1%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H2%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H3%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H4%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H5%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22H6%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20this._isElementWithoutContent(node))%20%7B%0A%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._removeAndGetNext(node)%3B%0A%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(this.DEFAULT_TAGS_TO_SCORE.indexOf(node.tagName)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20%20%20%20elementsToScore.push(node)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Turn%20all%20divs%20that%20don't%20have%20children%20block%20level%20elements%20into%20p's%0A%20%20%20%20%20%20%20%20if%20(node.tagName%20%3D%3D%3D%20%22DIV%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Put%20phrasing%20content%20into%20paragraphs.%0A%20%20%20%20%20%20%20%20%20%20let%20p%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20let%20childNode%20%3D%20node.firstChild%3B%0A%20%20%20%20%20%20%20%20%20%20while%20(childNode)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20nextSibling%20%3D%20childNode.nextSibling%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(this._isPhrasingContent(childNode))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(p%20!%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p.appendChild(childNode)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(!this._isWhitespace(childNode))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20doc.createElement(%22p%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20node.replaceChild(p%2C%20childNode)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p.appendChild(childNode)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(p%20!%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20while%20(p.lastChild%20%26%26%20this._isWhitespace(p.lastChild))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20p.removeChild(p.lastChild)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20p%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20childNode%20%3D%20nextSibling%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Sites%20like%20http%3A%2F%2Fmobile.slate.com%20encloses%20each%20paragraph%20with%20a%20DIV%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20element.%20DIVs%20with%20only%20a%20P%20element%20inside%20and%20no%20text%20content%20can%20be%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20safely%20converted%20into%20plain%20P%20elements%20to%20avoid%20confusing%20the%20scoring%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20algorithm%20with%20DIVs%20with%20are%2C%20in%20practice%2C%20paragraphs.%0A%20%20%20%20%20%20%20%20%20%20if%20(this._hasSingleTagInsideElement(node%2C%20%22P%22)%20%26%26%20this._getLinkDensity(node)%20%3C%200.25)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20newNode%20%3D%20node.children%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20node.parentNode.replaceChild(newNode%2C%20node)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%3D%20newNode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20elementsToScore.push(node)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(!this._hasChildBlockElement(node))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20node%20%3D%20this._setNodeTag(node%2C%20%22P%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20elementsToScore.push(node)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20node%20%3D%20this._getNextNode(node)%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20*%20Loop%20through%20all%20paragraphs%2C%20and%20assign%20a%20score%20to%20them%20based%20on%20how%20content-y%20they%20look.%0A%20%20%20%20%20%20%20*%20Then%20add%20their%20score%20to%20their%20parent%20node.%0A%20%20%20%20%20%20%20*%0A%20%20%20%20%20%20%20*%20A%20score%20is%20determined%20by%20things%20like%20number%20of%20commas%2C%20class%20names%2C%20etc.%20Maybe%20eventually%20link%20density.%0A%20%20%20%20%20%20**%2F%0A%20%20%20%20%20%20var%20candidates%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20this._forEachNode(elementsToScore%2C%20function(elementToScore)%20%7B%0A%20%20%20%20%20%20%20%20if%20(!elementToScore.parentNode%20%7C%7C%20typeof%20(elementToScore.parentNode.tagName)%20%3D%3D%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20If%20this%20paragraph%20is%20less%20than%2025%20characters%2C%20don't%20even%20count%20it.%0A%20%20%20%20%20%20%20%20const%20innerText%20%3D%20this._getInnerText(elementToScore)%3B%0A%20%20%20%20%20%20%20%20if%20(innerText.length%20%3C%2025)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Exclude%20nodes%20with%20no%20ancestor.%0A%20%20%20%20%20%20%20%20const%20ancestors%20%3D%20this._getNodeAncestors(elementToScore%2C%203)%3B%0A%20%20%20%20%20%20%20%20if%20(ancestors.length%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20let%20contentScore%20%3D%200%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Add%20a%20point%20for%20the%20paragraph%20itself%20as%20a%20base.%0A%20%20%20%20%20%20%20%20contentScore%20%2B%3D%201%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Add%20points%20for%20any%20commas%20within%20this%20paragraph.%0A%20%20%20%20%20%20%20%20contentScore%20%2B%3D%20innerText.split(%22%2C%22).length%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20For%20every%20100%20characters%20in%20this%20paragraph%2C%20add%20another%20point.%20Up%20to%203%20points.%0A%20%20%20%20%20%20%20%20contentScore%20%2B%3D%20Math.min(Math.floor(innerText.length%20%2F%20100)%2C%203)%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Initialize%20and%20score%20ancestors.%0A%20%20%20%20%20%20%20%20this._forEachNode(ancestors%2C%20function(ancestor%2C%20level)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(!ancestor.tagName%20%7C%7C%20!ancestor.parentNode%20%7C%7C%20typeof%20(ancestor.parentNode.tagName)%20%3D%3D%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(typeof%20(ancestor.readability)%20%3D%3D%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._initializeNode(ancestor)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20candidates.push(ancestor)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Node%20score%20divider%3A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20-%20parent%3A%20%20%20%20%20%20%20%20%20%20%20%20%201%20(no%20division)%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20-%20grandparent%3A%20%20%20%20%20%20%20%202%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20-%20great%20grandparent%2B%3A%20ancestor%20level%20*%203%0A%20%20%20%20%20%20%20%20%20%20if%20(level%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20scoreDivider%20%3D%201%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(level%20%3D%3D%3D%201)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20scoreDivider%20%3D%202%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20scoreDivider%20%3D%20level%20*%203%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20ancestor.readability.contentScore%20%2B%3D%20contentScore%20%2F%20scoreDivider%3B%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%20%20%2F%2F%20After%20we've%20calculated%20scores%2C%20loop%20through%20all%20of%20the%20possible%0A%20%20%20%20%20%20%2F%2F%20candidate%20nodes%20we%20found%20and%20find%20the%20one%20with%20the%20highest%20score.%0A%20%20%20%20%20%20const%20topCandidates%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20for%20(let%20c%20%3D%200%2C%20cl%20%3D%20candidates.length%3B%20c%20%3C%20cl%3B%20c%20%2B%3D%201)%20%7B%0A%20%20%20%20%20%20%20%20const%20candidate%20%3D%20candidates%5Bc%5D%3B%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Scale%20the%20final%20candidates%20score%20based%20on%20link%20density.%20Good%20content%0A%20%20%20%20%20%20%20%20%2F%2F%20should%20have%20a%20relatively%20small%20link%20density%20(5%25%20or%20less)%20and%20be%20mostly%0A%20%20%20%20%20%20%20%20%2F%2F%20unaffected%20by%20this%20operation.%0A%20%20%20%20%20%20%20%20const%20candidateScore%20%3D%20candidate.readability.contentScore%20*%20(1%20-%20this._getLinkDensity(candidate))%3B%0A%20%20%20%20%20%20%20%20candidate.readability.contentScore%20%3D%20candidateScore%3B%0A%0A%20%20%20%20%20%20%20%20this.log(%22Candidate%3A%22%2C%20candidate%2C%20%22with%20score%20%22%20%2B%20candidateScore)%3B%0A%0A%20%20%20%20%20%20%20%20for%20(let%20t%20%3D%200%3B%20t%20%3C%20this._nbTopCandidates%3B%20t%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20const%20aTopCandidate%20%3D%20topCandidates%5Bt%5D%3B%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(!aTopCandidate%20%7C%7C%20candidateScore%20%3E%20aTopCandidate.readability.contentScore)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20topCandidates.splice(t%2C%200%2C%20candidate)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(topCandidates.length%20%3E%20this._nbTopCandidates)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20topCandidates.pop()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20let%20topCandidate%20%3D%20topCandidates%5B0%5D%20%7C%7C%20null%3B%0A%20%20%20%20%20%20let%20neededToCreateTopCandidate%20%3D%20false%3B%0A%20%20%20%20%20%20var%20parentOfTopCandidate%3B%0A%0A%20%20%20%20%20%20%2F%2F%20If%20we%20still%20have%20no%20top%20candidate%2C%20just%20use%20the%20body%20as%20a%20last%20resort.%0A%20%20%20%20%20%20%2F%2F%20We%20also%20have%20to%20copy%20the%20body%20node%20so%20it%20is%20something%20we%20can%20modify.%0A%20%20%20%20%20%20if%20(topCandidate%20%3D%3D%3D%20null%20%7C%7C%20topCandidate.tagName%20%3D%3D%3D%20%22BODY%22)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Move%20all%20of%20the%20page's%20children%20into%20topCandidate%0A%20%20%20%20%20%20%20%20topCandidate%20%3D%20doc.createElement(%22DIV%22)%3B%0A%20%20%20%20%20%20%20%20neededToCreateTopCandidate%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20Move%20everything%20(not%20just%20elements%2C%20also%20text%20nodes%20etc.)%20into%20the%20container%0A%20%20%20%20%20%20%20%20%2F%2F%20so%20we%20even%20include%20text%20directly%20in%20the%20body%3A%0A%20%20%20%20%20%20%20%20const%20kids%20%3D%20page.childNodes%3B%0A%20%20%20%20%20%20%20%20while%20(kids.length)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this.log(%22Moving%20child%20out%3A%22%2C%20kids%5B0%5D)%3B%0A%20%20%20%20%20%20%20%20%20%20topCandidate.appendChild(kids%5B0%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20page.appendChild(topCandidate)%3B%0A%0A%20%20%20%20%20%20%20%20this._initializeNode(topCandidate)%3B%0A%20%20%20%20%20%20%7D%20else%20if%20(topCandidate)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Find%20a%20better%20top%20candidate%20node%20if%20it%20contains%20(at%20least%20three)%20nodes%20which%20belong%20to%20%60topCandidates%60%20array%0A%20%20%20%20%20%20%20%20%2F%2F%20and%20whose%20scores%20are%20quite%20closed%20with%20current%20%60topCandidate%60%20node.%0A%20%20%20%20%20%20%20%20const%20alternativeCandidateAncestors%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%201%3B%20i%20%3C%20topCandidates.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(topCandidates%5Bi%5D.readability.contentScore%20%2F%20topCandidate.readability.contentScore%20%3E%3D%200.75)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20alternativeCandidateAncestors.push(this._getNodeAncestors(topCandidates%5Bi%5D))%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20const%20MINIMUM_TOPCANDIDATES%20%3D%203%3B%0A%20%20%20%20%20%20%20%20if%20(alternativeCandidateAncestors.length%20%3E%3D%20MINIMUM_TOPCANDIDATES)%20%7B%0A%20%20%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20topCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20%20%20while%20(parentOfTopCandidate.tagName%20!%3D%3D%20%22BODY%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20let%20listsContainingThisAncestor%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20(let%20ancestorIndex%20%3D%200%3B%20ancestorIndex%20%3C%20alternativeCandidateAncestors.length%20%26%26%20listsContainingThisAncestor%20%3C%20MINIMUM_TOPCANDIDATES%3B%20ancestorIndex%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20listsContainingThisAncestor%20%2B%3D%20Number(alternativeCandidateAncestors%5BancestorIndex%5D.includes(parentOfTopCandidate))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(listsContainingThisAncestor%20%3E%3D%20MINIMUM_TOPCANDIDATES)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20topCandidate%20%3D%20parentOfTopCandidate%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20parentOfTopCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!topCandidate.readability)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._initializeNode(topCandidate)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Because%20of%20our%20bonus%20system%2C%20parents%20of%20candidates%20might%20have%20scores%0A%20%20%20%20%20%20%20%20%2F%2F%20themselves.%20They%20get%20half%20of%20the%20node.%20There%20won't%20be%20nodes%20with%20higher%0A%20%20%20%20%20%20%20%20%2F%2F%20scores%20than%20our%20topCandidate%2C%20but%20if%20we%20see%20the%20score%20going%20*up*%20in%20the%20first%0A%20%20%20%20%20%20%20%20%2F%2F%20few%20steps%20up%20the%20tree%2C%20that's%20a%20decent%20sign%20that%20there%20might%20be%20more%20content%0A%20%20%20%20%20%20%20%20%2F%2F%20lurking%20in%20other%20places%20that%20we%20want%20to%20unify%20in.%20The%20sibling%20stuff%0A%20%20%20%20%20%20%20%20%2F%2F%20below%20does%20some%20of%20that%20-%20but%20only%20if%20we've%20looked%20high%20enough%20up%20the%20DOM%0A%20%20%20%20%20%20%20%20%2F%2F%20tree.%0A%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20topCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20let%20lastScore%20%3D%20topCandidate.readability.contentScore%3B%0A%20%20%20%20%20%20%20%20%2F%2F%20The%20scores%20shouldn't%20get%20too%20low.%0A%20%20%20%20%20%20%20%20const%20scoreThreshold%20%3D%20lastScore%20%2F%203%3B%0A%20%20%20%20%20%20%20%20while%20(parentOfTopCandidate.tagName%20!%3D%3D%20%22BODY%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(!parentOfTopCandidate.readability)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20parentOfTopCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20const%20parentScore%20%3D%20parentOfTopCandidate.readability.contentScore%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(parentScore%20%3C%20scoreThreshold)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20if%20(parentScore%20%3E%20lastScore)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Alright!%20We%20found%20a%20better%20parent%20to%20use.%0A%20%20%20%20%20%20%20%20%20%20%20%20topCandidate%20%3D%20parentOfTopCandidate%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20lastScore%20%3D%20parentOfTopCandidate.readability.contentScore%3B%0A%20%20%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20parentOfTopCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20If%20the%20top%20candidate%20is%20the%20only%20child%2C%20use%20parent%20instead.%20This%20will%20help%20sibling%0A%20%20%20%20%20%20%20%20%2F%2F%20joining%20logic%20when%20adjacent%20content%20is%20actually%20located%20in%20parent's%20sibling%20node.%0A%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20topCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20while%20(parentOfTopCandidate.tagName%20!%3D%20%22BODY%22%20%26%26%20parentOfTopCandidate.children.length%20%3D%3D%201)%20%7B%0A%20%20%20%20%20%20%20%20%20%20topCandidate%20%3D%20parentOfTopCandidate%3B%0A%20%20%20%20%20%20%20%20%20%20parentOfTopCandidate%20%3D%20topCandidate.parentNode%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(!topCandidate.readability)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._initializeNode(topCandidate)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20Now%20that%20we%20have%20the%20top%20candidate%2C%20look%20through%20its%20siblings%20for%20content%0A%20%20%20%20%20%20%2F%2F%20that%20might%20also%20be%20related.%20Things%20like%20preambles%2C%20content%20split%20by%20ads%0A%20%20%20%20%20%20%2F%2F%20that%20we%20removed%2C%20etc.%0A%20%20%20%20%20%20let%20articleContent%20%3D%20doc.createElement(%22DIV%22)%3B%0A%20%20%20%20%20%20if%20(isPaging)%20%7B%0A%20%20%20%20%20%20%20%20articleContent.id%20%3D%20%22readability-content%22%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20const%20siblingScoreThreshold%20%3D%20Math.max(10%2C%20topCandidate.readability.contentScore%20*%200.2)%3B%0A%20%20%20%20%20%20%2F%2F%20Keep%20potential%20top%20candidate's%20parent%20node%20to%20try%20to%20get%20text%20direction%20of%20it%20later.%0A%20%20%20%20%20%20parentOfTopCandidate%20%3D%20topCandidate.parentNode%3B%0A%20%20%20%20%20%20const%20siblings%20%3D%20parentOfTopCandidate.children%3B%0A%0A%20%20%20%20%20%20for%20(let%20s%20%3D%200%2C%20sl%20%3D%20siblings.length%3B%20s%20%3C%20sl%3B%20s%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20let%20sibling%20%3D%20siblings%5Bs%5D%3B%0A%20%20%20%20%20%20%20%20let%20append%20%3D%20false%3B%0A%0A%20%20%20%20%20%20%20%20this.log(%22Looking%20at%20sibling%20node%3A%22%2C%20sibling%2C%20sibling.readability%20%3F%20(%22with%20score%20%22%20%2B%20sibling.readability.contentScore)%20%3A%20%22%22)%3B%0A%20%20%20%20%20%20%20%20this.log(%22Sibling%20has%20score%22%2C%20sibling.readability%20%3F%20sibling.readability.contentScore%20%3A%20%22Unknown%22)%3B%0A%0A%20%20%20%20%20%20%20%20if%20(sibling%20%3D%3D%3D%20topCandidate)%20%7B%0A%20%20%20%20%20%20%20%20%20%20append%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20let%20contentBonus%20%3D%200%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Give%20a%20bonus%20if%20sibling%20nodes%20and%20top%20candidates%20have%20the%20example%20same%20classname%0A%20%20%20%20%20%20%20%20%20%20if%20(sibling.className%20%3D%3D%3D%20topCandidate.className%20%26%26%20topCandidate.className%20!%3D%3D%20%22%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20contentBonus%20%2B%3D%20topCandidate.readability.contentScore%20*%200.2%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(sibling.readability%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20((sibling.readability.contentScore%20%2B%20contentBonus)%20%3E%3D%20siblingScoreThreshold))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20append%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(sibling.nodeName%20%3D%3D%3D%20%22P%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20linkDensity%20%3D%20this._getLinkDensity(sibling)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20nodeContent%20%3D%20this._getInnerText(sibling)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20nodeLength%20%3D%20nodeContent.length%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(nodeLength%20%3E%2080%20%26%26%20linkDensity%20%3C%200.25)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20append%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(nodeLength%20%3C%2080%20%26%26%20nodeLength%20%3E%200%20%26%26%20linkDensity%20%3D%3D%3D%200%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20nodeContent.search(%2F%5C.(%20%7C%24)%2F)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20append%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(append)%20%7B%0A%20%20%20%20%20%20%20%20%20%20this.log(%22Appending%20node%3A%22%2C%20sibling)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(this.ALTER_TO_DIV_EXCEPTIONS.indexOf(sibling.nodeName)%20%3D%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20We%20have%20a%20node%20that%20isn't%20a%20common%20block%20level%20element%2C%20like%20a%20form%20or%20td%20tag.%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Turn%20it%20into%20a%20div%20so%20it%20doesn't%20get%20filtered%20out%20later%20by%20accident.%0A%20%20%20%20%20%20%20%20%20%20%20%20this.log(%22Altering%20sibling%3A%22%2C%20sibling%2C%20%22to%20div.%22)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20sibling%20%3D%20this._setNodeTag(sibling%2C%20%22DIV%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20articleContent.appendChild(sibling)%3B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20siblings%20is%20a%20reference%20to%20the%20children%20array%2C%20and%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20sibling%20is%20removed%20from%20the%20array%20when%20we%20call%20appendChild().%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20As%20a%20result%2C%20we%20must%20revisit%20this%20index%20since%20the%20nodes%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20have%20been%20shifted.%0A%20%20%20%20%20%20%20%20%20%20s%20-%3D%201%3B%0A%20%20%20%20%20%20%20%20%20%20sl%20-%3D%201%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(this._debug)%20%7B%0A%20%20%20%20%20%20%20%20this.log(%22Article%20content%20pre-prep%3A%20%22%20%2B%20articleContent.innerHTML)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%2F%2F%20So%20we%20have%20all%20of%20the%20content%20that%20we%20need.%20Now%20we%20clean%20it%20up%20for%20presentation.%0A%20%20%20%20%20%20this._prepArticle(articleContent)%3B%0A%20%20%20%20%20%20if%20(this._debug)%20%7B%0A%20%20%20%20%20%20%20%20this.log(%22Article%20content%20post-prep%3A%20%22%20%2B%20articleContent.innerHTML)%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(neededToCreateTopCandidate)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20We%20already%20created%20a%20fake%20div%20thing%2C%20and%20there%20wouldn't%20have%20been%20any%20siblings%20left%0A%20%20%20%20%20%20%20%20%2F%2F%20for%20the%20previous%20loop%2C%20so%20there's%20no%20point%20trying%20to%20create%20a%20new%20div%2C%20and%20then%0A%20%20%20%20%20%20%20%20%2F%2F%20move%20all%20the%20children%20over.%20Just%20assign%20IDs%20and%20class%20names%20here.%20No%20need%20to%20append%0A%20%20%20%20%20%20%20%20%2F%2F%20because%20that%20already%20happened%20anyway.%0A%20%20%20%20%20%20%20%20topCandidate.id%20%3D%20%22readability-page-1%22%3B%0A%20%20%20%20%20%20%20%20topCandidate.className%20%3D%20%22page%22%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20const%20div%20%3D%20doc.createElement(%22DIV%22)%3B%0A%20%20%20%20%20%20%20%20div.id%20%3D%20%22readability-page-1%22%3B%0A%20%20%20%20%20%20%20%20div.className%20%3D%20%22page%22%3B%0A%20%20%20%20%20%20%20%20const%20children%20%3D%20articleContent.childNodes%3B%0A%20%20%20%20%20%20%20%20while%20(children.length)%20%7B%0A%20%20%20%20%20%20%20%20%20%20div.appendChild(children%5B0%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20articleContent.appendChild(div)%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(this._debug)%20%7B%0A%20%20%20%20%20%20%20%20this.log(%22Article%20content%20after%20paging%3A%20%22%20%2B%20articleContent.innerHTML)%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20let%20parseSuccessful%20%3D%20true%3B%0A%0A%20%20%20%20%20%20%2F%2F%20Now%20that%20we've%20gone%20through%20the%20full%20algorithm%2C%20check%20to%20see%20if%0A%20%20%20%20%20%20%2F%2F%20we%20got%20any%20meaningful%20content.%20If%20we%20didn't%2C%20we%20may%20need%20to%20re-run%0A%20%20%20%20%20%20%2F%2F%20grabArticle%20with%20different%20flags%20set.%20This%20gives%20us%20a%20higher%20likelihood%20of%0A%20%20%20%20%20%20%2F%2F%20finding%20the%20content%2C%20and%20the%20sieve%20approach%20gives%20us%20a%20higher%20likelihood%20of%0A%20%20%20%20%20%20%2F%2F%20finding%20the%20-right-%20content.%0A%20%20%20%20%20%20const%20textLength%20%3D%20this._getInnerText(articleContent%2C%20true).length%3B%0A%20%20%20%20%20%20if%20(textLength%20%3C%20this._charThreshold)%20%7B%0A%20%20%20%20%20%20%20%20parseSuccessful%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20page.innerHTML%20%3D%20pageCacheHtml%3B%0A%0A%20%20%20%20%20%20%20%20if%20(this._flagIsActive(this.FLAG_STRIP_UNLIKELYS))%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._removeFlag(this.FLAG_STRIP_UNLIKELYS)%3B%0A%20%20%20%20%20%20%20%20%20%20this._attempts.push(%7B%20articleContent%2C%20textLength%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20if%20(this._flagIsActive(this.FLAG_WEIGHT_CLASSES))%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._removeFlag(this.FLAG_WEIGHT_CLASSES)%3B%0A%20%20%20%20%20%20%20%20%20%20this._attempts.push(%7B%20articleContent%2C%20textLength%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20if%20(this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY))%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._removeFlag(this.FLAG_CLEAN_CONDITIONALLY)%3B%0A%20%20%20%20%20%20%20%20%20%20this._attempts.push(%7B%20articleContent%2C%20textLength%20%7D)%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20this._attempts.push(%7B%20articleContent%2C%20textLength%20%7D)%3B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20No%20luck%20after%20removing%20flags%2C%20just%20return%20the%20longest%20text%20we%20found%20during%20the%20different%20loops%0A%20%20%20%20%20%20%20%20%20%20this._attempts.sort((a%2C%20b)%20%3D%3E%20b.textLength%20-%20a.textLength)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20But%20first%20check%20if%20we%20actually%20have%20something%0A%20%20%20%20%20%20%20%20%20%20if%20(!this._attempts%5B0%5D.textLength)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20articleContent%20%3D%20this._attempts%5B0%5D.articleContent%3B%0A%20%20%20%20%20%20%20%20%20%20parseSuccessful%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(parseSuccessful)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Find%20out%20text%20direction%20from%20ancestors%20of%20final%20top%20candidate.%0A%20%20%20%20%20%20%20%20const%20ancestors%20%3D%20%5B%20parentOfTopCandidate%2C%20topCandidate%20%5D.concat(this._getNodeAncestors(parentOfTopCandidate))%3B%0A%20%20%20%20%20%20%20%20this._someNode(ancestors%2C%20function(ancestor)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(!ancestor.tagName)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20const%20articleDir%20%3D%20ancestor.getAttribute(%22dir%22)%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(articleDir)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._articleDir%20%3D%20articleDir%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D)%3B%0A%20%20%20%20%20%20%20%20return%20articleContent%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Check%20whether%20the%20input%20string%20could%20be%20a%20byline.%0A%20%20%20*%20This%20verifies%20that%20the%20input%20is%20a%20string%2C%20and%20that%20the%20length%0A%20%20%20*%20is%20less%20than%20100%20chars.%0A%20%20%20*%0A%20%20%20*%20%40param%20possibleByline%20%7Bstring%7D%20-%20a%20string%20to%20check%20whether%20its%20a%20byline.%0A%20%20%20*%20%40return%20Boolean%20-%20whether%20the%20input%20string%20is%20a%20byline.%0A%20%20%20*%2F%0A%20%20_isValidByline(byline)%20%7B%0A%20%20%20%20if%20(typeof%20byline%20%3D%3D%20%22string%22%20%7C%7C%20byline%20instanceof%20String)%20%7B%0A%20%20%20%20%20%20byline%20%3D%20byline.trim()%3B%0A%20%20%20%20%20%20return%20(byline.length%20%3E%200)%20%26%26%20(byline.length%20%3C%20100)%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20false%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Converts%20some%20of%20the%20common%20HTML%20entities%20in%20string%20to%20their%20corresponding%20characters.%0A%20%20%20*%0A%20%20%20*%20%40param%20str%20%7Bstring%7D%20-%20a%20string%20to%20unescape.%0A%20%20%20*%20%40return%20string%20without%20HTML%20entity.%0A%20%20%20*%2F%0A%20%20_unescapeHtmlEntities(str)%20%7B%0A%20%20%20%20if%20(!str)%20%7B%0A%20%20%20%20%20%20return%20str%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20htmlEscapeMap%20%3D%20this.HTML_ESCAPE_MAP%3B%0A%20%20%20%20return%20str.replace(%2F%26(quot%7Camp%7Capos%7Clt%7Cgt)%3B%2Fg%2C%20(_%2C%20tag)%20%3D%3E%20htmlEscapeMap%5Btag%5D).replace(%2F%26%23(%3F%3Ax(%5B0-9a-z%5D%7B1%2C4%7D)%7C(%5B0-9%5D%7B1%2C4%7D))%3B%2Fgi%2C%20(_%2C%20hex%2C%20numStr)%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20num%20%3D%20parseInt(hex%20%7C%7C%20numStr%2C%20hex%20%3F%2016%20%3A%2010)%3B%0A%20%20%20%20%20%20return%20String.fromCharCode(num)%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Attempts%20to%20get%20excerpt%20and%20byline%20metadata%20for%20the%20article.%0A%20%20%20*%0A%20%20%20*%20%40return%20Object%20with%20optional%20%22excerpt%22%20and%20%22byline%22%20properties%0A%20%20%20*%2F%0A%20%20_getArticleMetadata()%20%7B%0A%20%20%20%20const%20metadata%20%3D%20%7B%7D%3B%0A%20%20%20%20const%20values%20%3D%20%7B%7D%3B%0A%20%20%20%20const%20metaElements%20%3D%20this._doc.getElementsByTagName(%22meta%22)%3B%0A%0A%20%20%20%20%2F%2F%20property%20is%20a%20space-separated%20list%20of%20values%0A%20%20%20%20const%20propertyPattern%20%3D%20%2F%5Cs*(dc%7Cdcterm%7Cog%7Ctwitter)%5Cs*%3A%5Cs*(author%7Ccreator%7Cdescription%7Ctitle%7Csite_name)%5Cs*%2Fgi%3B%0A%0A%20%20%20%20%2F%2F%20name%20is%20a%20single%20value%0A%20%20%20%20const%20namePattern%20%3D%20%2F%5E%5Cs*(%3F%3A(dc%7Cdcterm%7Cog%7Ctwitter%7Cweibo%3A(article%7Cwebpage))%5Cs*%5B%5C.%3A%5D%5Cs*)%3F(author%7Ccreator%7Cdescription%7Ctitle%7Csite_name)%5Cs*%24%2Fi%3B%0A%0A%20%20%20%20%2F%2F%20Find%20description%20tags.%0A%20%20%20%20this._forEachNode(metaElements%2C%20element%20%3D%3E%20%7B%0A%20%20%20%20%20%20const%20elementName%20%3D%20element.getAttribute(%22name%22)%3B%0A%20%20%20%20%20%20const%20elementProperty%20%3D%20element.getAttribute(%22property%22)%3B%0A%20%20%20%20%20%20const%20content%20%3D%20element.getAttribute(%22content%22)%3B%0A%20%20%20%20%20%20if%20(!content)%20%7B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20let%20matches%20%3D%20null%3B%0A%20%20%20%20%20%20let%20name%20%3D%20null%3B%0A%0A%20%20%20%20%20%20if%20(elementProperty)%20%7B%0A%20%20%20%20%20%20%20%20matches%20%3D%20elementProperty.match(propertyPattern)%3B%0A%20%20%20%20%20%20%20%20if%20(matches)%20%7B%0A%20%20%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%20matches.length%20-%201%3B%20i%20%3E%3D%200%3B%20i--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Convert%20to%20lowercase%2C%20and%20remove%20any%20whitespace%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20so%20we%20can%20match%20below.%0A%20%20%20%20%20%20%20%20%20%20%20%20name%20%3D%20matches%5Bi%5D.toLowerCase().replace(%2F%5Cs%2Fg%2C%20%22%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20multiple%20authors%0A%20%20%20%20%20%20%20%20%20%20%20%20values%5Bname%5D%20%3D%20content.trim()%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20if%20(!matches%20%26%26%20elementName%20%26%26%20namePattern.test(elementName))%20%7B%0A%20%20%20%20%20%20%20%20name%20%3D%20elementName%3B%0A%20%20%20%20%20%20%20%20if%20(content)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20Convert%20to%20lowercase%2C%20remove%20any%20whitespace%2C%20and%20convert%20dots%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20to%20colons%20so%20we%20can%20match%20below.%0A%20%20%20%20%20%20%20%20%20%20name%20%3D%20name.toLowerCase().replace(%2F%5Cs%2Fg%2C%20%22%22).replace(%2F%5C.%2Fg%2C%20%22%3A%22)%3B%0A%20%20%20%20%20%20%20%20%20%20values%5Bname%5D%20%3D%20content.trim()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%2F%2F%20get%20title%0A%20%20%20%20metadata.title%20%3D%20values%5B%22dc%3Atitle%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22dcterm%3Atitle%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22og%3Atitle%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22weibo%3Aarticle%3Atitle%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22weibo%3Awebpage%3Atitle%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values.title%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22twitter%3Atitle%22%5D%3B%0A%0A%20%20%20%20if%20(!metadata.title)%20%7B%0A%20%20%20%20%20%20metadata.title%20%3D%20this._getArticleTitle()%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20get%20author%0A%20%20%20%20metadata.byline%20%3D%20values%5B%22dc%3Acreator%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22dcterm%3Acreator%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values.author%3B%0A%0A%20%20%20%20%2F%2F%20get%20description%0A%20%20%20%20metadata.excerpt%20%3D%20values%5B%22dc%3Adescription%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22dcterm%3Adescription%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22og%3Adescription%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22weibo%3Aarticle%3Adescription%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22weibo%3Awebpage%3Adescription%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values.description%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20values%5B%22twitter%3Adescription%22%5D%3B%0A%0A%20%20%20%20%2F%2F%20get%20site%20name%0A%20%20%20%20metadata.siteName%20%3D%20values%5B%22og%3Asite_name%22%5D%3B%0A%0A%20%20%20%20%2F%2F%20in%20many%20sites%20the%20meta%20value%20is%20escaped%20with%20HTML%20entities%2C%0A%20%20%20%20%2F%2F%20so%20here%20we%20need%20to%20unescape%20it%0A%20%20%20%20metadata.title%20%3D%20this._unescapeHtmlEntities(metadata.title)%3B%0A%20%20%20%20metadata.byline%20%3D%20this._unescapeHtmlEntities(metadata.byline)%3B%0A%20%20%20%20metadata.excerpt%20%3D%20this._unescapeHtmlEntities(metadata.excerpt)%3B%0A%20%20%20%20metadata.siteName%20%3D%20this._unescapeHtmlEntities(metadata.siteName)%3B%0A%0A%20%20%20%20return%20metadata%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Check%20if%20node%20is%20image%2C%20or%20if%20node%20contains%20exactly%20only%20one%20image%0A%20%20%20*%20whether%20as%20a%20direct%20child%20or%20as%20its%20descendants.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20**%2F%0A%20%20_isSingleImage(node)%20%7B%0A%20%20%20%20if%20(node.tagName%20%3D%3D%3D%20%22IMG%22)%20%7B%0A%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(node.children.length%20!%3D%3D%201%20%7C%7C%20node.textContent.trim()%20!%3D%3D%20%22%22)%20%7B%0A%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20this._isSingleImage(node.children%5B0%5D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Find%20all%20%3Cnoscript%3E%20that%20are%20located%20after%20%3Cimg%3E%20nodes%2C%20and%20which%20contain%20only%20one%0A%20%20%20*%20%3Cimg%3E%20element.%20Replace%20the%20first%20image%20with%20the%20image%20from%20inside%20the%20%3Cnoscript%3E%20tag%2C%0A%20%20%20*%20and%20remove%20the%20%3Cnoscript%3E%20tag.%20This%20improves%20the%20quality%20of%20the%20images%20we%20use%20on%0A%20%20%20*%20some%20sites%20(e.g.%20Medium).%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20**%2F%0A%20%20_unwrapNoscriptImages(doc)%20%7B%0A%20%20%20%20%2F%2F%20Find%20img%20without%20source%20or%20attributes%20that%20might%20contains%20image%2C%20and%20remove%20it.%0A%20%20%20%20%2F%2F%20This%20is%20done%20to%20prevent%20a%20placeholder%20img%20is%20replaced%20by%20img%20from%20noscript%20in%20next%20step.%0A%20%20%20%20const%20imgs%20%3D%20Array.from(doc.getElementsByTagName(%22img%22))%3B%0A%20%20%20%20this._forEachNode(imgs%2C%20img%20%3D%3E%20%7B%0A%20%20%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20img.attributes.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20const%20attr%20%3D%20img.attributes%5Bi%5D%3B%0A%20%20%20%20%20%20%20%20switch%20(attr.name)%20%7B%0A%20%20%20%20%20%20%20%20%20%20case%20%22src%22%3A%0A%20%20%20%20%20%20%20%20%20%20case%20%22srcset%22%3A%0A%20%20%20%20%20%20%20%20%20%20case%20%22data-src%22%3A%0A%20%20%20%20%20%20%20%20%20%20case%20%22data-srcset%22%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(%2F%5C.(jpg%7Cjpeg%7Cpng%7Cwebp)%2Fi.test(attr.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20img.parentNode.removeChild(img)%3B%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%2F%2F%20Next%20find%20noscript%20and%20try%20to%20extract%20its%20image%0A%20%20%20%20const%20noscripts%20%3D%20Array.from(doc.getElementsByTagName(%22noscript%22))%3B%0A%20%20%20%20this._forEachNode(noscripts%2C%20function(noscript)%20%7B%0A%20%20%20%20%20%20%2F%2F%20Parse%20content%20of%20noscript%20and%20make%20sure%20it%20only%20contains%20image%0A%20%20%20%20%20%20const%20tmp%20%3D%20doc.createElement(%22div%22)%3B%0A%20%20%20%20%20%20tmp.innerHTML%20%3D%20noscript.innerHTML%3B%0A%20%20%20%20%20%20if%20(!this._isSingleImage(tmp))%20%7B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20If%20noscript%20has%20previous%20sibling%20and%20it%20only%20contains%20image%2C%0A%20%20%20%20%20%20%2F%2F%20replace%20it%20with%20noscript%20content.%20However%20we%20also%20keep%20old%0A%20%20%20%20%20%20%2F%2F%20attributes%20that%20might%20contains%20image.%0A%20%20%20%20%20%20const%20prevElement%20%3D%20noscript.previousElementSibling%3B%0A%20%20%20%20%20%20if%20(prevElement%20%26%26%20this._isSingleImage(prevElement))%20%7B%0A%20%20%20%20%20%20%20%20let%20prevImg%20%3D%20prevElement%3B%0A%20%20%20%20%20%20%20%20if%20(prevImg.tagName%20!%3D%3D%20%22IMG%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20prevImg%20%3D%20prevElement.getElementsByTagName(%22img%22)%5B0%5D%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20const%20newImg%20%3D%20tmp.getElementsByTagName(%22img%22)%5B0%5D%3B%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20prevImg.attributes.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20const%20attr%20%3D%20prevImg.attributes%5Bi%5D%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(attr.value%20%3D%3D%3D%20%22%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(attr.name%20%3D%3D%3D%20%22src%22%20%7C%7C%20attr.name%20%3D%3D%3D%20%22srcset%22%20%7C%7C%20%2F%5C.(jpg%7Cjpeg%7Cpng%7Cwebp)%2Fi.test(attr.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(newImg.getAttribute(attr.name)%20%3D%3D%3D%20attr.value)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20let%20attrName%20%3D%20attr.name%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(newImg.hasAttribute(attrName))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20attrName%20%3D%20%22data-old-%22%20%2B%20attrName%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20newImg.setAttribute(attrName%2C%20attr.value)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20noscript.parentNode.replaceChild(tmp.firstElementChild%2C%20prevElement)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Removes%20script%20tags%20from%20the%20document.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20**%2F%0A%20%20_removeScripts(doc)%20%7B%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(doc%2C%20%5B%20%22script%22%20%5D)%2C%20scriptNode%20%3D%3E%20%7B%0A%20%20%20%20%20%20scriptNode.nodeValue%20%3D%20%22%22%3B%0A%20%20%20%20%20%20scriptNode.removeAttribute(%22src%22)%3B%0A%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D)%3B%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(doc%2C%20%5B%20%22noscript%22%20%5D))%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Check%20if%20this%20node%20has%20only%20whitespace%20and%20a%20single%20element%20with%20given%20tag%0A%20%20%20*%20Returns%20false%20if%20the%20DIV%20node%20contains%20non-empty%20text%20nodes%0A%20%20%20*%20or%20if%20it%20contains%20no%20element%20with%20given%20tag%20or%20more%20than%201%20element.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40param%20string%20tag%20of%20child%20element%0A%20%20**%2F%0A%20%20_hasSingleTagInsideElement(element%2C%20tag)%20%7B%0A%20%20%20%20%2F%2F%20There%20should%20be%20exactly%201%20element%20child%20with%20given%20tag%0A%20%20%20%20if%20(element.children.length%20!%3D%201%20%7C%7C%20element.children%5B0%5D.tagName%20!%3D%3D%20tag)%20%7B%0A%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20And%20there%20should%20be%20no%20text%20nodes%20with%20real%20content%0A%20%20%20%20return%20!this._someNode(element.childNodes%2C%20function(node)%20%7B%0A%20%20%20%20%20%20return%20node.nodeType%20%3D%3D%3D%20this.TEXT_NODE%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%26%26%20this.REGEXPS.hasContent.test(node.textContent)%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20_isElementWithoutContent(node)%20%7B%0A%20%20%20%20return%20node.nodeType%20%3D%3D%3D%20this.ELEMENT_NODE%0A%20%20%20%20%20%20%26%26%20node.textContent.trim().length%20%3D%3D%200%0A%20%20%20%20%20%20%26%26%20(node.children.length%20%3D%3D%200%0A%20%20%20%20%20%20%20%7C%7C%20node.children.length%20%3D%3D%20node.getElementsByTagName(%22br%22).length%20%2B%20node.getElementsByTagName(%22hr%22).length)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Determine%20whether%20element%20has%20any%20children%20block%20level%20elements.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%2F%0A%20%20_hasChildBlockElement(element)%20%7B%0A%20%20%20%20return%20this._someNode(element.childNodes%2C%20function(node)%20%7B%0A%20%20%20%20%20%20return%20this.DIV_TO_P_ELEMS.indexOf(node.tagName)%20!%3D%3D%20-1%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20this._hasChildBlockElement(node)%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%20*%0A%20%20%20*%20Determine%20if%20a%20node%20qualifies%20as%20phrasing%20content.%0A%20%20%20*%20https%3A%2F%2Fdeveloper.mozilla.org%2Fen-US%2Fdocs%2FWeb%2FGuide%2FHTML%2FContent_categories%23Phrasing_content%0A%20%20**%2F%0A%20%20_isPhrasingContent(node)%20%7B%0A%20%20%20%20return%20node.nodeType%20%3D%3D%3D%20this.TEXT_NODE%20%7C%7C%20this.PHRASING_ELEMS.indexOf(node.tagName)%20!%3D%3D%20-1%0A%20%20%20%20%20%20%7C%7C%20((node.tagName%20%3D%3D%3D%20%22A%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22DEL%22%20%7C%7C%20node.tagName%20%3D%3D%3D%20%22INS%22)%0A%20%20%20%20%20%20%20%20%26%26%20this._everyNode(node.childNodes%2C%20this._isPhrasingContent))%3B%0A%20%20%7D%2C%0A%0A%20%20_isWhitespace(node)%20%7B%0A%20%20%20%20return%20(node.nodeType%20%3D%3D%3D%20this.TEXT_NODE%20%26%26%20node.textContent.trim().length%20%3D%3D%3D%200)%0A%20%20%20%20%20%20%20%20%20%20%20%7C%7C%20(node.nodeType%20%3D%3D%3D%20this.ELEMENT_NODE%20%26%26%20node.tagName%20%3D%3D%3D%20%22BR%22)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Get%20the%20inner%20text%20of%20a%20node%20-%20cross%20browser%20compatibly.%0A%20%20%20*%20This%20also%20strips%20out%20any%20excess%20whitespace%20to%20be%20found.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40param%20Boolean%20normalizeSpaces%20(default%3A%20true)%0A%20%20%20*%20%40return%20string%0A%20%20**%2F%0A%20%20_getInnerText(e%2C%20normalizeSpaces)%20%7B%0A%20%20%20%20normalizeSpaces%20%3D%20(typeof%20normalizeSpaces%20%3D%3D%3D%20%22undefined%22)%20%3F%20true%20%3A%20normalizeSpaces%3B%0A%20%20%20%20const%20textContent%20%3D%20e.textContent.trim()%3B%0A%0A%20%20%20%20if%20(normalizeSpaces)%20%7B%0A%20%20%20%20%20%20return%20textContent.replace(this.REGEXPS.normalize%2C%20%22%20%22)%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20textContent%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Get%20the%20number%20of%20times%20a%20string%20s%20appears%20in%20the%20node%20e.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40param%20string%20-%20what%20to%20split%20on.%20Default%20is%20%22%2C%22%0A%20%20%20*%20%40return%20number%20(integer)%0A%20%20**%2F%0A%20%20_getCharCount(e%2C%20s)%20%7B%0A%20%20%20%20s%20%3D%20s%20%7C%7C%20%22%2C%22%3B%0A%20%20%20%20return%20this._getInnerText(e).split(s).length%20-%201%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Remove%20the%20style%20attribute%20on%20every%20e%20and%20under.%0A%20%20%20*%20TODO%3A%20Test%20if%20getElementsByTagName(*)%20is%20faster.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20**%2F%0A%20%20_cleanStyles(e)%20%7B%0A%20%20%20%20if%20(!e%20%7C%7C%20e.tagName.toLowerCase()%20%3D%3D%3D%20%22svg%22)%20%7B%0A%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20Remove%20%60style%60%20and%20deprecated%20presentational%20attributes%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20this.PRESENTATIONAL_ATTRIBUTES.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20e.removeAttribute(this.PRESENTATIONAL_ATTRIBUTES%5Bi%5D)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(this.DEPRECATED_SIZE_ATTRIBUTE_ELEMS.indexOf(e.tagName)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20e.removeAttribute(%22width%22)%3B%0A%20%20%20%20%20%20e.removeAttribute(%22height%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20let%20cur%20%3D%20e.firstElementChild%3B%0A%20%20%20%20while%20(cur%20!%3D%3D%20null)%20%7B%0A%20%20%20%20%20%20this._cleanStyles(cur)%3B%0A%20%20%20%20%20%20cur%20%3D%20cur.nextElementSibling%3B%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Get%20the%20density%20of%20links%20as%20a%20percentage%20of%20the%20content%0A%20%20%20*%20This%20is%20the%20amount%20of%20text%20that%20is%20inside%20a%20link%20divided%20by%20the%20total%20text%20in%20the%20node.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20number%20(float)%0A%20%20**%2F%0A%20%20_getLinkDensity(element)%20%7B%0A%20%20%20%20const%20textLength%20%3D%20this._getInnerText(element).length%3B%0A%20%20%20%20if%20(textLength%20%3D%3D%3D%200)%20%7B%0A%20%20%20%20%20%20return%200%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20let%20linkLength%20%3D%200%3B%0A%0A%20%20%20%20%2F%2F%20XXX%20implement%20_reduceNodeList%3F%0A%20%20%20%20this._forEachNode(element.getElementsByTagName(%22a%22)%2C%20function(linkNode)%20%7B%0A%20%20%20%20%20%20linkLength%20%2B%3D%20this._getInnerText(linkNode).length%3B%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20return%20linkLength%20%2F%20textLength%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Get%20an%20elements%20class%2Fid%20weight.%20Uses%20regular%20expressions%20to%20tell%20if%20this%0A%20%20%20*%20element%20looks%20good%20or%20bad.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20number%20(Integer)%0A%20%20**%2F%0A%20%20_getClassWeight(e)%20%7B%0A%20%20%20%20if%20(!this._flagIsActive(this.FLAG_WEIGHT_CLASSES))%20%7B%0A%20%20%20%20%20%20return%200%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20let%20weight%20%3D%200%3B%0A%0A%20%20%20%20%2F%2F%20Look%20for%20a%20special%20classname%0A%20%20%20%20if%20(typeof%20(e.className)%20%3D%3D%3D%20%22string%22%20%26%26%20e.className%20!%3D%3D%20%22%22)%20%7B%0A%20%20%20%20%20%20if%20(this.REGEXPS.negative.test(e.className))%20%7B%0A%20%20%20%20%20%20%20%20weight%20-%3D%2025%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(this.REGEXPS.positive.test(e.className))%20%7B%0A%20%20%20%20%20%20%20%20weight%20%2B%3D%2025%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20Look%20for%20a%20special%20ID%0A%20%20%20%20if%20(typeof%20(e.id)%20%3D%3D%3D%20%22string%22%20%26%26%20e.id%20!%3D%3D%20%22%22)%20%7B%0A%20%20%20%20%20%20if%20(this.REGEXPS.negative.test(e.id))%20%7B%0A%20%20%20%20%20%20%20%20weight%20-%3D%2025%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(this.REGEXPS.positive.test(e.id))%20%7B%0A%20%20%20%20%20%20%20%20weight%20%2B%3D%2025%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20return%20weight%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Clean%20a%20node%20of%20all%20elements%20of%20type%20%22tag%22.%0A%20%20%20*%20(Unless%20it's%20a%20youtube%2Fvimeo%20video.%20People%20love%20movies.)%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40param%20string%20tag%20to%20clean%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_clean(e%2C%20tag)%20%7B%0A%20%20%20%20const%20isEmbed%20%3D%20%5B%20%22object%22%2C%20%22embed%22%2C%20%22iframe%22%20%5D.indexOf(tag)%20!%3D%3D%20-1%3B%0A%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(e%2C%20%5B%20tag%20%5D)%2C%20function(element)%20%7B%0A%20%20%20%20%20%20%2F%2F%20Allow%20youtube%20and%20vimeo%20videos%20through%20as%20people%20usually%20want%20to%20see%20those.%0A%20%20%20%20%20%20if%20(isEmbed)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20First%2C%20check%20the%20elements%20attributes%20to%20see%20if%20any%20of%20them%20contain%20youtube%20or%20vimeo%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20element.attributes.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20if%20(this.REGEXPS.videos.test(element.attributes%5Bi%5D.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20For%20embed%20with%20%3Cobject%3E%20tag%2C%20check%20inner%20HTML%20as%20well.%0A%20%20%20%20%20%20%20%20if%20(element.tagName%20%3D%3D%3D%20%22object%22%20%26%26%20this.REGEXPS.videos.test(element.innerHTML))%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Check%20if%20a%20given%20node%20has%20one%20of%20its%20ancestor%20tag%20name%20matching%20the%0A%20%20%20*%20provided%20one.%0A%20%20%20*%20%40param%20%20HTMLElement%20node%0A%20%20%20*%20%40param%20%20String%20%20%20%20%20%20tagName%0A%20%20%20*%20%40param%20%20Number%20%20%20%20%20%20maxDepth%0A%20%20%20*%20%40param%20%20Function%20%20%20%20filterFn%20a%20filter%20to%20invoke%20to%20determine%20whether%20this%20node%20'counts'%0A%20%20%20*%20%40return%20Boolean%0A%20%20%20*%2F%0A%20%20_hasAncestorTag(node%2C%20tagName%2C%20maxDepth%2C%20filterFn)%20%7B%0A%20%20%20%20maxDepth%20%3D%20maxDepth%20%7C%7C%203%3B%0A%20%20%20%20tagName%20%3D%20tagName.toUpperCase()%3B%0A%20%20%20%20let%20depth%20%3D%200%3B%0A%20%20%20%20while%20(node.parentNode)%20%7B%0A%20%20%20%20%20%20if%20(maxDepth%20%3E%200%20%26%26%20depth%20%3E%20maxDepth)%20%7B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20if%20(node.parentNode.tagName%20%3D%3D%3D%20tagName%20%26%26%20(!filterFn%20%7C%7C%20filterFn(node.parentNode)))%20%7B%0A%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20node%20%3D%20node.parentNode%3B%0A%20%20%20%20%20%20depth%2B%2B%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20false%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Return%20an%20object%20indicating%20how%20many%20rows%20and%20columns%20this%20table%20has.%0A%20%20%20*%2F%0A%20%20_getRowAndColumnCount(table)%20%7B%0A%20%20%20%20let%20rows%20%3D%200%3B%0A%20%20%20%20let%20columns%20%3D%200%3B%0A%20%20%20%20const%20trs%20%3D%20table.getElementsByTagName(%22tr%22)%3B%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20trs.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20let%20rowspan%20%3D%20trs%5Bi%5D.getAttribute(%22rowspan%22)%20%7C%7C%200%3B%0A%20%20%20%20%20%20if%20(rowspan)%20%7B%0A%20%20%20%20%20%20%20%20rowspan%20%3D%20parseInt(rowspan%2C%2010)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20rows%20%2B%3D%20(rowspan%20%7C%7C%201)%3B%0A%0A%20%20%20%20%20%20%2F%2F%20Now%20look%20for%20column-related%20info%0A%20%20%20%20%20%20let%20columnsInThisRow%20%3D%200%3B%0A%20%20%20%20%20%20const%20cells%20%3D%20trs%5Bi%5D.getElementsByTagName(%22td%22)%3B%0A%20%20%20%20%20%20for%20(let%20j%20%3D%200%3B%20j%20%3C%20cells.length%3B%20j%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20let%20colspan%20%3D%20cells%5Bj%5D.getAttribute(%22colspan%22)%20%7C%7C%200%3B%0A%20%20%20%20%20%20%20%20if%20(colspan)%20%7B%0A%20%20%20%20%20%20%20%20%20%20colspan%20%3D%20parseInt(colspan%2C%2010)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20columnsInThisRow%20%2B%3D%20(colspan%20%7C%7C%201)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20columns%20%3D%20Math.max(columns%2C%20columnsInThisRow)%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20%7B%20rows%2C%20columns%20%7D%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Look%20for%20'data'%20(as%20opposed%20to%20'layout')%20tables%2C%20for%20which%20we%20use%0A%20%20%20*%20similar%20checks%20as%0A%20%20%20*%20https%3A%2F%2Fdxr.mozilla.org%2Fmozilla-central%2Frev%2F71224049c0b52ab190564d3ea0eab089a159a4cf%2Faccessible%2Fhtml%2FHTMLTableAccessible.cpp%23920%0A%20%20%20*%2F%0A%20%20_markDataTables(root)%20%7B%0A%20%20%20%20const%20tables%20%3D%20root.getElementsByTagName(%22table%22)%3B%0A%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20tables.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20var%20table%20%3D%20tables%5Bi%5D%3B%0A%20%20%20%20%20%20const%20role%20%3D%20table.getAttribute(%22role%22)%3B%0A%20%20%20%20%20%20if%20(role%20%3D%3D%20%22presentation%22)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20const%20datatable%20%3D%20table.getAttribute(%22datatable%22)%3B%0A%20%20%20%20%20%20if%20(datatable%20%3D%3D%20%220%22)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20const%20summary%20%3D%20table.getAttribute(%22summary%22)%3B%0A%20%20%20%20%20%20if%20(summary)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20const%20caption%20%3D%20table.getElementsByTagName(%22caption%22)%5B0%5D%3B%0A%20%20%20%20%20%20if%20(caption%20%26%26%20caption.childNodes.length%20%3E%200)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20If%20the%20table%20has%20a%20descendant%20with%20any%20of%20these%20tags%2C%20consider%20a%20data%20table%3A%0A%20%20%20%20%20%20const%20dataTableDescendants%20%3D%20%5B%20%22col%22%2C%20%22colgroup%22%2C%20%22tfoot%22%2C%20%22thead%22%2C%20%22th%22%20%5D%3B%0A%20%20%20%20%20%20const%20descendantExists%20%3D%20function(tag)%20%7B%0A%20%20%20%20%20%20%20%20return%20!!table.getElementsByTagName(tag)%5B0%5D%3B%0A%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20if%20(dataTableDescendants.some(descendantExists))%20%7B%0A%20%20%20%20%20%20%20%20this.log(%22Data%20table%20because%20found%20data-y%20descendant%22)%3B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20Nested%20tables%20indicate%20a%20layout%20table%3A%0A%20%20%20%20%20%20if%20(table.getElementsByTagName(%22table%22)%5B0%5D)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20const%20sizeInfo%20%3D%20this._getRowAndColumnCount(table)%3B%0A%20%20%20%20%20%20if%20(sizeInfo.rows%20%3E%3D%2010%20%7C%7C%20sizeInfo.columns%20%3E%204)%20%7B%0A%20%20%20%20%20%20%20%20table._readabilityDataTable%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%2F%2F%20Now%20just%20go%20by%20size%20entirely%3A%0A%20%20%20%20%20%20table._readabilityDataTable%20%3D%20sizeInfo.rows%20*%20sizeInfo.columns%20%3E%2010%3B%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F*%20convert%20images%20and%20figures%20that%20have%20properties%20like%20data-src%20into%20images%20that%20can%20be%20loaded%20without%20JS%20*%2F%0A%20%20_fixLazyImages(root)%20%7B%0A%20%20%20%20this._forEachNode(this._getAllNodesWithTag(root%2C%20%5B%20%22img%22%2C%20%22picture%22%2C%20%22figure%22%20%5D)%2C%20function(elem)%20%7B%0A%20%20%20%20%20%20%2F%2F%20In%20some%20sites%20(e.g.%20Kotaku)%2C%20they%20put%201px%20square%20image%20as%20base64%20data%20uri%20in%20the%20src%20attribute.%0A%20%20%20%20%20%20%2F%2F%20So%2C%20here%20we%20check%20if%20the%20data%20uri%20is%20too%20short%2C%20just%20might%20as%20well%20remove%20it.%0A%20%20%20%20%20%20if%20(elem.src%20%26%26%20this.REGEXPS.b64DataUrl.test(elem.src))%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20Make%20sure%20it's%20not%20SVG%2C%20because%20SVG%20can%20have%20a%20meaningful%20image%20in%20under%20133%20bytes.%0A%20%20%20%20%20%20%20%20const%20parts%20%3D%20this.REGEXPS.b64DataUrl.exec(elem.src)%3B%0A%20%20%20%20%20%20%20%20if%20(parts%5B1%5D%20%3D%3D%3D%20%22image%2Fsvg%2Bxml%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Make%20sure%20this%20element%20has%20other%20attributes%20which%20contains%20image.%0A%20%20%20%20%20%20%20%20%2F%2F%20If%20it%20doesn't%2C%20then%20this%20src%20is%20important%20and%20shouldn't%20be%20removed.%0A%20%20%20%20%20%20%20%20let%20srcCouldBeRemoved%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20elem.attributes.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20var%20attr%20%3D%20elem.attributes%5Bi%5D%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(attr.name%20%3D%3D%3D%20%22src%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20if%20(%2F%5C.(jpg%7Cjpeg%7Cpng%7Cwebp)%2Fi.test(attr.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20srcCouldBeRemoved%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F%2F%20Here%20we%20assume%20if%20image%20is%20less%20than%20100%20bytes%20(or%20133B%20after%20encoded%20to%20base64)%0A%20%20%20%20%20%20%20%20%2F%2F%20it%20will%20be%20too%20small%2C%20therefore%20it%20might%20be%20placeholder%20image.%0A%20%20%20%20%20%20%20%20if%20(srcCouldBeRemoved)%20%7B%0A%20%20%20%20%20%20%20%20%20%20const%20b64starts%20%3D%20elem.src.search(%2Fbase64%5Cs*%2Fi)%20%2B%207%3B%0A%20%20%20%20%20%20%20%20%20%20const%20b64length%20%3D%20elem.src.length%20-%20b64starts%3B%0A%20%20%20%20%20%20%20%20%20%20if%20(b64length%20%3C%20133)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20elem.removeAttribute(%22src%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20also%20check%20for%20%22null%22%20to%20work%20around%20https%3A%2F%2Fgithub.com%2Fjsdom%2Fjsdom%2Fissues%2F2580%0A%20%20%20%20%20%20if%20((elem.src%20%7C%7C%20(elem.srcset%20%26%26%20elem.srcset%20!%3D%20%22null%22))%20%26%26%20elem.className.toLowerCase().indexOf(%22lazy%22)%20%3D%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20for%20(let%20j%20%3D%200%3B%20j%20%3C%20elem.attributes.length%3B%20j%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20attr%20%3D%20elem.attributes%5Bj%5D%3B%0A%20%20%20%20%20%20%20%20if%20(attr.name%20%3D%3D%3D%20%22src%22%20%7C%7C%20attr.name%20%3D%3D%3D%20%22srcset%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20let%20copyTo%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20if%20(%2F%5C.(jpg%7Cjpeg%7Cpng%7Cwebp)%5Cs%2B%5Cd%2F.test(attr.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20copyTo%20%3D%20%22srcset%22%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20if%20(%2F%5E%5Cs*%5CS%2B%5C.(jpg%7Cjpeg%7Cpng%7Cwebp)%5CS*%5Cs*%24%2F.test(attr.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20copyTo%20%3D%20%22src%22%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20(copyTo)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20if%20this%20is%20an%20img%20or%20picture%2C%20set%20the%20attribute%20directly%0A%20%20%20%20%20%20%20%20%20%20if%20(elem.tagName%20%3D%3D%3D%20%22IMG%22%20%7C%7C%20elem.tagName%20%3D%3D%3D%20%22PICTURE%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20elem.setAttribute(copyTo%2C%20attr.value)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(elem.tagName%20%3D%3D%3D%20%22FIGURE%22%20%26%26%20!this._getAllNodesWithTag(elem%2C%20%5B%20%22img%22%2C%20%22picture%22%20%5D).length)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20if%20the%20item%20is%20a%20%3Cfigure%3E%20that%20does%20not%20contain%20an%20image%20or%20picture%2C%20create%20one%20and%20place%20it%20inside%20the%20figure%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20see%20the%20nytimes-3%20testcase%20for%20an%20example%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20img%20%3D%20this._doc.createElement(%22img%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20img.setAttribute(copyTo%2C%20attr.value)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20elem.appendChild(img)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Clean%20an%20element%20of%20all%20tags%20of%20type%20%22tag%22%20if%20they%20look%20fishy.%0A%20%20%20*%20%22Fishy%22%20is%20an%20algorithm%20based%20on%20content%20length%2C%20classnames%2C%20link%20density%2C%20number%20of%20images%20%26%20embeds%2C%20etc.%0A%20%20%20*%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_cleanConditionally(e%2C%20tag)%20%7B%0A%20%20%20%20if%20(!this._flagIsActive(this.FLAG_CLEAN_CONDITIONALLY))%20%7B%0A%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20isList%20%3D%20tag%20%3D%3D%3D%20%22ul%22%20%7C%7C%20tag%20%3D%3D%3D%20%22ol%22%3B%0A%0A%20%20%20%20%2F%2F%20Gather%20counts%20for%20other%20typical%20elements%20embedded%20within.%0A%20%20%20%20%2F%2F%20Traverse%20backwards%20so%20we%20can%20remove%20nodes%20at%20the%20same%20time%0A%20%20%20%20%2F%2F%20without%20effecting%20the%20traversal.%0A%20%20%20%20%2F%2F%0A%20%20%20%20%2F%2F%20TODO%3A%20Consider%20taking%20into%20account%20original%20contentScore%20here.%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(e%2C%20%5B%20tag%20%5D)%2C%20function(node)%20%7B%0A%20%20%20%20%20%20%2F%2F%20First%20check%20if%20this%20node%20IS%20data%20table%2C%20in%20which%20case%20don't%20remove%20it.%0A%20%20%20%20%20%20const%20isDataTable%20%3D%20function(t)%20%7B%0A%20%20%20%20%20%20%20%20return%20t._readabilityDataTable%3B%0A%20%20%20%20%20%20%7D%3B%0A%0A%20%20%20%20%20%20if%20(tag%20%3D%3D%3D%20%22table%22%20%26%26%20isDataTable(node))%20%7B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%2F%2F%20Next%20check%20if%20we're%20inside%20a%20data%20table%2C%20in%20which%20case%20don't%20remove%20it%20as%20well.%0A%20%20%20%20%20%20if%20(this._hasAncestorTag(node%2C%20%22table%22%2C%20-1%2C%20isDataTable))%20%7B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20const%20weight%20%3D%20this._getClassWeight(node)%3B%0A%20%20%20%20%20%20const%20contentScore%20%3D%200%3B%0A%0A%20%20%20%20%20%20this.log(%22Cleaning%20Conditionally%22%2C%20node)%3B%0A%0A%20%20%20%20%20%20if%20(weight%20%2B%20contentScore%20%3C%200)%20%7B%0A%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20if%20(this._getCharCount(node%2C%20%22%2C%22)%20%3C%2010)%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20If%20there%20are%20not%20very%20many%20commas%2C%20and%20the%20number%20of%0A%20%20%20%20%20%20%20%20%2F%2F%20non-paragraph%20elements%20is%20more%20than%20paragraphs%20or%20other%0A%20%20%20%20%20%20%20%20%2F%2F%20ominous%20signs%2C%20remove%20the%20element.%0A%20%20%20%20%20%20%20%20const%20p%20%3D%20node.getElementsByTagName(%22p%22).length%3B%0A%20%20%20%20%20%20%20%20const%20img%20%3D%20node.getElementsByTagName(%22img%22).length%3B%0A%20%20%20%20%20%20%20%20const%20li%20%3D%20node.getElementsByTagName(%22li%22).length%20-%20100%3B%0A%20%20%20%20%20%20%20%20const%20input%20%3D%20node.getElementsByTagName(%22input%22).length%3B%0A%0A%20%20%20%20%20%20%20%20let%20embedCount%20%3D%200%3B%0A%20%20%20%20%20%20%20%20const%20embeds%20%3D%20this._getAllNodesWithTag(node%2C%20%5B%20%22object%22%2C%20%22embed%22%2C%20%22iframe%22%20%5D)%3B%0A%0A%20%20%20%20%20%20%20%20for%20(let%20i%20%3D%200%3B%20i%20%3C%20embeds.length%3B%20i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20If%20this%20embed%20has%20attribute%20that%20matches%20video%20regex%2C%20don't%20delete%20it.%0A%20%20%20%20%20%20%20%20%20%20for%20(let%20j%20%3D%200%3B%20j%20%3C%20embeds%5Bi%5D.attributes.length%3B%20j%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(this.REGEXPS.videos.test(embeds%5Bi%5D.attributes%5Bj%5D.value))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%2F%2F%20For%20embed%20with%20%3Cobject%3E%20tag%2C%20check%20inner%20HTML%20as%20well.%0A%20%20%20%20%20%20%20%20%20%20if%20(embeds%5Bi%5D.tagName%20%3D%3D%3D%20%22object%22%20%26%26%20this.REGEXPS.videos.test(embeds%5Bi%5D.innerHTML))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20embedCount%2B%2B%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20const%20linkDensity%20%3D%20this._getLinkDensity(node)%3B%0A%20%20%20%20%20%20%20%20const%20contentLength%20%3D%20this._getInnerText(node).length%3B%0A%0A%20%20%20%20%20%20%20%20const%20haveToRemove%0A%20%20%20%20%20%20%20%20%20%20%3D%20(img%20%3E%201%20%26%26%20p%20%2F%20img%20%3C%200.5%20%26%26%20!this._hasAncestorTag(node%2C%20%22figure%22))%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20(!isList%20%26%26%20li%20%3E%20p)%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20(input%20%3E%20Math.floor(p%20%2F%203))%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20(!isList%20%26%26%20contentLength%20%3C%2025%20%26%26%20(img%20%3D%3D%3D%200%20%7C%7C%20img%20%3E%202)%20%26%26%20!this._hasAncestorTag(node%2C%20%22figure%22))%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20(!isList%20%26%26%20weight%20%3C%2025%20%26%26%20linkDensity%20%3E%200.2)%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20(weight%20%3E%3D%2025%20%26%26%20linkDensity%20%3E%200.5)%0A%20%20%20%20%20%20%20%20%20%20%7C%7C%20((embedCount%20%3D%3D%3D%201%20%26%26%20contentLength%20%3C%2075)%20%7C%7C%20embedCount%20%3E%201)%3B%0A%20%20%20%20%20%20%20%20return%20haveToRemove%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Clean%20out%20elements%20that%20match%20the%20specified%20conditions%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40param%20Function%20determines%20whether%20a%20node%20should%20be%20removed%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20_cleanMatchedNodes(e%2C%20filter)%20%7B%0A%20%20%20%20const%20endOfSearchMarkerNode%20%3D%20this._getNextNode(e%2C%20true)%3B%0A%20%20%20%20let%20next%20%3D%20this._getNextNode(e)%3B%0A%20%20%20%20while%20(next%20%26%26%20next%20!%3D%20endOfSearchMarkerNode)%20%7B%0A%20%20%20%20%20%20if%20(filter.call(this%2C%20next%2C%20next.className%20%2B%20%22%20%22%20%2B%20next.id))%20%7B%0A%20%20%20%20%20%20%20%20next%20%3D%20this._removeAndGetNext(next)%3B%0A%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20next%20%3D%20this._getNextNode(next)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Clean%20out%20spurious%20headers%20from%20an%20Element.%20Checks%20things%20like%20classnames%20and%20link%20density.%0A%20%20%20*%0A%20%20%20*%20%40param%20Element%0A%20%20%20*%20%40return%20void%0A%20%20**%2F%0A%20%20_cleanHeaders(e)%20%7B%0A%20%20%20%20this._removeNodes(this._getAllNodesWithTag(e%2C%20%5B%20%22h1%22%2C%20%22h2%22%20%5D)%2C%20function(header)%20%7B%0A%20%20%20%20%20%20return%20this._getClassWeight(header)%20%3C%200%3B%0A%20%20%20%20%7D)%3B%0A%20%20%7D%2C%0A%0A%20%20_flagIsActive(flag)%20%7B%0A%20%20%20%20return%20(this._flags%20%26%20flag)%20%3E%200%3B%0A%20%20%7D%2C%0A%0A%20%20_removeFlag(flag)%20%7B%0A%20%20%20%20this._flags%20%3D%20this._flags%20%26%20~flag%3B%0A%20%20%7D%2C%0A%0A%20%20_isProbablyVisible(node)%20%7B%0A%20%20%20%20%2F%2F%20Have%20to%20null-check%20node.style%20and%20node.className.indexOf%20to%20deal%20with%20SVG%20and%20MathML%20nodes.%0A%20%20%20%20return%20(!node.style%20%7C%7C%20node.style.display%20!%3D%20%22none%22)%0A%20%20%20%20%20%20%26%26%20!node.hasAttribute(%22hidden%22)%0A%20%20%20%20%20%20%2F%2F%20check%20for%20%22fallback-image%22%20so%20that%20wikimedia%20math%20images%20are%20displayed%0A%20%20%20%20%20%20%26%26%20(!node.hasAttribute(%22aria-hidden%22)%20%7C%7C%20node.getAttribute(%22aria-hidden%22)%20!%3D%20%22true%22%20%7C%7C%20(node.className%20%26%26%20node.className.indexOf%20%26%26%20node.className.indexOf(%22fallback-image%22)%20!%3D%3D%20-1))%3B%0A%20%20%7D%2C%0A%0A%20%20%2F**%0A%20%20%20*%20Runs%20readability.%0A%20%20%20*%0A%20%20%20*%20Workflow%3A%0A%20%20%20*%20%201.%20Prep%20the%20document%20by%20removing%20script%20tags%2C%20css%2C%20etc.%0A%20%20%20*%20%202.%20Build%20readability's%20DOM%20tree.%0A%20%20%20*%20%203.%20Grab%20the%20article%20content%20from%20the%20current%20dom%20tree.%0A%20%20%20*%20%204.%20Replace%20the%20current%20DOM%20tree%20with%20the%20new%20one.%0A%20%20%20*%20%205.%20Read%20peacefully.%0A%20%20%20*%0A%20%20%20*%20%40return%20void%0A%20%20%20**%2F%0A%20%20parse()%20%7B%0A%20%20%20%20%2F%2F%20Avoid%20parsing%20too%20large%20documents%2C%20as%20per%20configuration%20option%0A%20%20%20%20if%20(this._maxElemsToParse%20%3E%200)%20%7B%0A%20%20%20%20%20%20const%20numTags%20%3D%20this._doc.getElementsByTagName(%22*%22).length%3B%0A%20%20%20%20%20%20if%20(numTags%20%3E%20this._maxElemsToParse)%20%7B%0A%20%20%20%20%20%20%20%20throw%20new%20Error(%22Aborting%20parsing%20document%3B%20%22%20%2B%20numTags%20%2B%20%22%20elements%20found%22)%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20%2F%2F%20Unwrap%20image%20from%20noscript%0A%20%20%20%20this._unwrapNoscriptImages(this._doc)%3B%0A%0A%20%20%20%20%2F%2F%20Remove%20script%20tags%20from%20the%20document.%0A%20%20%20%20this._removeScripts(this._doc)%3B%0A%0A%20%20%20%20this._prepDocument()%3B%0A%0A%20%20%20%20const%20metadata%20%3D%20this._getArticleMetadata()%3B%0A%20%20%20%20this._articleTitle%20%3D%20metadata.title%3B%0A%0A%20%20%20%20const%20articleContent%20%3D%20this._grabArticle()%3B%0A%20%20%20%20if%20(!articleContent)%20%7B%0A%20%20%20%20%20%20return%20null%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20this.log(%22Grabbed%3A%20%22%20%2B%20articleContent.innerHTML)%3B%0A%0A%20%20%20%20this._postProcessContent(articleContent)%3B%0A%0A%20%20%20%20%2F%2F%20If%20we%20haven't%20found%20an%20excerpt%20in%20the%20article's%20metadata%2C%20use%20the%20article's%0A%20%20%20%20%2F%2F%20first%20paragraph%20as%20the%20excerpt.%20This%20is%20used%20for%20displaying%20a%20preview%20of%0A%20%20%20%20%2F%2F%20the%20article's%20content.%0A%20%20%20%20if%20(!metadata.excerpt)%20%7B%0A%20%20%20%20%20%20const%20paragraphs%20%3D%20articleContent.getElementsByTagName(%22p%22)%3B%0A%20%20%20%20%20%20if%20(paragraphs.length%20%3E%200)%20%7B%0A%20%20%20%20%20%20%20%20metadata.excerpt%20%3D%20paragraphs%5B0%5D.textContent.trim()%3B%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20const%20textContent%20%3D%20articleContent.textContent%3B%0A%20%20%20%20return%20%7B%0A%20%20%20%20%20%20title%3A%20this._articleTitle%2C%0A%20%20%20%20%20%20byline%3A%20metadata.byline%20%7C%7C%20this._articleByline%2C%0A%20%20%20%20%20%20dir%3A%20this._articleDir%2C%0A%20%20%20%20%20%20content%3A%20articleContent.innerHTML%2C%0A%20%20%20%20%20%20textContent%2C%0A%20%20%20%20%20%20length%3A%20textContent.length%2C%0A%20%20%20%20%20%20excerpt%3A%20metadata.excerpt%2C%0A%20%20%20%20%20%20siteName%3A%20metadata.siteName%20%7C%7C%20this._articleSiteName%2C%0A%20%20%20%20%7D%3B%0A%20%20%7D%2C%0A%7D%3B%0A%0A%0A%0A%2F*%0A%20*%0A%20*%20Send%20to%20Obsidian%20%0A%20*%0A%20*%2F%0A%0A%0A%0A%0A%2F*%20parse%20and%20lightly%20clean%20the%20site's%20meta%20keywords%20content%20into%20tags%2C%20if%20present%20*%2F%0Aif%20(document.querySelector('meta%5Bname%3D%22keywords%22%20i%5D'))%20%7B%0A%20%20%20%20var%20keywords%20%3D%20document.querySelector('meta%5Bname%3D%22keywords%22%20i%5D').getAttribute('content').split('%2C')%3B%0A%0A%20%20%20%20keywords.forEach(function%20(keyword)%20%7B%0A%20%20%20%20%20%20%20%20let%20tag%20%3D%20'%20'%20%2B%20keyword.split('%20').join('')%3B%0A%20%20%20%20%20%20%20%20tags%20%2B%3D%20tag%3B%0A%20%20%20%20%7D)%3B%0A%7D%0A%0Afunction%20getSelectionHtml()%20%7B%0A%20%20%20%20var%20html%20%3D%20%22%22%3B%0A%20%20%20%20if%20(typeof%20window.getSelection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20var%20sel%20%3D%20window.getSelection()%3B%0A%20%20%20%20%20%20%20%20if%20(sel.rangeCount)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20container%20%3D%20document.createElement(%22div%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20(var%20i%20%3D%200%2C%20len%20%3D%20sel.rangeCount%3B%20i%20%3C%20len%3B%20%2B%2Bi)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20container.appendChild(sel.getRangeAt(i).cloneContents())%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20html%20%3D%20container.innerHTML%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%20else%20if%20(typeof%20document.selection%20!%3D%20%22undefined%22)%20%7B%0A%20%20%20%20%20%20%20%20if%20(document.selection.type%20%3D%3D%20%22Text%22)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20html%20%3D%20document.selection.createRange().htmlText%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20return%20html%3B%0A%7D%0A%0Aconst%20selection%20%3D%20getSelectionHtml()%3B%0A%0Aconst%20%7B%0A%20%20%20%20title%2C%0A%20%20%20%20byline%2C%0A%20%20%20%20content%0A%7D%20%3D%20new%20Readability(document.cloneNode(true)).parse()%3B%0A%0Afunction%20getFileName(fileName)%20%7B%0A%20%20%20%20var%20userAgent%20%3D%20window.navigator.userAgent%2C%0A%20%20%20%20%20%20%20%20platform%20%3D%20window.navigator.platform%2C%0A%20%20%20%20%20%20%20%20windowsPlatforms%20%3D%20%5B'Win32'%2C%20'Win64'%2C%20'Windows'%2C%20'WinCE'%5D%3B%0A%0A%20%20%20%20if%20(windowsPlatforms.indexOf(platform)%20!%3D%3D%20-1)%20%7B%0A%20%20%20%20%20%20%20%20fileName%20%3D%20fileName.replace('%3A'%2C%20'').replace(%2F%5B%2F%5C%5C%3F%25*%7C%22%3C%3E%5D%2Fg%2C%20'-')%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20fileName%20%3D%20fileName.replace('%3A'%2C%20'').replace(%2F%5C%2F%2Fg%2C%20'-').replace(%2F%5C%5C%2Fg%2C%20'-')%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20fileName%3B%0A%7D%0Aconst%20fileName%20%3D%20getFileName(title)%3B%0A%0Aif%20(selection)%20%7B%0A%20%20%20%20var%20markdownify%20%3D%20selection%3B%0A%7D%20else%20%7B%0A%20%20%20%20var%20markdownify%20%3D%20content%3B%0A%7D%0A%0Aif%20(vault)%20%7B%0A%20%20%20%20var%20vaultName%20%3D%20'%26vault%3D'%20%2B%20encodeURIComponent(%60%24%7Bvault%7D%60)%3B%0A%7D%20else%20%7B%0A%20%20%20%20var%20vaultName%20%3D%20''%3B%0A%7D%0A%0Aconst%20markdownBody%20%3D%20new%20TurndownService(%7B%0A%20%20%20%20headingStyle%3A%20'atx'%2C%0A%20%20%20%20hr%3A%20'~~~'%2C%0A%20%20%20%20bulletListMarker%3A%20'-'%2C%0A%20%20%20%20codeBlockStyle%3A%20'fenced'%2C%0A%20%20%20%20emDelimiter%3A%20'*'%2C%0A%7D).turndown(markdownify)%3B%0A%0Avar%20date%20%3D%20new%20Date()%3B%0A%0Afunction%20convertDate(date)%20%7B%0A%20%20%20%20var%20yyyy%20%3D%20date.getFullYear().toString()%3B%0A%20%20%20%20var%20mm%20%3D%20(date.getMonth()%20%2B%201).toString()%3B%0A%20%20%20%20var%20dd%20%3D%20date.getDate().toString()%3B%0A%20%20%20%20var%20mmChars%20%3D%20mm.split('')%3B%0A%20%20%20%20var%20ddChars%20%3D%20dd.split('')%3B%0A%20%20%20%20return%20yyyy%20%2B%20'-'%20%2B%20(mmChars%5B1%5D%20%3F%20mm%20%3A%20%220%22%20%2B%20mmChars%5B0%5D)%20%2B%20'-'%20%2B%20(ddChars%5B1%5D%20%3F%20dd%20%3A%20%220%22%20%2B%20ddChars%5B0%5D)%3B%0A%7D%0A%0Aconst%20today%20%3D%20convertDate(date)%3B%0A%0A%2F*%20YAML%20front%20matter%20as%20tags%20render%20cleaner%20with%20special%20chars%20%20*%2F%0Aconst%20fileContent%20%3D%0A%20%20%20%20%22---%5Cn%22%0A%20%20%20%20%2B%20%22author%3A%20%20%20%20%22%20%2B%20byline%20%2B%20%22%5Cn%22%0A%20%20%20%20%2B%20%22title%3A%20%20%20%20%20%5B%22%20%2B%20title%20%2B%20%22%5D%5Cn%22%0A%20%20%20%20%2B%20%22source%3A%20%20%20%20%22%20%2B%20document.URL%20%2B%20%22%5Cn%22%0A%20%20%20%20%2B%20%22clip%20date%3A%20%22%20%2B%20today%20%2B%20%22%5Cn%22%0A%20%20%20%20%2B%20%22published%3A%20%5Cn%5Cn%22%0A%20%20%20%20%2B%20%22tags%3A%20%20%20%20%20%20%5B%22%20%2B%20tags%20%2B%20%22%5D%5Cn%22%0A%20%20%20%20%2B%20%22---%5Cn%5Cn%22%0A%20%20%20%20%2B%20markdownBody%0A%20%20%20%20%2B%20%22%5Cn%5Cn---%5Cn%22%20%2B%20String(markdownBody.length)%20%2B%20%22%20chars%20clipped%20from%20%22%0A%20%20%20%20%2B%20%22%5B%22%20%2B%20title%20%2B%20%22%5D(%22%20%2B%20document.URL%20%2B%20%22)%5Cn%22%3B%0A%0A%0A%2F%2F%20This%20function%20must%20be%20called%20in%20a%20visible%20page%2C%20such%20as%20a%20browserAction%20popup%0A%2F%2F%20or%20a%20content%20script.%20Calling%20it%20in%20a%20background%20page%20has%20no%20effect!%0Aasync%20function%20copyContent()%20%7B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20await%20navigator.clipboard.writeText(fileContent)%3B%0A%20%20%20%20%7D%20catch%20(err)%20%7B%0A%20%20%20%20%20%20%20%20console.error('Failed%20to%20copy%3A%20'%2C%20err)%3B%0A%20%20%20%20%7D%0A%7D%0A%0AcopyContent()%3B%0A%0Adocument.location.href%20%3D%20%22obsidian%3A%2F%2Fadvanced-uri%3F%22%0A%20%20%20%20%2B%20%22vault%3D%22%20%2B%20vaultName%0A%20%20%20%20%2B%20%22%26clipboard%3Dtrue%22%0A%20%20%20%20%2B%20%22%26mode%3Dnew%22%0A%20%20%20%20%2B%20%22%26filepath%3D%22%20%2B%20encodeURIComponent(folder%20%2B%20fileName)%3B%7D)()%3B
```
3. Rename bookmark to `💾Obsidian.Save`
4. Click/tap on bookmark while on desired page
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment