From 202d7617e3e213fdd1510d2c741db42edb9f4555 Mon Sep 17 00:00:00 2001 From: noface Date: Tue, 18 Jun 2013 19:42:42 +0200 Subject: [PATCH] Add Linkification. --- Gruntfile.coffee | 1 + src/General/Config.coffee | 3 + src/General/Main.coffee | 1 + src/Linkification/Linkify.coffee | 92 ++++++++++++++++++++++++++ src/Miscellaneous/ExpandComment.coffee | 2 + 5 files changed, 99 insertions(+) create mode 100644 src/Linkification/Linkify.coffee diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 2c0ff150b..753ac3df2 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -31,6 +31,7 @@ module.exports = (grunt) -> 'src/Quotelinks/**/*' 'src/Posting/**/*' 'src/Images/**/*' + 'src/Linkification/**/*' 'src/Menu/**/*' 'src/Monitoring/**/*' 'src/Archive/**/*' diff --git a/src/General/Config.coffee b/src/General/Config.coffee index fc174dcfb..b35d0a399 100644 --- a/src/General/Config.coffee +++ b/src/General/Config.coffee @@ -27,6 +27,9 @@ Config = 'Image Hover': [false, 'Show a floating expanded image on hover.'] 'Sauce': [true, 'Add sauce links to images.'] 'Reveal Spoilers': [false, 'Reveal spoiler thumbnails.'] + 'Linkification': + 'Linkify': [true, 'Convert text links into hyperlinks.'] + 'Clean Links': [true, 'Remove spoiler texts commonly used to bypass banned links.'] 'Menu': 'Menu': [true, 'Add a drop-down menu to posts.'] 'Report Link': [true, 'Add a report link to the menu.'] diff --git a/src/General/Main.coffee b/src/General/Main.coffee index 4c1be0574..1aeaafa1f 100644 --- a/src/General/Main.coffee +++ b/src/General/Main.coffee @@ -128,6 +128,7 @@ Main = initFeature 'Index Navigation', Nav initFeature 'Keybinds', Keybinds initFeature 'Show Dice Roll', Dice + initFeature 'Linkify', Linkify # c.timeEnd 'All initializations' $.on d, 'AddCallback', Main.addCallback diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee new file mode 100644 index 000000000..febbc3e42 --- /dev/null +++ b/src/Linkification/Linkify.coffee @@ -0,0 +1,92 @@ +Linkify = + init: -> + return if g.VIEW is 'catalog' or !Conf['Linkify'] + + @catchAll = /\b(?:([a-z][\w-]+):(?:\/{1,3}|[a-z0-9%]|(\?(?:dn|xl|xt|as|xs|kt|mt|tr)=))|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/i + + @globalCatchAll = ///#{@catchAll.source}///g + + Post::callbacks.push + name: 'Linkify' + cb: @node + + node: -> + return if @isClone or !links = @info.comment.match Linkify.globalCatchAll + walker = d.createTreeWalker @nodes.comment, 4, null, false + anchor = false + while (node = walker.nextNode())? + {parentNode} = node + if parentNode.nodeName is 'A' + # prettyprint has some issues + if parentNode.textContent is links[0] + delete links[0] + walker.currentNode = parentNode.lastChild + continue + continue unless data = node.data + while !anchor + return unless link = links.shift() + [anchor, link] = Linkify.parseLink link + if data.length >= link.length and (index = data.indexOf link) >= 0 + walker.currentNode = Linkify.sourround anchor, link, index, index + link.length, node + anchor = false + continue + index = found = 0 + nextContent = node.nextSibling?.textContent + while index isnt data.length + start = data[index++..] + startLength = start.length + threshold = startLength is 2 + if threshold and nextContent and link[...startLength + nextContent.length] is start + nextContent + found = true + if threshold or found = link[...startLength] is start + index-- + break + continue unless found + startNode = node + while start.length < link.length + start += data = (node = walker.nextNode())?.data + {parentNode} = node + if parentNode.nodeName is 'S' and Conf['Clean Links'] + $.replace parentNode, node + continue unless start[...link.length] is link + endIndex = link[start.length - data.length...].length + walker.currentNode = Linkify.sourround anchor, link, index, endIndex, startNode, node + anchor = false + return + + parseLink: (link) -> + unless result = link.match @catchAll + return false + [link, protocol, isMagnet] = result + try + decodeURIComponent link + catch + return false + target = if isMagnet or /^(irc|ftps?)$/.test protocol + '_self' + else + '_blank' + href = if protocol + link + else + "http://#{link}" + anchor = $.el 'a', + target: target + href: href + rel: 'noreferrer' + [anchor, link] + + sourround: (anchor, link, startIndex, endIndex, startNode, endNode = startNode) -> + parent = startNode.parentNode + if parent?.nodeName is 'S' and parent.textContent.length < link.length + parentClone = parent.cloneNode true + $.replace parent, startNode + range = d.createRange() + range.setStart startNode, startIndex + range.setEnd endNode, endIndex + try + range.surroundContents anchor + if !Conf['Clean Links'] and parentClone and anchor.firstChild + $.replace anchor.firstChild, parentClone + catch + endNode diff --git a/src/Miscellaneous/ExpandComment.coffee b/src/Miscellaneous/ExpandComment.coffee index 8d87de91e..3567e9f84 100644 --- a/src/Miscellaneous/ExpandComment.coffee +++ b/src/Miscellaneous/ExpandComment.coffee @@ -72,3 +72,5 @@ ExpandComment = Fourchan.code.call post if g.BOARD.ID is 'sci' Fourchan.math.call post + if Conf['Linkify'] + Linkify.node.call post