org-ruby 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 0.8.0 / 2013-02-10
2
+
3
+ * A lot of refactoring work and bugfixes contributed by vonavi (many thanks!)
4
+ * Raw HTML is supported with #+html
5
+ * Code indentation for code blocks is fixed now
6
+ * Support for definition lists is improved
7
+ * Bugfix for when including headlines in center and quote blocks.
8
+ * Indentation of HTML output improved
9
+ * Improvements to entities support for Textile and HTML outputs
10
+
1
11
  == 0.7.2 / 2012-10-07
2
12
 
3
13
  * Many fixes to the regular expressions used for emphasis, contributed by [[http://github.com/vonavi][vonavi]]
data/README.rdoc CHANGED
@@ -1,68 +1,72 @@
1
- org-ruby
2
- by Brian Dewey
3
- http://github.com/bdewey/org-ruby
1
+ = org-ruby
2
+ <em>Originally by Brian Dewey</em> (http://github.com/bdewey/org-ruby)
4
3
 
5
- == DESCRIPTION:
4
+ {<img src="https://secure.travis-ci.org/wallyqs/org-ruby.png?branch=master" alt="Build Status" />}[http://travis-ci.org/wallyqs/org-ruby]
6
5
 
7
- This gem contains Ruby routines for parsing org-mode files.The most
8
- significant thing this library does today is convert org-mode files to
9
- HTML or textile. Currently, you cannot do much to customize the
10
- conversion. The supplied textile conversion is optimized for
11
- extracting "content" from the orgfile as opposed to "metadata."
6
+ An {org-mode}[http://orgmode.org] parser written in Ruby. The most significant thing this library does today is convert org-mode files to HTML or Textile.
7
+ Currently, you cannot do much to customize the conversion. The supplied textile conversion is optimized for extracting
8
+ “content” from the orgfile as opposed to “metadata.”
12
9
 
13
- == FEATURES/PROBLEMS:
10
+ == Installation
14
11
 
15
- * Converts org-mode files to HTML or Textile
16
- * Supports tables, block quotes, and block code
17
- * Supports bold, italic, underline, strikethrough, and code inline formatting.
18
- * Supports hyperlinks that are in double-brackets
19
- * Supports +.org+ views in Rails through Tilt.
20
- * Code syntax highlight of code blocks using Pygments.rb or Coderay when available
21
- * Upcoming: Handle export options specified in the org buffer.
22
-
23
- == SYNOPSIS:
24
-
25
- From the command line:
26
-
27
- org-ruby sample.org
28
-
29
- ...will output a HTML version of sample.org.
30
-
31
- org-ruby --translate textile sample.org
12
+ gem install org-ruby
32
13
 
33
- ...will output a textile version of sample.org.
14
+ == Usage
34
15
 
35
16
  From Ruby code:
36
17
 
37
- Orgmode::Parser.new(data)
18
+ require 'org-ruby'
19
+
20
+ # Renders HTML
21
+ Orgmode::Parser.new("* Hello world!).to_html
22
+ # => "<h1>Hello world!</h1>\n"
38
23
 
39
- ...will construct a new +Parser+ object.
24
+ # Renders Textile
25
+ Orgmode::Parser.new("* Hello world!).to_textile
26
+ # => "h1. Hello world!\n"
40
27
 
41
- == INSTALL:
28
+ It can also be used from the command line:
42
29
 
43
- sudo gem install org-ruby
30
+ org-ruby sample.org --translate html
44
31
 
45
- == LICENSE:
32
+ ...will output a HTML version of sample.org.
46
33
 
47
- (The MIT License)
34
+ org-ruby --translate textile sample.org
48
35
 
49
- Copyright (c) 2009 Brian Dewey
36
+ ...will output a textile version of sample.org.
50
37
 
51
- Permission is hereby granted, free of charge, to any person obtaining
52
- a copy of this software and associated documentation files (the
53
- 'Software'), to deal in the Software without restriction, including
54
- without limitation the rights to use, copy, modify, merge, publish,
55
- distribute, sublicense, and/or sell copies of the Software, and to
56
- permit persons to whom the Software is furnished to do so, subject to
57
- the following conditions:
38
+ == Features
58
39
 
59
- The above copyright notice and this permission notice shall be
60
- included in all copies or substantial portions of the Software.
40
+ * Converts org-mode files to HTML or Textile
41
+ * Supports tables, block quotes, code blocks, and html blocks
42
+ * Supports bold, italic, underline, strikethrough, and code inline formatting.
43
+ * Supports hyperlinks that are in double-brackets
44
+ * Supports definition lists
45
+ * Supports footnotes
46
+ * Supports +.org+ views in Rails through Tilt.
47
+ * Code syntax highlight of code blocks using Pygments.rb or Coderay when available
61
48
 
62
- THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
+ == License
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2009 Brian Dewey
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/lib/org-ruby.rb CHANGED
@@ -21,7 +21,7 @@ require 'org-ruby/tilt'
21
21
  module OrgRuby
22
22
 
23
23
  # :stopdoc:
24
- VERSION = '0.7.2'
24
+ VERSION = '0.8.0'
25
25
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
26
26
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
27
27
  # :startdoc:
@@ -45,7 +45,6 @@ module Orgmode
45
45
  def initialize(line, parser = nil, offset=0)
46
46
  super(line, parser)
47
47
  @body_lines = []
48
- @body_lines << self # Make @body_lines contain the headline?
49
48
  @tags = []
50
49
  @export_state = :exclude
51
50
  if (@line =~ LineRegexp) then
@@ -15,12 +15,19 @@ module Orgmode
15
15
 
16
16
  HtmlBlockTag = {
17
17
  :paragraph => "p",
18
- :ordered_list => "li",
19
- :unordered_list => "li",
18
+ :ordered_list => "ol",
19
+ :unordered_list => "ul",
20
+ :list_item => "li",
21
+ :definition_list => "dl",
20
22
  :definition_term => "dt",
21
23
  :definition_descr => "dd",
24
+ :table => "table",
22
25
  :table_row => "tr",
23
- :table_header => "tr",
26
+ :quote => "blockquote",
27
+ :example => "pre",
28
+ :src => "pre",
29
+ :inline_example => "pre",
30
+ :center => "div",
24
31
  :heading1 => "h1",
25
32
  :heading2 => "h2",
26
33
  :heading3 => "h3",
@@ -29,18 +36,6 @@ module Orgmode
29
36
  :heading6 => "h6"
30
37
  }
31
38
 
32
- ModeTag = {
33
- :unordered_list => "ul",
34
- :ordered_list => "ol",
35
- :definition_list => "dl",
36
- :table => "table",
37
- :blockquote => "blockquote",
38
- :example => "pre",
39
- :src => "pre",
40
- :inline_example => "pre",
41
- :center => "div"
42
- }
43
-
44
39
  attr_reader :options
45
40
 
46
41
  def initialize(output, opts = {})
@@ -50,167 +45,160 @@ module Orgmode
50
45
  else
51
46
  @title_decoration = ""
52
47
  end
48
+ @buffer_tag = "HTML"
53
49
  @options = opts
50
+ @new_paragraph = :start
54
51
  @footnotes = {}
55
52
  @unclosed_tags = []
56
53
  @logger.debug "HTML export options: #{@options.inspect}"
57
54
  end
58
55
 
59
56
  # Output buffer is entering a new mode. Use this opportunity to
60
- # write out one of the block tags in the ModeTag constant to put
61
- # this information in the HTML stream.
62
- def push_mode(mode)
63
- if ModeTag[mode] then
64
- output_indentation
65
- css_class = ""
66
- css_class = " class=\"src\"" if mode == :src and @block_lang.empty?
67
- css_class = " class=\"src src-#{@block_lang}\"" if mode == :src and not @block_lang.empty?
68
- css_class = " class=\"example\"" if (mode == :example || mode == :inline_example)
69
- css_class = " style=\"text-align: center\"" if mode == :center
70
-
71
- unless ((mode == :table and skip_tables?) or
57
+ # write out one of the block tags in the HtmlBlockTag constant to
58
+ # put this information in the HTML stream.
59
+ def push_mode(mode, indent)
60
+ super(mode)
61
+ @list_indent_stack.push(indent)
62
+
63
+ if HtmlBlockTag[mode]
64
+ unless ((mode_is_table?(mode) and skip_tables?) or
72
65
  (mode == :src and defined? Pygments))
73
- @logger.debug "#{mode}: <#{ModeTag[mode]}#{css_class}>\n"
74
- @output << "<#{ModeTag[mode]}#{css_class}>\n"
66
+ css_class = case
67
+ when (mode == :src and @block_lang.empty?)
68
+ " class=\"src\""
69
+ when (mode == :src and not @block_lang.empty?)
70
+ " class=\"src src-#{@block_lang}\""
71
+ when (mode == :example || mode == :inline_example)
72
+ " class=\"example\""
73
+ when mode == :center
74
+ " style=\"text-align: center\""
75
+ else
76
+ @title_decoration
77
+ end
78
+
79
+ add_paragraph unless @new_paragraph == :start
80
+ @new_paragraph = true
81
+
82
+ @logger.debug "#{mode}: <#{HtmlBlockTag[mode]}#{css_class}>"
83
+ @output << "<#{HtmlBlockTag[mode]}#{css_class}>"
84
+ # Entering a new mode obliterates the title decoration
85
+ @title_decoration = ""
75
86
  end
76
- # Entering a new mode obliterates the title decoration
77
- @title_decoration = ""
78
87
  end
79
- super(mode)
80
88
  end
81
89
 
82
90
  # We are leaving a mode. Close any tags that were opened when
83
91
  # entering this mode.
84
92
  def pop_mode(mode = nil)
85
93
  m = super(mode)
86
- if ModeTag[m] then
87
- output_indentation
88
- # Need to close the floating li elements before closing the list
89
- if (m == :unordered_list or
90
- m == :ordered_list or
91
- m == :definition_list) and
92
- (not @unclosed_tags.empty?)
93
- close_floating_li_tags
94
- end
95
-
96
- unless ((mode == :table and skip_tables?) or
97
- (mode == :src and defined? Pygments))
98
- @logger.debug "</#{ModeTag[m]}>\n"
99
- @output << "</#{ModeTag[m]}>\n"
94
+ if HtmlBlockTag[m]
95
+ unless ((mode_is_table?(m) and skip_tables?) or
96
+ (m == :src and defined? Pygments))
97
+ add_paragraph if @new_paragraph
98
+ @new_paragraph = true
99
+ @logger.debug "</#{HtmlBlockTag[m]}>"
100
+ @output << "</#{HtmlBlockTag[m]}>"
100
101
  end
101
-
102
- # In case it was a sublist, close it here
103
- close_last_li_tag_maybe
104
102
  end
103
+ @list_indent_stack.pop
105
104
  end
106
105
 
107
106
  def flush!
108
- if buffer_mode_is_src_block?
109
-
110
- # Only try to colorize #+BEGIN_SRC blocks with a specified language,
111
- # but we still have to catch the cases when a lexer for the language was not available
112
- if defined? Pygments or defined? CodeRay
113
- lang = normalize_lang(@block_lang)
107
+ return false if @buffer.empty?
108
+ case
109
+ when preserve_whitespace?
110
+ strip_code_block! if mode_is_code? current_mode
111
+
112
+ # NOTE: CodeRay and Pygments already escape the html once, so
113
+ # no need to escape_string!(@buffer)
114
+ case
115
+ when (current_mode == :src and defined? Pygments)
116
+ lang = normalize_lang @block_lang
117
+ @output << "\n" unless @new_paragraph == :start
118
+
119
+ begin
120
+ @buffer = Pygments.highlight(@buffer, :lexer => lang)
121
+ rescue
122
+ # Not supported lexer from Pygments, we fallback on using the text lexer
123
+ @buffer = Pygments.highlight(@buffer, :lexer => 'text')
124
+ end
125
+ when (current_mode == :src and defined? CodeRay)
126
+ lang = normalize_lang @block_lang
114
127
 
115
- # NOTE: CodeRay and Pygments already escape the html once, so no need to escape_buffer!
116
- if defined? Pygments
128
+ # CodeRay might throw a warning when unsupported lang is set,
129
+ # then fallback to using the text lexer
130
+ silence_warnings do
117
131
  begin
118
- @buffer = Pygments.highlight(@buffer, :lexer => lang)
119
- rescue
120
- # Not supported lexer from Pygments, we fallback on using the text lexer
121
- @buffer = Pygments.highlight(@buffer, :lexer => 'text')
122
- end
123
- elsif defined? CodeRay
124
- # CodeRay might throw a warning when unsupported lang is set,
125
- # then fallback to using the text lexer
126
- silence_warnings do
127
- begin
128
- @buffer = CodeRay.scan(@buffer, lang).html(:wrap => nil, :css => :style)
129
- rescue ArgumentError
130
- @buffer = CodeRay.scan(@buffer, 'text').html(:wrap => nil, :css => :style)
131
- end
132
+ @buffer = CodeRay.scan(@buffer, lang).html(:wrap => nil, :css => :style)
133
+ rescue ArgumentError
134
+ @buffer = CodeRay.scan(@buffer, 'text').html(:wrap => nil, :css => :style)
132
135
  end
133
136
  end
137
+ when (current_mode == :html or current_mode == :raw_text)
138
+ @buffer.gsub!(/\A\n/, "") if @new_paragraph == :start
139
+ @new_paragraph = true
134
140
  else
135
- escape_buffer!
141
+ escape_string! @buffer
136
142
  end
137
143
 
138
- @logger.debug "FLUSH SRC CODE ==========> #{@buffer.inspect}"
144
+ # Whitespace is significant in :code mode. Always output the
145
+ # buffer and do not do any additional translation.
146
+ @logger.debug "FLUSH CODE ==========> #{@buffer.inspect}"
139
147
  @output << @buffer
140
- elsif mode_is_code(@buffer_mode) then
141
- escape_buffer!
142
148
 
143
- # Whitespace is significant in :code mode. Always output the buffer
144
- # and do not do any additional translation.
145
- @logger.debug "FLUSH CODE ==========> #{@buffer.inspect}"
146
- @output << @buffer << "\n"
149
+ when (mode_is_table? current_mode and skip_tables?)
150
+ @logger.debug "SKIP ==========> #{current_mode}"
151
+
147
152
  else
148
- escape_buffer!
149
- if @buffer.length > 0 and @output_type == :horizontal_rule then
150
- @output << "<hr />\n"
151
- elsif @buffer.length > 0 and @output_type == :definition_list then
152
- unless buffer_mode_is_table? and skip_tables?
153
- output_indentation
154
- d = @buffer.split("::", 2)
155
- @output << "<#{HtmlBlockTag[:definition_term]}#{@title_decoration}>" << inline_formatting(d[0].strip) \
156
- << "</#{HtmlBlockTag[:definition_term]}>"
157
- if d.length > 1 then
158
- @output << "<#{HtmlBlockTag[:definition_descr]}#{@title_decoration}>" << inline_formatting(d[1].strip) \
159
- << "</#{HtmlBlockTag[:definition_descr]}>\n"
160
- else
161
- @output << "\n"
162
- end
163
- @title_decoration = ""
164
- end
165
- elsif @buffer.length > 0 then
166
- unless buffer_mode_is_table? and skip_tables?
167
- @logger.debug "FLUSH ==========> #{@buffer_mode}"
168
- output_indentation
169
- if ((@buffered_lines[0].plain_list?) and
170
- (@unclosed_tags.count == @list_indent_stack.count))
171
- @output << @unclosed_tags.pop
172
- output_indentation
173
- end
174
- @output << "<#{HtmlBlockTag[@output_type]}#{@title_decoration}>"
175
- if (@buffered_lines[0].kind_of?(Headline)) then
176
- headline = @buffered_lines[0]
177
- raise "Cannot be more than one headline!" if @buffered_lines.length > 1
178
- if @options[:export_heading_number] then
179
- level = headline.level
180
- heading_number = get_next_headline_number(level)
181
- output << "<span class=\"heading-number heading-number-#{level}\">#{heading_number} </span>"
182
- end
183
- if @options[:export_todo] and headline.keyword then
184
- keyword = headline.keyword
185
- output << "<span class=\"todo-keyword #{keyword}\">#{keyword} </span>"
186
- end
187
- end
188
- @output << inline_formatting(@buffer)
189
-
190
- # Only close the list when it is the last element from that list,
191
- # or when starting another list
192
- if (@output_type == :unordered_list or
193
- @output_type == :ordered_list or
194
- @output_type == :definition_list) and
195
- (not @list_indent_stack.empty?)
196
- @unclosed_tags.push("</#{HtmlBlockTag[@output_type]}>\n")
197
- @output << "\n"
198
- else
199
- @output << "</#{HtmlBlockTag[@output_type]}>\n"
200
- end
201
- @title_decoration = ""
153
+ @buffer.lstrip!
154
+ @new_paragraph = nil
155
+ @logger.debug "FLUSH ==========> #{current_mode}"
156
+
157
+ case current_mode
158
+ when :definition_term
159
+ d = @buffer.split(/\A(.*[ \t]+|)::(|[ \t]+.*?)$/, 4)
160
+ d[1] = d[1].strip
161
+ unless d[1].empty?
162
+ @output << inline_formatting(d[1])
202
163
  else
203
- @logger.debug "SKIP ==========> #{@buffer_mode}"
164
+ @output << "???"
204
165
  end
166
+ indent = @list_indent_stack.last
167
+ pop_mode
168
+
169
+ @new_paragraph = :start
170
+ push_mode(:definition_descr, indent)
171
+ @output << inline_formatting(d[2].strip + d[3])
172
+ @new_paragraph = nil
173
+
174
+ when :horizontal_rule
175
+ add_paragraph unless @new_paragraph == :start
176
+ @new_paragraph = true
177
+ @output << "<hr />"
178
+
179
+ else
180
+ @output << inline_formatting(@buffer)
205
181
  end
206
182
  end
207
- clear_accumulation_buffer!
183
+ @buffer = ""
184
+ end
185
+
186
+ def add_line_attributes headline
187
+ if @options[:export_heading_number] then
188
+ level = headline.level
189
+ heading_number = get_next_headline_number(level)
190
+ @output << "<span class=\"heading-number heading-number-#{level}\">#{heading_number}</span> "
191
+ end
192
+ if @options[:export_todo] and headline.keyword then
193
+ keyword = headline.keyword
194
+ @output << "<span class=\"todo-keyword #{keyword}\">#{keyword}</span> "
195
+ end
208
196
  end
209
197
 
210
198
  def output_footnotes!
211
199
  return false unless @options[:export_footnotes] and not @footnotes.empty?
212
200
 
213
- @output << "<div id=\"footnotes\">\n<h2 class=\"footnotes\">Footnotes: </h2>\n<div id=\"text-footnotes\">\n"
201
+ @output << "\n<div id=\"footnotes\">\n<h2 class=\"footnotes\">Footnotes:</h2>\n<div id=\"text-footnotes\">\n"
214
202
 
215
203
  @footnotes.each do |name, defi|
216
204
  @output << "<p class=\"footnote\"><sup><a class=\"footnum\" name=\"fn.#{name}\" href=\"#fnr.#{name}\">#{name}</a></sup>" \
@@ -218,11 +206,15 @@ module Orgmode
218
206
  << "</p>\n"
219
207
  end
220
208
 
221
- @output << "</div>\n</div>\n"
209
+ @output << "</div>\n</div>"
222
210
 
223
211
  return true
224
212
  end
225
213
 
214
+ # Test if we're in an output mode in which whitespace is significant.
215
+ def preserve_whitespace?
216
+ super or current_mode == :html
217
+ end
226
218
 
227
219
  ######################################################################
228
220
  private
@@ -231,116 +223,105 @@ module Orgmode
231
223
  @options[:skip_tables]
232
224
  end
233
225
 
234
- def buffer_mode_is_table?
235
- @buffer_mode == :table
226
+ def mode_is_table?(mode)
227
+ (mode == :table or mode == :table_row or
228
+ mode == :table_separator or mode == :table_header)
236
229
  end
237
230
 
238
- def buffer_mode_is_src_block?
239
- @buffer_mode == :src
231
+ # Escapes any HTML content in the output accumulation buffer @buffer.
232
+ def escape_string! str
233
+ str.gsub!(/&/, "&amp;")
234
+ # Escapes the left and right angular brackets but construction
235
+ # @<text> which is formatted to <text>
236
+ str.gsub! /<([^<>\n]*)/ do |match|
237
+ if $`[-1..-1] == "@" and $'[0..0] == ">" then $&
238
+ else "&lt;#{$1}"
239
+ end
240
+ end
241
+ str.gsub! /([^<>\n]*)>/ do |match|
242
+ if $`[-2..-1] == "@<" then $&
243
+ else "#{$1}&gt;"
244
+ end
245
+ end
246
+ str.gsub!(/@(<[^<>\n]*>)/, "\\1")
240
247
  end
241
248
 
242
- # Escapes any HTML content in the output accumulation buffer @buffer.
243
- def escape_buffer!
244
- @buffer.gsub!(/&/, "&amp;")
245
- @buffer.gsub!(/</, "&lt;")
246
- @buffer.gsub!(/>/, "&gt;")
249
+ def buffer_indentation
250
+ indent = " " * @list_indent_stack.length
251
+ @buffer << indent
247
252
  end
248
253
 
249
- def output_indentation
250
- indent = " " * (@mode_stack.length - 1)
251
- @output << indent
254
+ def add_paragraph
255
+ indent = " " * (@list_indent_stack.length - 1)
256
+ @output << "\n" << indent
252
257
  end
253
258
 
254
259
  Tags = {
255
- "*" => { :open => "<b>", :close => "</b>" },
256
- "/" => { :open => "<i>", :close => "</i>" },
257
- "_" => { :open => "<span style=\"text-decoration:underline;\">",
258
- :close => "</span>" },
259
- "=" => { :open => "<code>", :close => "</code>" },
260
- "~" => { :open => "<code>", :close => "</code>" },
261
- "+" => { :open => "<del>", :close => "</del>" }
260
+ "*" => { :open => "b", :close => "b" },
261
+ "/" => { :open => "i", :close => "i" },
262
+ "_" => { :open => "span style=\"text-decoration:underline;\"",
263
+ :close => "span" },
264
+ "=" => { :open => "code", :close => "code" },
265
+ "~" => { :open => "code", :close => "code" },
266
+ "+" => { :open => "del", :close => "del" }
262
267
  }
263
268
 
264
269
  # Applies inline formatting rules to a string.
265
270
  def inline_formatting(str)
266
- str.rstrip!
267
- str = @re_help.rewrite_emphasis(str) do |marker, s|
268
- "#{Tags[marker][:open]}#{s}#{Tags[marker][:close]}"
271
+ @re_help.rewrite_emphasis str do |marker, s|
272
+ "@<#{Tags[marker][:open]}>#{s}@</#{Tags[marker][:close]}>"
269
273
  end
270
274
  if @options[:use_sub_superscripts] then
271
- str = @re_help.rewrite_subp(str) do |type, text|
275
+ @re_help.rewrite_subp str do |type, text|
272
276
  if type == "_" then
273
- "<sub>#{text}</sub>"
277
+ "@<sub>#{text}@</sub>"
274
278
  elsif type == "^" then
275
- "<sup>#{text}</sup>"
279
+ "@<sup>#{text}@</sup>"
276
280
  end
277
281
  end
278
282
  end
279
- str = @re_help.rewrite_images(str) do |link|
280
- "<a href=\"#{link}\"><img src=\"#{link}\" /></a>"
281
- end
282
- str = @re_help.rewrite_links(str) do |link, text|
283
- text ||= link
284
- link = link.sub(/^file:(.*)::(.*?)$/) do
285
-
283
+ @re_help.rewrite_links str do |link, defi|
284
+ [link, defi].compact.each do |text|
286
285
  # We don't support search links right now. Get rid of it.
287
-
288
- "file:#{$1}"
289
- end
290
- if link.match(/^file:.*\.org$/)
291
- link = link.sub(/\.org$/i, ".html")
286
+ text.sub!(/\A(file:[^\s]+)::[^\s]*?\Z/, "\\1")
287
+ text.sub!(/\A(file:[^\s]+)\.org\Z/i, "\\1.html")
288
+ text.sub!(/\Afile:(?=[^\s]+\Z)/, "")
292
289
  end
293
290
 
294
- link = link.sub(/^file:/i, "") # will default to HTTP
291
+ # We don't add a description for images in links, because its
292
+ # empty value forces the image to be inlined.
293
+ defi ||= link unless link =~ @re_help.org_image_file_regexp
295
294
 
296
- text = text.gsub(/([^\]]*\.(jpg|jpeg|gif|png))/xi) do |img_link|
297
- "<img src=\"#{img_link}\" />"
295
+ if defi =~ @re_help.org_image_file_regexp
296
+ defi = "@<img src=\"#{defi}\" alt=\"#{defi}\" />"
297
+ end
298
+
299
+ if defi
300
+ "@<a href=\"#{link}\">#{defi}@</a>"
301
+ else
302
+ "@<img src=\"#{link}\" alt=\"#{link}\" />"
298
303
  end
299
- "<a href=\"#{link}\">#{text}</a>"
300
304
  end
301
- if (@output_type == :table_row) then
302
- str.gsub!(/^\|\s*/, "<td>")
303
- str.gsub!(/\s*\|$/, "</td>")
304
- str.gsub!(/\s*\|\s*/, "</td><td>")
305
+ if @output_type == :table_row
306
+ str.gsub!(/^\|\s*/, "@<td>")
307
+ str.gsub!(/\s*\|$/, "@</td>")
308
+ str.gsub!(/\s*\|\s*/, "@</td>@<td>")
305
309
  end
306
- if (@output_type == :table_header) then
307
- str.gsub!(/^\|\s*/, "<th>")
308
- str.gsub!(/\s*\|$/, "</th>")
309
- str.gsub!(/\s*\|\s*/, "</th><th>")
310
+ if @output_type == :table_header
311
+ str.gsub!(/^\|\s*/, "@<th>")
312
+ str.gsub!(/\s*\|$/, "@</th>")
313
+ str.gsub!(/\s*\|\s*/, "@</th>@<th>")
310
314
  end
311
315
  if @options[:export_footnotes] then
312
- str = @re_help.rewrite_footnote(str) do |name, defi|
316
+ @re_help.rewrite_footnote str do |name, defi|
313
317
  # TODO escape name for url?
314
318
  @footnotes[name] = defi if defi
315
- "<sup><a class=\"footref\" name=\"fnr.#{name}\" href=\"#fn.#{name}\">#{name}</a></sup>"
316
- end
317
- end
318
- Orgmode.special_symbols_to_html(str)
319
- str
320
- end
321
-
322
- def close_floating_li_tags
323
- unless @final_list_node
324
- unless @unclosed_tags.empty?
325
- @output << " " << @unclosed_tags.pop
326
- output_indentation
327
- end
328
- end
329
-
330
- @final_list_node = false
331
- end
332
-
333
- def close_last_li_tag_maybe
334
- if (@list_indent_stack.count < @unclosed_tags.count) and not
335
- (@list_indent_stack.empty? and @unclosed_tags.empty?)
336
- output_indentation
337
- @output << @unclosed_tags.pop
338
- if (@list_indent_stack.count == @unclosed_tags.count) and not
339
- (@list_indent_stack.empty? and @unclosed_tags.empty?)
340
- @final_list_node = true
341
- pop_mode
319
+ "@<sup>@<a class=\"footref\" name=\"fnr.#{name}\" href=\"#fn.#{name}\">#{name}@</a>@</sup>"
342
320
  end
343
321
  end
322
+ escape_string! str
323
+ Orgmode.special_symbols_to_html str
324
+ str = @re_help.restore_code_snippets str
344
325
  end
345
326
 
346
327
  def normalize_lang(lang)
@@ -363,5 +344,11 @@ module Orgmode
363
344
  ensure
364
345
  $VERBOSE = warn_level
365
346
  end
347
+
348
+ def strip_code_block!
349
+ strip_regexp = Regexp.new("^" + " " * @code_block_indent)
350
+ @buffer.gsub!(strip_regexp, "")
351
+ @code_block_indent = nil
352
+ end
366
353
  end # class HtmlOutputBuffer
367
354
  end # module Orgmode