PageRenderTime 52ms CodeModel.GetById 13ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/tools/Ruby/lib/ruby/1.8/rdoc/markup/simple_markup/to_html.rb

http://github.com/agross/netopenspace
Ruby | 289 lines | 207 code | 54 blank | 28 comment | 20 complexity | 33af6b3059559ba4878f6abe0f05d63b MD5 | raw file
  1require 'rdoc/markup/simple_markup/fragments'
  2require 'rdoc/markup/simple_markup/inline'
  3
  4require 'cgi'
  5
  6module SM
  7
  8  class ToHtml
  9
 10    LIST_TYPE_TO_HTML = {
 11      ListBase::BULLET =>  [ "<ul>", "</ul>" ],
 12      ListBase::NUMBER =>  [ "<ol>", "</ol>" ],
 13      ListBase::UPPERALPHA =>  [ "<ol>", "</ol>" ],
 14      ListBase::LOWERALPHA =>  [ "<ol>", "</ol>" ],
 15      ListBase::LABELED => [ "<dl>", "</dl>" ],
 16      ListBase::NOTE    => [ "<table>", "</table>" ],
 17    }
 18
 19    InlineTag = Struct.new(:bit, :on, :off)
 20
 21    def initialize
 22      init_tags
 23    end
 24
 25    ##
 26    # Set up the standard mapping of attributes to HTML tags
 27    #
 28    def init_tags
 29      @attr_tags = [
 30        InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
 31        InlineTag.new(SM::Attribute.bitmap_for(:TT),   "<tt>", "</tt>"),
 32        InlineTag.new(SM::Attribute.bitmap_for(:EM),   "<em>", "</em>"),
 33      ]
 34    end
 35
 36    ##
 37    # Add a new set of HTML tags for an attribute. We allow
 38    # separate start and end tags for flexibility
 39    #
 40    def add_tag(name, start, stop)
 41      @attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
 42    end
 43
 44    ##
 45    # Given an HTML tag, decorate it with class information
 46    # and the like if required. This is a no-op in the base
 47    # class, but is overridden in HTML output classes that
 48    # implement style sheets
 49
 50    def annotate(tag)
 51      tag
 52    end
 53
 54    ## 
 55    # Here's the client side of the visitor pattern
 56
 57    def start_accepting
 58      @res = ""
 59      @in_list_entry = []
 60    end
 61
 62    def end_accepting
 63      @res
 64    end
 65
 66    def accept_paragraph(am, fragment)
 67      @res << annotate("<p>") + "\n"
 68      @res << wrap(convert_flow(am.flow(fragment.txt)))
 69      @res << annotate("</p>") + "\n"
 70    end
 71
 72    def accept_verbatim(am, fragment)
 73      @res << annotate("<pre>") + "\n"
 74      @res << CGI.escapeHTML(fragment.txt)
 75      @res << annotate("</pre>") << "\n"
 76    end
 77
 78    def accept_rule(am, fragment)
 79      size = fragment.param
 80      size = 10 if size > 10
 81      @res << "<hr size=\"#{size}\"></hr>"
 82    end
 83
 84    def accept_list_start(am, fragment)
 85      @res << html_list_name(fragment.type, true) <<"\n"
 86      @in_list_entry.push false
 87    end
 88
 89    def accept_list_end(am, fragment)
 90      if tag = @in_list_entry.pop
 91        @res << annotate(tag) << "\n"
 92      end
 93      @res << html_list_name(fragment.type, false) <<"\n"
 94    end
 95
 96    def accept_list_item(am, fragment)
 97      if tag = @in_list_entry.last
 98        @res << annotate(tag) << "\n"
 99      end
