Linkify =
init: ->
return if g.VIEW is 'catalog' or not Conf['Linkify']
@types = {}
@types[type.key] = type for type in @ordered_types
if Conf['Comment Expansion']
ExpandComment.callbacks.push @node
Post.callbacks.push
name: 'Linkify'
cb: @node
events: (post) ->
i = 0
items = $$ '.embedder', post.nodes.comment
while el = items[i++]
$.on el, 'click', Linkify.cb.toggle
Linkify.cb.toggle.call el if $.hasClass el, 'embedded'
return
node: ->
return (if Conf['Embedding'] then Linkify.events @ else null) if @isClone
return unless Linkify.regString.test @info.comment
test = /[^\s'"]+/g
space = /[\s'"]/
snapshot = $.X './/br|.//text()', @nodes.comment
i = 0
links = []
while node = snapshot.snapshotItem i++
{data} = node
continue if !data or node.parentElement.nodeName is "A"
while result = test.exec data
{index} = result
endNode = node
word = result[0]
# End of node, not necessarily end of space-delimited string
if (length = index + word.length) is data.length
test.lastIndex = 0
while (saved = snapshot.snapshotItem i++)
if saved.nodeName is 'BR'
break
endNode = saved
{data} = saved
word += data
{length} = data
if end = space.exec data
# Set our snapshot and regex to start on this node at this position when the loop resumes
test.lastIndex = length = end.index
i--
break
links.push Linkify.makeRange node, endNode, index, length if Linkify.regString.exec word
break unless test.lastIndex and node is endNode
i = links.length
while i--
link = Linkify.makeLink links[i]
unless $.x 'ancestor::pre', link
Linkify.embedProcess link, @
return
embedProcess: (link, post) ->
if data = Linkify.services link
data.push post
Linkify.embed data if Conf['Embedding']
Linkify.title data if Conf['Link Title']
regString: ///(
# http, magnet, ftp, etc
(https?|mailto|git|magnet|ftp|irc):(
[a-z\d%/]
)
| # This should account for virtually all links posted without http:
[-a-z\d]+[.](
aero|asia|biz|cat|com|coop|info|int|jobs|mobi|museum|name|net|org|post|pro|tel|travel|xxx|edu|gov|mil|[a-z]{2}
)([:/]|(?!.))
| # IPv4 Addresses
[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}
| # E-mails
[-\w\d.@]+@[a-z\d.-]+\.[a-z\d]
)///i
makeRange: (startNode, endNode, startOffset, endOffset) ->
range = document.createRange()
range.setStart startNode, startOffset
range.setEnd endNode, endOffset
range
makeLink: (range) ->
text = range.toString()
# Clean start of range
i = 0
i++ while /[(\[{<>]/.test text.charAt i
if i
text = text.slice i
i-- while range.startOffset + i >= range.startContainer.data.length
range.setStart range.startContainer, range.startOffset + i if i
# Clean end of range
i = 0
while /[)\]}>.,]/.test t = text.charAt text.length - (1 + i)
break unless /[.,]/.test(t) or (text.match /[()\[\]{}<>]/g).length % 2
i++
if i
text = text.slice 0, -i
i-- while range.endOffset - i < 0
if i
range.setEnd range.endContainer, range.endOffset - i
# Make our link 'valid' if it is formatted incorrectly.
unless /(mailto:|.+:\/\/)/.test text
text = (
if /@/.test text
'mailto:'
else
'http://'
) + text
a = $.el 'a',
className: 'linkify'
rel: 'nofollow noreferrer'
target: '_blank'
href: text
# Insert the range into the anchor, the anchor into the range's DOM location, and destroy the range.
$.add a, range.extractContents()
range.insertNode a
range.detach()
a
services: (link) ->
{href} = link
for type in Linkify.ordered_types when match = type.regExp.exec href
return if type.dummy
return [type.key, match[1], match[2], link]
return
embed: (data) ->
[key, uid, options, link, post] = data
embed = $.el 'a',
className: 'embedder'
href: link.href
textContent: '(embed)'
embed.dataset[name] = value for name, value of {key, uid, options}
$.addClass link, "#{embed.dataset.key}"
$.on embed, 'click', Linkify.cb.toggle
$.after link, [$.tn(' '), embed]
Linkify.cb.toggle.call embed if Conf['Auto-embed']
title: (data) ->
[key, uid, options, link, post] = data
return unless service = Linkify.types[key].title
titles = Conf['CachedTitles']
if title = titles[uid]
link.textContent = title[0]
else
try
$.cache service.api(uid), (-> Linkify.cb.title @, data), responseType: 'json'
catch err
$.extend link, <%= html('[${key}] Title Link Blocked (are you using NoScript?)') %>
return
cb:
toggle: (e) ->
e?.preventDefault()
if $.hasClass @, "embedded"
$.rm @previousElementSibling
@previousElementSibling.hidden = false
@textContent = '(embed)'
else
@previousElementSibling.hidden = true
$.before @, Linkify.cb.embed @
@textContent = '(unembed)'
$.toggleClass @, 'embedded'
embed: (a) ->
# We create an element to embed
el = (type = Linkify.types[a.dataset.key]).el a
# Set style values.
el.style.cssText = if type.style?
type.style
else
"border: 0; width: 640px; height: 390px"
return el
title: (req, data) ->
[key, uid, options, link, post] = data
{status} = req
service = Linkify.types[key].title
text = "[#{key}] #{switch status
when 200, 304
service.text req.response
when 404
"Not Found"
when 403
"Forbidden or Private"
else
"#{status}'d"
}"
link.textContent = text
for post2 in post.clones
for link2 in $$ 'a', post2.nodes.comment when link2.href is link.href
link2.textContent = text
return
ordered_types: [
key: 'audio'
regExp: /(.*\.(mp3|ogg|wav))$/
style: ''
el: (a) ->
$.el 'audio',
controls: true
preload: 'auto'
src: a.dataset.uid
,
key: 'gist'
regExp: /.*(?:gist.github.com.*\/)([^\/][^\/]*)$/
el: (a) ->
el = $.el 'iframe'
el.setAttribute 'sandbox', 'allow-scripts'
content = <%= html('