summaryrefslogtreecommitdiffstats
path: root/lib/redmine/wiki_formatting.rb
blob: dc296547d17630c8396f107fd3ce0d4fcc21d23d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
require 'redcloth'
require 'coderay'
require 'pp'
module Redmine
  module WikiFormatting
  
  private
  
    class TextileFormatter < RedCloth      
      RULES = [:inline_auto_link, :inline_auto_mailto, :textile, :inline_toc]
      
      def initialize(*args)
        super
        self.hard_breaks=true
      end
      
      def to_html
        @toc = []
        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 ) 
        text.gsub!( /(.)\n(?!\n|\Z| *([#*=]+(\s|$)|[{|]))/, "\\1<br />" ) if hard_breaks 
      end
      
      # 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\">" + 
                CodeRay.scan($2, $1).html(:escape => false, :line_numbers => :inline)
            end
            content
          end
        end
      end
      
      # 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
        content = "<a name=\"#{@toc.length}\" class=\"wiki-page\"></a>" + content
        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)
        text.gsub!(/<p>\{\{([<>]?)toc\}\}<\/p>/i) do
          div_class = 'toc'
          div_class << ' right' if $1 == '>'
          out = "<div class=\"#{div_class}\">"
          @toc.each_with_index do |heading, index|
            # remove wiki links from the item
            toc_item = heading.last.gsub(/(\[\[|\]\])/, '')
            out << "<a href=\"##{index+1}\" class=\"heading#{heading.first}\">#{toc_item}</a>"
          end
          out << '</div>'
          out
        end
      end
      
      AUTO_LINK_RE = %r{
                        (                          # leading text
                          <\w+.*?>|                # leading HTML tag, or
                          [^=<>!:'"/]|             # leading punctuation, or 
                          ^                        # beginning of line
                        )
                        (
                          (?:https?://)|           # protocol spec, or
                          (?:www\.)                # www.*
                        )
                        (
                          (\S+?)                   # url
                          (\/)?                    # slash
                        )
                        ([^\w\=\/;]*?)               # post
                        (?=<|\s|$)
                       }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
          all, leading, proto, url, post = $&, $1, $2, $3, $6
          if leading =~ /<a\s/i || leading =~ /![<>=]?/
            # don't replace URL's that are already linked
            # and URL's prefixed with ! !> !< != (textile images)
            all
          else            
            %(#{leading}<a href="#{proto=="www."?"http://www.":proto}#{url}">#{proto + url}</a>#{post})
          end
        end
      end
      
      # 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
          text = $1
          %{<a href="mailto:#{$1}" class="email">#{text}</a>}
        end
      end
    end
    
  public
  
    def self.to_html(text, options = {})
      TextileFormatter.new(text).to_html    
    end
  end
end