100      @res << list_item_start(am, fragment)
101      @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
102      @in_list_entry[-1] = list_end_for(fragment.type)
103    end
104
105    def accept_blank_line(am, fragment)
106      # @res << annotate("<p />") << "\n"
107    end
108
109    def accept_heading(am, fragment)
110      @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
111    end
112
113    # This is a higher speed (if messier) version of wrap
114
115    def wrap(txt, line_len = 76)
116      res = ""
117      sp = 0
118      ep = txt.length
119      while sp < ep
120        # scan back for a space
121        p = sp + line_len - 1
122        if p >= ep
123          p = ep
124        else
125          while p > sp and txt[p] != ?\s
126            p -= 1
127          end
128          if p <= sp
129            p = sp + line_len
130            while p < ep and txt[p] != ?\s
131              p += 1
132            end
133          end
134        end
135        res << txt[sp...p] << "\n"
136        sp = p
137        sp += 1 while sp < ep and txt[sp] == ?\s
138      end
139      res
140    end
141
142    #######################################################################
143
144    private
145
146    #######################################################################
147
148    def on_tags(res, item)
149      attr_mask = item.turn_on
150      return if attr_mask.zero?
151
152      @attr_tags.each do |tag|
153        if attr_mask & tag.bit != 0
154          res << annotate(tag.on)
155        end
156      end
157    end
158
159    def off_tags(res, item)
160      attr_mask = item.turn_off
161      return if attr_mask.zero?
162
163      @attr_tags.reverse_each do |tag|
164        if attr_mask & tag.bit != 0
165          res << annotate(tag.off)
166        end
167      end
168    end
169
170    def convert_flow(flow)
171      res = ""
172      flow.each do |item|
173        case item
174        when String
175          res << convert_string(item)
176        when AttrChanger
177          off_tags(res, item)
178          on_tags(res,  item)
179        when Special
180          res << convert_special(item)
181        else
182          raise "Unknown flow element: #{item.inspect}"
183        end
184      end
185      res
186    end
187
188    # some of these patterns are taken from SmartyPants...
189
190    def convert_string(item)
191      CGI.escapeHTML(item).
192      
193      
194      # convert -- to em-dash, (-- to en-dash)
195        gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
196
197      # convert ... to elipsis (and make sure .... becomes .<elipsis>)
198        gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
199
200      # convert single closing quote
201        gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
202        gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
203
204      # convert single opening quote
205        gsub(/'/, '&#8216;').
206
207      # convert double closing quote
208        gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
209
210      # convert double opening quote
211        gsub(/'/, '&#8220;').
212
213      # convert copyright
214        gsub(/\(c\)/, '&#169;').
215
216      # convert and registered trademark
217        gsub(/\(r\)/, '&#174;')
218
219    end
220
221    def convert_special(special)
222      handled = false
223      Attribute.each_name_of(special.type) do |name|
224        method_name = "handle_special_#{name}"
225        if self.respond_to? method_name
226          special.text = send(method_name, special)
227          handled = true
228        end
229      end
230      raise "Unhandled special: #{special}" unless handled
231      special.text
232    end
233
234    def convert_heading(level, flow)
235      res =
236        annotate("<h#{level}>") + 
237        convert_flow(flow) + 
238        annotate("</h#{level}>\n")
239    end
240
241    def html_list_name(list_type, is_open_tag)
242      tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
243      annotate(tags[ is_open_tag ? 0 : 1])
244    end
245
246    def list_item_start(am, fragment)
247      case fragment.type
248      when ListBase::BULLET, ListBase::NUMBER
249        annotate("<li>")
250
251      when ListBase::UPPERALPHA
252	annotate("<li type=\"A\">")
253
254      when ListBase::LOWERALPHA
255	annotate("<li type=\"a\">")
256
257      when ListBase::LABELED
258        annotate("<dt>") +
259          convert_flow(am.flow(fragment.param)) + 
260          annotate("</dt>") +
261          annotate("<dd>")
262
263      when ListBase::NOTE
264        annotate("<tr>") +
265          annotate("<td valign=\"top\">") +
266          convert_flow(am.flow(fragment.param)) + 
267          annotate("</td>") +
268          annotate("<td>")
269      else
270        raise "Invalid list type"
271      end
272    end
273
274    def list_end_for(fragment_type)
275      case fragment_type
276      when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA, ListBase::LOWERALPHA
277        "</li>"
278      when ListBase::LABELED
279        "</dd>"
280      when ListBase::NOTE
281        "</td></tr>"
282      else
283        raise "Invalid list type"
284      end
285    end
286
287  end
288
289end