#
# Linko is a simple filter to ease linking for common words.
#
# Linko::WORDS holds a Hash that maps words to URLs, feel free
# to +replace+ it of you want, or just add your common links.
#
# Each occurence of myword_
will be translated into a
# footnote-style link. Each occurence of myword__
will
# be translated into a link with the word as text.
#
# Linko uses the standard *Cloth API, that is:
#
# Linko.new(mytext).to_html
#
# Linko tries to be minimal invasive and will not do link replacement
# inside tags like +pre+, +code+ and +script+.
#
class Linko < String
# Word -> HTML mapping for Linko substitution.
WORDS = {
'Ruby' => 'http://www.ruby-lang.org/',
'Anarchaia' => 'http://chneukirchen.org/anarchaia/',
'chris blogs' => 'http://chneukirchen.org/blog/',
'Linko' => 'http://chneukirchen.org/blog/archive/2005/06/introducing-linko.html',
'Darcs' => 'http://darcs.net'
}
# Substitute Linko tags in the string, return HTML with proper links.
def to_html
result = ""
apply = true
footnotes = assign_footnotes
tokenize { |kind, text|
if kind == :tag
result << text
# Don't mess around in scripts and code.
if text =~ %r!<(/?)(?:pre|code|kbd|script|math)[\s>]!
apply = ($1 == "/") # Opening or closing tag?
end
else
apply && WORDS.each { |word, url|
text.gsub!(/#{Regexp.quote word}__/) {
%Q{#{word}}
}
text.gsub!(/#{Regexp.quote word}_/) {
%Q{#{word}#{footnotes[word]}}
}
}
result << text
end
}
result
end
private
def assign_footnotes
footnotes = {}
WORDS.keys.find_all { |word| index_of word }. # What words do appear?
sort_by { |word| index_of word }. # Where do they appear?
each_with_index { |word, i|
footnotes[word] = i + 1 # Assign a sequential number.
}
footnotes
end
# Match a single underscore word.
def index_of(word)
index /#{Regexp.quote(word)}_(?!_)/
end
TAG_SOUP = /\G([^<]*)(<[^>]*>)/
# Small, little and probably horrible tag_soup-style parser.
def tokenize(&block)
tokens = []
prev_end = 0
scan(TAG_SOUP) {
block.call(:text, $1) if $1 != ""
block.call(:tag, $2)
prev_end = $~.end(0)
}
if prev_end < size
block.call(:text, self[prev_end..-1]) # Flush rest.
end
self
end
end