commit
f4433ccf4c
@ -1,3 +1,7 @@
|
|||||||
|
- **New feature**: `Linkify` and `Clean Links`, enabled by default
|
||||||
|
- Linkify will turn text URLs into working links.
|
||||||
|
- Clean Links will get rid of spoiler and code tags in linkified URLs used to bypass spam blocks.
|
||||||
|
|
||||||
## 3.9.0 - *2013-08-18*
|
## 3.9.0 - *2013-08-18*
|
||||||
|
|
||||||
- **New feature**: `Desktop Notifications`
|
- **New feature**: `Desktop Notifications`
|
||||||
|
|||||||
@ -31,6 +31,7 @@ module.exports = (grunt) ->
|
|||||||
'src/Quotelinks/**/*'
|
'src/Quotelinks/**/*'
|
||||||
'src/Posting/**/*'
|
'src/Posting/**/*'
|
||||||
'src/Images/**/*'
|
'src/Images/**/*'
|
||||||
|
'src/Linkification/**/*'
|
||||||
'src/Menu/**/*'
|
'src/Menu/**/*'
|
||||||
'src/Monitoring/**/*'
|
'src/Monitoring/**/*'
|
||||||
'src/Archive/**/*'
|
'src/Archive/**/*'
|
||||||
|
|||||||
@ -27,6 +27,9 @@ Config =
|
|||||||
'Image Hover': [false, 'Show a floating expanded image on hover.']
|
'Image Hover': [false, 'Show a floating expanded image on hover.']
|
||||||
'Sauce': [true, 'Add sauce links to images.']
|
'Sauce': [true, 'Add sauce links to images.']
|
||||||
'Reveal Spoilers': [false, 'Reveal spoiler thumbnails.']
|
'Reveal Spoilers': [false, 'Reveal spoiler thumbnails.']
|
||||||
|
'Linkification':
|
||||||
|
'Linkify': [true, 'Convert text links into hyperlinks.']
|
||||||
|
'Clean Links': [true, 'Remove spoiler and code tags commonly used to bypass blocked links.']
|
||||||
'Menu':
|
'Menu':
|
||||||
'Menu': [true, 'Add a drop-down menu to posts.']
|
'Menu': [true, 'Add a drop-down menu to posts.']
|
||||||
'Report Link': [true, 'Add a report link to the menu.']
|
'Report Link': [true, 'Add a report link to the menu.']
|
||||||
|
|||||||
@ -128,6 +128,7 @@ Main =
|
|||||||
initFeature 'Index Navigation', Nav
|
initFeature 'Index Navigation', Nav
|
||||||
initFeature 'Keybinds', Keybinds
|
initFeature 'Keybinds', Keybinds
|
||||||
initFeature 'Show Dice Roll', Dice
|
initFeature 'Show Dice Roll', Dice
|
||||||
|
initFeature 'Linkify', Linkify
|
||||||
# c.timeEnd 'All initializations'
|
# c.timeEnd 'All initializations'
|
||||||
|
|
||||||
$.on d, 'AddCallback', Main.addCallback
|
$.on d, 'AddCallback', Main.addCallback
|
||||||
|
|||||||
@ -56,6 +56,8 @@ class Post
|
|||||||
@kill() if that.isArchived
|
@kill() if that.isArchived
|
||||||
|
|
||||||
parseComment: ->
|
parseComment: ->
|
||||||
|
# Merge text nodes and remove empty ones.
|
||||||
|
@nodes.comment.normalize()
|
||||||
# Get the comment's text.
|
# Get the comment's text.
|
||||||
# <br> -> \n
|
# <br> -> \n
|
||||||
# Remove:
|
# Remove:
|
||||||
|
|||||||
104
src/Linkification/Linkify.coffee
Normal file
104
src/Linkification/Linkify.coffee
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
Linkify =
|
||||||
|
init: ->
|
||||||
|
return if g.VIEW is 'catalog' or !Conf['Linkify']
|
||||||
|
|
||||||
|
# gruber revised + magnet support
|
||||||
|
# http://rodneyrehm.de/t/url-regex.html
|
||||||
|
@catchAll = /\b([a-z][\w-]+:(\/{1,3}|[a-z0-9%]|\?(dn|x[lts]|as|kt|mt|tr)=)|www\d{0,3}\.|[a-z0-9.\-]+\.[a-z]{2,4}\/)([^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/g
|
||||||
|
|
||||||
|
Post::callbacks.push
|
||||||
|
name: 'Linkify'
|
||||||
|
cb: @node
|
||||||
|
|
||||||
|
node: ->
|
||||||
|
return if @isClone or !links = @info.comment.match Linkify.catchAll
|
||||||
|
walker = d.createTreeWalker @nodes.comment, 4
|
||||||
|
range = d.createRange()
|
||||||
|
for link in links
|
||||||
|
boundaries = Linkify.find link, walker
|
||||||
|
# break unless boundaries
|
||||||
|
anchor = Linkify.createLink link
|
||||||
|
if Linkify.surround anchor, range, boundaries
|
||||||
|
if (parent = anchor.parentNode).href is anchor.href
|
||||||
|
# Replace already-linkified links,
|
||||||
|
# f.e.: https://boards.4chan.org/b/%
|
||||||
|
$.replace parent, anchor
|
||||||
|
Linkify.cleanLink anchor, link if Conf['Clean Links']
|
||||||
|
walker.currentNode = anchor.lastChild
|
||||||
|
else
|
||||||
|
walker.currentNode = boundaries.endNode
|
||||||
|
range.detach()
|
||||||
|
|
||||||
|
find: (link, walker) ->
|
||||||
|
# Walk through the nodes until we find the entire link.
|
||||||
|
text = ''
|
||||||
|
while node = walker.nextNode()
|
||||||
|
text += node.data
|
||||||
|
break if text.indexOf(link) > -1
|
||||||
|
# return unless node
|
||||||
|
startNode = endNode = node
|
||||||
|
|
||||||
|
# Walk backwards to find the startNode.
|
||||||
|
text = node.data
|
||||||
|
until (index = text.indexOf link) > -1
|
||||||
|
startNode = walker.previousNode()
|
||||||
|
text = "#{startNode.data}#{text}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
startNode, endNode
|
||||||
|
startOffset: index
|
||||||
|
endOffset: endNode.length - (text.length - index - link.length)
|
||||||
|
}
|
||||||
|
|
||||||
|
createLink: (link) ->
|
||||||
|
unless /^[a-z][\w-]+:/.test link
|
||||||
|
link = "http://#{link}"
|
||||||
|
$.el 'a',
|
||||||
|
href: link
|
||||||
|
className: 'linkified'
|
||||||
|
target: '_blank'
|
||||||
|
|
||||||
|
surround: (anchor, range, boundaries) ->
|
||||||
|
{startOffset, endOffset, startNode, endNode} = boundaries
|
||||||
|
range.setStart startNode, startOffset
|
||||||
|
range.setEnd endNode, endOffset
|
||||||
|
try
|
||||||
|
range.surroundContents anchor
|
||||||
|
true
|
||||||
|
catch
|
||||||
|
<% if (type === 'crx') { %>
|
||||||
|
# Chrome bug: crbug.com/275848
|
||||||
|
return true if anchor.parentNode
|
||||||
|
<% } %>
|
||||||
|
# Attempt to handle cases such as:
|
||||||
|
# [spoiler]www.[/spoiler]example.com #
|
||||||
|
# www.example[spoiler].com[/spoiler] #
|
||||||
|
return false if boundaries.areRelocated
|
||||||
|
Linkify.relocate boundaries
|
||||||
|
Linkify.surround anchor, range, boundaries
|
||||||
|
|
||||||
|
relocate: (boundaries) ->
|
||||||
|
# What do you mean, "silly"?
|
||||||
|
boundaries.areRelocated = true
|
||||||
|
|
||||||
|
if boundaries.startOffset is 0
|
||||||
|
parentNode = boundaries.startNode
|
||||||
|
until parentNode.previousSibling
|
||||||
|
{parentNode} = parentNode
|
||||||
|
parent = parentNode.parentNode
|
||||||
|
boundaries.startNode = parent
|
||||||
|
boundaries.startOffset = [parent.childNodes...].indexOf parentNode
|
||||||
|
|
||||||
|
if boundaries.endOffset is boundaries.endNode.length
|
||||||
|
parentNode = boundaries.endNode
|
||||||
|
until parentNode.nextSibling
|
||||||
|
{parentNode} = parentNode
|
||||||
|
parent = parentNode.parentNode
|
||||||
|
boundaries.endNode = parent
|
||||||
|
boundaries.endOffset = [parent.childNodes...].indexOf(parentNode) + 1
|
||||||
|
|
||||||
|
cleanLink: (anchor, link) ->
|
||||||
|
{length} = link
|
||||||
|
for node in $$ 's, .prettyprint', anchor
|
||||||
|
$.replace node, [node.childNodes...] if length > node.textContent.length
|
||||||
|
return
|
||||||
@ -72,3 +72,5 @@ ExpandComment =
|
|||||||
Fourchan.code.call post
|
Fourchan.code.call post
|
||||||
if g.BOARD.ID is 'sci'
|
if g.BOARD.ID is 'sci'
|
||||||
Fourchan.math.call post
|
Fourchan.math.call post
|
||||||
|
if Conf['Linkify']
|
||||||
|
Linkify.node.call post
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user