OSDN Git Service

Adds multi-levels blockquotes support by using > at the beginning of lines.
[redminele/redmine.git] / lib / redmine / wiki_formatting.rb
1 # redMine - project management software
2 # Copyright (C) 2006-2007  Jean-Philippe Lang
3 #
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.
8
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.
13
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.
17
18 require 'redcloth'
19 require 'coderay'
20
21 module Redmine
22   module WikiFormatting
23   
24   private
25   
26     class TextileFormatter < RedCloth
27       
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]
30       
31       def initialize(*args)
32         super
33         self.hard_breaks=true
34         self.no_span_caps=true
35       end
36       
37       def to_html(*rules, &block)
38         @toc = []
39         @macros_runner = block
40         super(*RULES).to_s
41       end
42
43     private
44
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 
49       end
50       
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)
60             end
61             content
62           end
63         end
64       end
65       
66       # Patch to add 'table of content' support to RedCloth
67       def textile_p_withtoc(tag, atts, cite, content)
68         if tag =~ /^h(\d)$/
69           @toc << [$1.to_i, content]
70         end
71         content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
72         textile_p(tag, atts, cite, content)
73       end
74
75       alias :textile_h1 :textile_p_withtoc
76       alias :textile_h2 :textile_p_withtoc
77       alias :textile_h3 :textile_p_withtoc
78       
79       def inline_toc(text)
80         text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
81           div_class = 'toc'
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>"
89           end
90           out << '</div>'
91           out
92         end
93       end
94       
95       MACROS_RE = /
96                     (!)?                        # escaping
97                     (
98                     \{\{                        # opening tag
99                     ([\w]+)                     # macro name
100                     (\(([^\}]*)\))?             # optional arguments
101                     \}\}                        # closing tag
102                     )
103                   /x unless const_defined?(:MACROS_RE)
104       
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)
109           if esc.nil?
110             begin
111               @macros_runner.call(macro, args)
112             rescue => e
113               "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>"
114             end || all
115           else
116             all
117           end
118         end
119       end
120       
121       AUTO_LINK_RE = %r{
122                         (                          # leading text
123                           <\w+.*?>|                # leading HTML tag, or
124                           [^=<>!:'"/]|             # leading punctuation, or 
125                           ^                        # beginning of line
126                         )
127                         (
128                           (?:https?://)|           # protocol spec, or
129                           (?:www\.)                # www.*
130                         )
131                         (
132                           (\S+?)                   # url
133                           (\/)?                    # slash
134                         )
135                         ([^\w\=\/;]*?)               # post
136                         (?=<|\s|$)
137                        }x unless const_defined?(:AUTO_LINK_RE)
138
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)
146             all
147           else            
148             %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
149           end
150         end
151       end
152       
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
156           text = $1
157           %{<a href="mailto:#{$1}" class="email">#{text}</a>}
158         end
159       end
160     end
161     
162   public
163   
164     def self.to_html(text, options = {}, &block)
165       TextileFormatter.new(text).to_html(&block)
166     end
167   end
168 end