| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  | # redMine - project management software | 
					
						
							|  |  |  | # Copyright (C) 2006-2007  Jean-Philippe Lang | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # This program is free software; you can redistribute it and/or | 
					
						
							|  |  |  | # modify it under the terms of the GNU General Public License | 
					
						
							|  |  |  | # as published by the Free Software Foundation; either version 2 | 
					
						
							|  |  |  | # of the License, or (at your option) any later version. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | # This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | # GNU General Public License for more details. | 
					
						
							|  |  |  | #  | 
					
						
							|  |  |  | # You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  | # along with this program; if not, write to the Free Software | 
					
						
							|  |  |  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  | require 'redcloth' | 
					
						
							| 
									
										
										
										
											2007-09-06 17:06:07 +00:00
										 |  |  | require 'coderay' | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  | module Redmine | 
					
						
							|  |  |  |   module WikiFormatting | 
					
						
							|  |  |  |    | 
					
						
							|  |  |  |   private | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |     class TextileFormatter < RedCloth | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2007-12-19 21:06:06 +00:00
										 |  |  |       # auto_link rule after textile rules so that it doesn't break !image_url! tags | 
					
						
							| 
									
										
										
										
											2008-05-01 08:44:40 +00:00
										 |  |  |       RULES = [:textile, :block_markdown_rule, :inline_auto_link, :inline_auto_mailto, :inline_toc, :inline_macros] | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |        | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |       def initialize(*args) | 
					
						
							|  |  |  |         super | 
					
						
							|  |  |  |         self.hard_breaks=true | 
					
						
							| 
									
										
										
										
											2007-10-01 15:25:31 +00:00
										 |  |  |         self.no_span_caps=true | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |       def to_html(*rules, &block) | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |         @toc = [] | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |         @macros_runner = block | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |         super(*RULES).to_s | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     private | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # Patch for RedCloth.  Fixed in RedCloth r128 but _why hasn't released it yet. | 
					
						
							|  |  |  |       # <a href="http://code.whytheluckystiff.net/redcloth/changeset/128">http://code.whytheluckystiff.net/redcloth/changeset/128</a> | 
					
						
							|  |  |  |       def hard_break( text )  | 
					
						
							| 
									
										
										
										
											2008-05-30 16:35:36 +00:00
										 |  |  |         text.gsub!( /(.)\n(?!\n|\Z|>| *(>? *[#*=]+(\s|$)|[{|]))/, "\\1<br />\n" ) if hard_breaks  | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |       end | 
					
						
							| 
									
										
										
										
											2007-09-06 17:06:07 +00:00
										 |  |  |        | 
					
						
							|  |  |  |       # Patch to add code highlighting support to RedCloth | 
					
						
							|  |  |  |       def smooth_offtags( text ) | 
					
						
							|  |  |  |         unless @pre_list.empty? | 
					
						
							|  |  |  |           ## replace <pre> content | 
					
						
							|  |  |  |           text.gsub!(/<redpre#(\d+)>/) do | 
					
						
							|  |  |  |             content = @pre_list[$1.to_i] | 
					
						
							|  |  |  |             if content.match(/<code\s+class="(\w+)">\s?(.+)/m) | 
					
						
							|  |  |  |               content = "<code class=\"#{$1} CodeRay\">" +  | 
					
						
							| 
									
										
										
										
											2008-05-02 15:35:41 +00:00
										 |  |  |                 CodeRay.scan($2, $1.downcase).html(:escape => false, :line_numbers => :inline) | 
					
						
							| 
									
										
										
										
											2007-09-06 17:06:07 +00:00
										 |  |  |             end | 
					
						
							|  |  |  |             content | 
					
						
							|  |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |       # Patch to add 'table of content' support to RedCloth | 
					
						
							|  |  |  |       def textile_p_withtoc(tag, atts, cite, content) | 
					
						
							|  |  |  |         if tag =~ /^h(\d)$/ | 
					
						
							|  |  |  |           @toc << [$1.to_i, content] | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2007-09-27 19:44:23 +00:00
										 |  |  |         content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |         textile_p(tag, atts, cite, content) | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       alias :textile_h1 :textile_p_withtoc | 
					
						
							|  |  |  |       alias :textile_h2 :textile_p_withtoc | 
					
						
							|  |  |  |       alias :textile_h3 :textile_p_withtoc | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       def inline_toc(text) | 
					
						
							| 
									
										
										
										
											2007-09-09 10:41:02 +00:00
										 |  |  |         text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |           div_class = 'toc' | 
					
						
							|  |  |  |           div_class << ' right' if $1 == '>' | 
					
						
							| 
									
										
										
										
											2007-10-01 16:46:46 +00:00
										 |  |  |           div_class << ' left' if $1 == '<' | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |           out = "<div class=\"#{div_class}\">" | 
					
						
							|  |  |  |           @toc.each_with_index do |heading, index| | 
					
						
							| 
									
										
										
										
											2007-09-27 19:44:23 +00:00
										 |  |  |             # remove wiki links from the item | 
					
						
							|  |  |  |             toc_item = heading.last.gsub(/(\[\[|\]\])/, '') | 
					
						
							| 
									
										
										
										
											2008-06-15 11:00:40 +00:00
										 |  |  |             # remove styles | 
					
						
							|  |  |  |             # eg. %{color:red}Triggers% => Triggers | 
					
						
							|  |  |  |             toc_item.gsub! %r[%\{[^\}]*\}([^%]+)%], '\\1' | 
					
						
							| 
									
										
										
										
											2007-09-27 19:44:23 +00:00
										 |  |  |             out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>" | 
					
						
							| 
									
										
										
										
											2007-09-09 10:16:59 +00:00
										 |  |  |           end | 
					
						
							|  |  |  |           out << '</div>' | 
					
						
							|  |  |  |           out | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |       MACROS_RE = /
 | 
					
						
							| 
									
										
										
										
											2008-02-16 15:34:17 +00:00
										 |  |  |                     (!)?                        # escaping | 
					
						
							|  |  |  |                     ( | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |                     \{\{                        # opening tag | 
					
						
							|  |  |  |                     ([\w]+)                     # macro name | 
					
						
							|  |  |  |                     (\(([^\}]*)\))?             # optional arguments | 
					
						
							|  |  |  |                     \}\}                        # closing tag | 
					
						
							| 
									
										
										
										
											2008-02-16 15:34:17 +00:00
										 |  |  |                     ) | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |                   /x unless const_defined?(:MACROS_RE)
 | 
					
						
							|  |  |  |        | 
					
						
							|  |  |  |       def inline_macros(text) | 
					
						
							|  |  |  |         text.gsub!(MACROS_RE) do | 
					
						
							| 
									
										
										
										
											2008-02-16 15:34:17 +00:00
										 |  |  |           esc, all, macro = $1, $2, $3.downcase | 
					
						
							|  |  |  |           args = ($5 || '').split(',').each(&:strip) | 
					
						
							|  |  |  |           if esc.nil? | 
					
						
							|  |  |  |             begin | 
					
						
							|  |  |  |               @macros_runner.call(macro, args) | 
					
						
							|  |  |  |             rescue => e | 
					
						
							|  |  |  |               "<div class=\"flash error\">Error executing the <strong>#{macro}</strong> macro (#{e})</div>" | 
					
						
							|  |  |  |             end || all | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             all | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |        | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |       AUTO_LINK_RE = %r{
 | 
					
						
							|  |  |  |                         (                          # leading text | 
					
						
							|  |  |  |                           <\w+.*?>|                # leading HTML tag, or | 
					
						
							|  |  |  |                           [^=<>!:'"/]|             # leading punctuation, or 
 | 
					
						
							|  |  |  |                           ^                        # beginning of line | 
					
						
							|  |  |  |                         ) | 
					
						
							|  |  |  |                         ( | 
					
						
							|  |  |  |                           (?:https?://)|           # protocol spec, or | 
					
						
							| 
									
										
										
										
											2008-06-23 16:51:13 +00:00
										 |  |  |                           (?:ftp://)| | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |                           (?:www\.)                # www.* | 
					
						
							| 
									
										
										
										
											2007-09-30 15:16:58 +00:00
										 |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |                         ( | 
					
						
							| 
									
										
										
										
											2007-09-30 15:16:58 +00:00
										 |  |  |                           (\S+?)                   # url | 
					
						
							|  |  |  |                           (\/)?                    # slash | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |                         ) | 
					
						
							| 
									
										
										
										
											2007-09-30 15:16:58 +00:00
										 |  |  |                         ([^\w\=\/;]*?)               # post | 
					
						
							|  |  |  |                         (?=<|\s|$) | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |                        }x unless const_defined?(:AUTO_LINK_RE) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       # Turns all urls into clickable links (code from Rails). | 
					
						
							|  |  |  |       def inline_auto_link(text) | 
					
						
							|  |  |  |         text.gsub!(AUTO_LINK_RE) do | 
					
						
							| 
									
										
										
										
											2007-09-30 15:16:58 +00:00
										 |  |  |           all, leading, proto, url, post = $&, $1, $2, $3, $6 | 
					
						
							|  |  |  |           if leading =~ /<a\s/i || leading =~ /![<>=]?/ | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |             # don't replace URL's that are already linked | 
					
						
							|  |  |  |             # and URL's prefixed with ! !> !< != (textile images) | 
					
						
							|  |  |  |             all | 
					
						
							| 
									
										
										
										
											2007-09-30 15:16:58 +00:00
										 |  |  |           else             | 
					
						
							| 
									
										
										
										
											2007-10-01 17:45:40 +00:00
										 |  |  |             %(#{leading}<a class="external" href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post}) | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |           end | 
					
						
							|  |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							| 
									
										
										
										
											2008-06-17 19:27:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |       # Turns all email addresses into clickable links (code from Rails). | 
					
						
							|  |  |  |       def inline_auto_mailto(text) | 
					
						
							|  |  |  |         text.gsub!(/([\w\.!#\$%\-+.]+@[A-Za-z0-9\-]+(\.[A-Za-z0-9\-]+)+)/) do | 
					
						
							| 
									
										
										
										
											2008-06-17 19:27:03 +00:00
										 |  |  |           mail = $1 | 
					
						
							|  |  |  |           if text.match(/<a\b[^>]*>(.*)(#{Regexp.escape(mail)})(.*)<\/a>/) | 
					
						
							|  |  |  |             mail | 
					
						
							|  |  |  |           else | 
					
						
							|  |  |  |             %{<a href="mailto:#{mail}" class="email">#{mail}</a>} | 
					
						
							|  |  |  |           end | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |   public | 
					
						
							|  |  |  |    | 
					
						
							| 
									
										
										
										
											2007-11-12 14:36:33 +00:00
										 |  |  |     def self.to_html(text, options = {}, &block) | 
					
						
							|  |  |  |       TextileFormatter.new(text).to_html(&block) | 
					
						
							| 
									
										
										
										
											2007-09-02 20:41:47 +00:00
										 |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | end |