module RedCloth::Formatters::HTML
include RedCloth::Formatters::Base
[:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
define_method(m) do |opts|
"<#{m}#{pba(opts)}>#{opts[:text]}#{m}>\n"
end
end
[:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
define_method(m) do |opts|
opts[:block] = true
"<#{m}#{pba(opts)}>#{opts[:text]}#{m}>"
end
end
def hr(opts)
"
\n"
end
def acronym(opts)
opts[:block] = true
"#{caps(:text => opts[:text])}"
end
def caps(opts)
if no_span_caps
opts[:text]
else
opts[:class] = 'caps'
span(opts)
end
end
def del(opts)
opts[:block] = true
"#{opts[:text]}"
end
[:ol, :ul].each do |m|
define_method("#{m}_open") do |opts|
opts[:block] = true
"#{"\n" if opts[:nest] > 1}#{"\t" * (opts[:nest] - 1)}<#{m}#{pba(opts)}>\n"
end
define_method("#{m}_close") do |opts|
"#{"\t" * (opts[:nest] - 1)}#{m}>#{"\n" if opts[:nest] <= 1}"
end
end
def li_open(opts)
"#{"\t" * opts[:nest]}#{opts[:text]}"
end
def li_close(opts=nil)
"\n"
end
def dl_open(opts)
opts[:block] = true
"\n"
end
def dl_close(opts=nil)
"
\n"
end
[:dt, :dd].each do |m|
define_method(m) do |opts|
"\t<#{m}#{pba(opts)}>#{opts[:text]}#{m}>\n"
end
end
def td(opts)
tdtype = opts[:th] ? 'th' : 'td'
"\t\t<#{tdtype}#{pba(opts)}>#{opts[:text]}#{tdtype}>\n"
end
def tr_open(opts)
"\t\n"
end
def tr_close(opts)
"\t
\n"
end
def table_open(opts)
"\n"
end
def table_close(opts)
"
\n"
end
def bc_open(opts)
opts[:block] = true
""
end
def bc_close(opts)
"
\n"
end
def bq_open(opts)
opts[:block] = true
cite = opts[:cite] ? " cite=\"#{ escape_attribute opts[:cite] }\"" : ''
"\n"
end
def bq_close(opts)
"
\n"
end
def link(opts)
"#{opts[:name]}"
end
def truncate(text, length = 30, truncate_string = "...")
if text.nil? then return end
l = length - truncate_string.chars.to_a.size
(text.chars.to_a.size > length ? text.chars.to_a[0...l].join + truncate_string : text).to_s
end
def auto_link(opts)
return opts[:href] unless auto_link_urls
href_with_proto = opts[:href]
href_with_proto = 'http://' + href_with_proto unless href_with_proto.index('http') == 0
text = opts[:href]
text = truncate(text[0, text.length - 6] + text[-5,3].replace("...") + text[-2..-1], 50)
"#{escape_attribute text}"
end
def image(opts)
opts.delete(:align)
opts[:alt] = opts[:title]
img = ""
img = "#{img}" if opts[:href]
img
end
def footno(opts)
opts[:id] ||= opts[:text]
%Q{}
end
def fn(opts)
no = opts[:id]
opts[:id] = "fn#{no}"
opts[:class] = ["footnote", opts[:class]].compact.join(" ")
"#{no} #{opts[:text]}
\n"
end
def snip(opts)
"#{opts[:text]}
\n"
end
def quote1(opts)
"‘#{opts[:text]}’"
end
def quote2(opts)
"“#{opts[:text]}”"
end
def multi_paragraph_quote(opts)
"“#{opts[:text]}"
end
def ellipsis(opts)
"#{opts[:text]}…"
end
def emdash(opts)
"—"
end
def endash(opts)
" – "
end
def arrow(opts)
"→"
end
def dim(opts)
opts[:text].gsub!('x', '×')
opts[:text].gsub!("'", '′')
opts[:text].gsub!('"', '″')
opts[:text]
end
def trademark(opts)
"™"
end
def registered(opts)
"®"
end
def copyright(opts)
"©"
end
def entity(opts)
"{opts[:text]};"
end
def amp(opts)
"&"
end
def gt(opts)
">"
end
def lt(opts)
"<"
end
def br(opts)
if hard_breaks == false
"\n"
else
"
\n"
end
end
def quot(opts)
"""
end
def squot(opts)
"’"
end
def apos(opts)
"'"
end
def html(opts)
"#{opts[:text]}\n"
end
def html_block(opts)
inline_html(:text => "#{opts[:indent_before_start]}#{opts[:start_tag]}#{opts[:indent_after_start]}") +
"#{opts[:text]}" +
inline_html(:text => "#{opts[:indent_before_end]}#{opts[:end_tag]}#{opts[:indent_after_end]}")
end
def notextile(opts)
if filter_html
html_esc(opts[:text], :html_escape_preformatted)
else
opts[:text]
end
end
def inline_html(opts)
if filter_html
html_esc(opts[:text], :html_escape_preformatted)
else
"#{opts[:text]}" # nil-safe
end
end
def ignored_line(opts)
opts[:text] + "\n"
end
private
# escapement for regular HTML (not in PRE tag)
def escape(text)
html_esc(text)
end
# escapement for HTML in a PRE tag
def escape_pre(text)
html_esc(text, :html_escape_preformatted)
end
# escaping for HTML attributes
def escape_attribute(text)
html_esc(text, :html_escape_attributes)
end
def after_transform(text)
text.chomp!
end
def before_transform(text)
clean_html(text) if sanitize_html
end
# HTML cleansing stuff
BASIC_TAGS = {
'a' => ['href', 'title'],
'img' => ['src', 'alt', 'title'],
'br' => [],
'i' => nil,
'u' => nil,
'b' => nil,
'pre' => nil,
'kbd' => nil,
'code' => ['lang'],
'cite' => nil,
'strong' => nil,
'em' => nil,
'ins' => nil,
'sup' => nil,
'sub' => nil,
'del' => nil,
'table' => nil,
'tr' => nil,
'td' => ['colspan', 'rowspan'],
'th' => nil,
'ol' => ['start'],
'ul' => nil,
'li' => nil,
'p' => nil,
'h1' => nil,
'h2' => nil,
'h3' => nil,
'h4' => nil,
'h5' => nil,
'h6' => nil,
'blockquote' => ['cite'],
'notextile' => nil
}
# Clean unauthorized tags.
def clean_html( text, allowed_tags = BASIC_TAGS )
text.gsub!( /]*?)(\s?\/?)>/ ) do |m|
raw = $~
tag = raw[2].downcase
if allowed_tags.has_key? tag
pcs = [tag]
allowed_tags[tag].each do |prop|
['"', "'", ''].each do |q|
q2 = ( q != '' ? q : '\s' )
if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
attrv = $1
next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
break
end
end
end if allowed_tags[tag]
"<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
else # Unauthorized tag
if block_given?
yield m
else
''
end
end
end
end
end