1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 class TextileFormatter < RedCloth
28 # auto_link rule after textile rules so that it doesn't break !image_url! tags
29 RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros]
34 self.no_span_caps=true
37 def to_html(*rules, &block)
39 @macros_runner = block
45 # Patch for RedCloth. Fixed in RedCloth r128 but _why hasn't released it yet.
46 # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a>
47 def hard_break( text )
48 text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks
51 # Patch to add code highlighting support to RedCloth
52 def smooth_offtags( text )
53 unless @pre_list.empty?
54 ## replace <pre> content
55 text.gsub!(/<redpre#(\d+)>/) do
56 content = @pre_list[$1.to_i]
57 if content.match(/<code\s+class="(\w+)">\s?(.+)/m)
58 content = "<code class=\"#{$1} CodeRay\">" +
59 CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline)
66 # Patch to add 'table of content' support to RedCloth
67 def textile_p_withtoc(tag, atts, cite, content)
69 @toc << [$1.to_i, content]
71 content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
72 textile_p(tag, atts, cite, content)
75 alias :textile_h1 :textile_p_withtoc
76 alias :textile_h2 :textile_p_withtoc
77 alias :textile_h3 :textile_p_withtoc
80 text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
82 div_class << ' right' if $1 == '>'
83 div_class << ' left' if $1 == '<'
84 out = "<div class=\"#{div_class}\">"
85 @toc.each_with_index do |heading, index|
86 # remove wiki links from the item
87 toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
88 out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
100 (\(([^\}]*)\))? # optional arguments
103 /x unless const_defined?(:MACROS_RE)
105 def inline_macros(text)
106 text.gsub!(MACROS_RE) do
107 esc, all, macro = $1, $2, $3.downcase
108 args = ($5 || '').split(',').each(&:strip)
111 @macros_runner.call(macro, args)
113 "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
123 <\w+.*?>| # leading HTML tag, or
124 [^=<>!:'"/]| # leading punctuation, or
125 ^ # beginning of line
128 (?:https?://)| # protocol spec, or
135 ([^\w\=\/;]*?) # post
137 }x unless const_defined?(:AUTO_LINK_RE)
139 # Turns all urls into clickable links (code from Rails).
140 def inline_auto_link(text)
141 text.gsub!(AUTO_LINK_RE) do
142 all, leading, proto, url, post = $&, $1, $2, $3, $6
143 if leading =~ /<a\s/i || leading =~ /![<>=]?/
144 # don't replace URL's that are already linked
145 # and URL's prefixed with ! !> !< != (textile images)
148 %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
153 # Turns all email addresses into clickable links (code from Rails).
154 def inline_auto_mailto(text)
155 text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do
157 %{<a href="mailto:#{$1}" class="email">#{text}</a>}
164 def self.to_html(text, options = {}, &block)
165 TextileFormatter.new(text).to_html(&block)