kramdown 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kramdown might be problematic. Click here for more details.

Files changed (132) hide show
  1. data/CONTRIBUTERS +1 -1
  2. data/ChangeLog +594 -0
  3. data/Rakefile +2 -2
  4. data/VERSION +1 -1
  5. data/data/kramdown/document.html +11 -2
  6. data/doc/default.template +2 -2
  7. data/doc/index.page +1 -2
  8. data/doc/quickref.page +2 -2
  9. data/doc/syntax.page +270 -179
  10. data/lib/kramdown/converter/html.rb +43 -29
  11. data/lib/kramdown/converter/kramdown.rb +97 -73
  12. data/lib/kramdown/converter/latex.rb +18 -13
  13. data/lib/kramdown/document.rb +8 -6
  14. data/lib/kramdown/options.rb +7 -10
  15. data/lib/kramdown/parser/html.rb +29 -21
  16. data/lib/kramdown/parser/kramdown.rb +19 -3
  17. data/lib/kramdown/parser/kramdown/abbreviation.rb +1 -0
  18. data/lib/kramdown/parser/kramdown/attribute_list.rb +18 -12
  19. data/lib/kramdown/parser/kramdown/autolink.rb +1 -1
  20. data/lib/kramdown/parser/kramdown/block_boundary.rb +46 -0
  21. data/lib/kramdown/parser/kramdown/blockquote.rb +7 -3
  22. data/lib/kramdown/parser/kramdown/codeblock.rb +5 -3
  23. data/lib/kramdown/parser/kramdown/escaped_chars.rb +1 -1
  24. data/lib/kramdown/parser/kramdown/extension.rb +22 -8
  25. data/lib/kramdown/parser/kramdown/footnote.rb +3 -2
  26. data/lib/kramdown/parser/kramdown/header.rb +10 -10
  27. data/lib/kramdown/parser/kramdown/html.rb +16 -14
  28. data/lib/kramdown/parser/kramdown/html_entity.rb +1 -1
  29. data/lib/kramdown/parser/kramdown/link.rb +8 -8
  30. data/lib/kramdown/parser/kramdown/list.rb +36 -29
  31. data/lib/kramdown/parser/kramdown/math.rb +15 -4
  32. data/lib/kramdown/parser/kramdown/paragraph.rb +14 -3
  33. data/lib/kramdown/parser/kramdown/table.rb +17 -9
  34. data/lib/kramdown/utils.rb +1 -0
  35. data/lib/kramdown/utils/html.rb +9 -9
  36. data/lib/kramdown/utils/ordered_hash.rb +79 -0
  37. data/lib/kramdown/version.rb +1 -1
  38. data/man/man1/kramdown.1 +9 -12
  39. data/test/test_files.rb +6 -1
  40. data/test/testcases/block/02_eob/middle.html +0 -1
  41. data/test/testcases/block/04_header/atx_header.html +5 -2
  42. data/test/testcases/block/04_header/atx_header.text +3 -1
  43. data/test/testcases/block/04_header/setext_header.html +4 -5
  44. data/test/testcases/block/04_header/setext_header.html.19 +30 -0
  45. data/test/testcases/block/05_blockquote/lazy.html +34 -0
  46. data/test/testcases/block/05_blockquote/lazy.text +20 -0
  47. data/test/testcases/block/05_blockquote/nested.html +1 -0
  48. data/test/testcases/block/05_blockquote/nested.text +1 -0
  49. data/test/testcases/block/05_blockquote/with_code_blocks.html +2 -2
  50. data/test/testcases/block/06_codeblock/lazy.html +4 -0
  51. data/test/testcases/block/06_codeblock/lazy.text +5 -0
  52. data/test/testcases/block/06_codeblock/no_newline_at_end_1.html +2 -0
  53. data/test/testcases/block/06_codeblock/no_newline_at_end_1.text +2 -0
  54. data/test/testcases/block/06_codeblock/with_ial.html +6 -0
  55. data/test/testcases/block/06_codeblock/with_ial.text +5 -0
  56. data/test/testcases/block/07_horizontal_rule/normal.html +0 -2
  57. data/test/testcases/block/07_horizontal_rule/normal.text +0 -2
  58. data/test/testcases/block/08_list/item_ial.html +1 -3
  59. data/test/testcases/block/08_list/lazy.html +39 -0
  60. data/test/testcases/block/08_list/lazy.text +29 -0
  61. data/test/testcases/block/08_list/list_and_others.html +5 -3
  62. data/test/testcases/block/08_list/list_and_others.text +1 -0
  63. data/test/testcases/block/08_list/other_first_element.html +2 -2
  64. data/test/testcases/block/08_list/other_first_element.text +1 -1
  65. data/test/testcases/block/08_list/simple_ul.html +0 -13
  66. data/test/testcases/block/08_list/simple_ul.text +0 -7
  67. data/test/testcases/block/08_list/special_cases.html +8 -31
  68. data/test/testcases/block/08_list/special_cases.text +2 -15
  69. data/test/testcases/block/09_html/comment.html +2 -2
  70. data/test/testcases/block/09_html/html_to_native/emphasis.html +2 -0
  71. data/test/testcases/block/09_html/html_to_native/emphasis.text +2 -0
  72. data/test/testcases/block/09_html/html_to_native/table_normal.html +2 -1
  73. data/test/testcases/block/09_html/html_to_native/table_simple.html +4 -2
  74. data/test/testcases/block/09_html/invalid_html_1.html +2 -0
  75. data/test/testcases/block/09_html/parse_as_raw.html +2 -2
  76. data/test/testcases/block/09_html/parse_as_span.html +1 -1
  77. data/test/testcases/block/09_html/simple.html +2 -0
  78. data/test/testcases/block/09_html/simple.html.19 +2 -0
  79. data/test/testcases/block/09_html/simple.text +2 -0
  80. data/test/testcases/block/11_ial/auto_id_and_ial.html +1 -1
  81. data/test/testcases/block/11_ial/simple.html +2 -3
  82. data/test/testcases/block/12_extension/comment.html +3 -1
  83. data/test/testcases/block/12_extension/comment.text +2 -1
  84. data/test/testcases/block/12_extension/ignored.html +5 -1
  85. data/test/testcases/block/12_extension/ignored.text +1 -1
  86. data/test/testcases/block/12_extension/nomarkdown.html +5 -1
  87. data/test/testcases/block/12_extension/nomarkdown.kramdown +20 -0
  88. data/test/testcases/block/12_extension/nomarkdown.latex +13 -0
  89. data/test/testcases/block/12_extension/nomarkdown.text +11 -1
  90. data/test/testcases/block/13_definition_list/item_ial.html +1 -3
  91. data/test/testcases/block/13_definition_list/item_ial.text +1 -1
  92. data/test/testcases/block/13_definition_list/simple.html +2 -2
  93. data/test/testcases/block/14_table/errors.html +5 -0
  94. data/test/testcases/block/14_table/errors.text +6 -0
  95. data/test/testcases/block/14_table/header.text +1 -1
  96. data/test/testcases/block/14_table/no_table.text +1 -1
  97. data/test/testcases/block/14_table/simple.html +78 -0
  98. data/test/testcases/block/14_table/simple.text +22 -0
  99. data/test/testcases/block/15_math/normal.html +11 -4
  100. data/test/testcases/block/15_math/normal.text +10 -0
  101. data/test/testcases/encoding.html +1 -1
  102. data/test/testcases/span/01_link/image_in_a.html +3 -3
  103. data/test/testcases/span/01_link/imagelinks.html +7 -7
  104. data/test/testcases/span/01_link/inline.html +11 -5
  105. data/test/testcases/span/01_link/inline.html.19 +11 -5
  106. data/test/testcases/span/01_link/inline.text +11 -5
  107. data/test/testcases/span/01_link/link_defs.html +2 -1
  108. data/test/testcases/span/01_link/link_defs.text +4 -0
  109. data/test/testcases/span/01_link/reference.html +3 -0
  110. data/test/testcases/span/01_link/reference.html.19 +3 -0
  111. data/test/testcases/span/01_link/reference.text +5 -0
  112. data/test/testcases/span/03_codespan/highlighting.html +1 -0
  113. data/test/testcases/span/03_codespan/highlighting.text +1 -0
  114. data/test/testcases/span/04_footnote/definitions.html +3 -0
  115. data/test/testcases/span/04_footnote/definitions.latex +3 -4
  116. data/test/testcases/span/04_footnote/definitions.text +6 -0
  117. data/test/testcases/span/04_footnote/footnote_nr.latex +1 -5
  118. data/test/testcases/span/04_footnote/markers.latex +5 -14
  119. data/test/testcases/span/05_html/markdown_attr.html +1 -1
  120. data/test/testcases/span/05_html/markdown_attr.text +1 -1
  121. data/test/testcases/span/05_html/normal.html +5 -3
  122. data/test/testcases/span/05_html/normal.text +2 -0
  123. data/test/testcases/span/escaped_chars/normal.html +2 -0
  124. data/test/testcases/span/escaped_chars/normal.text +2 -0
  125. data/test/testcases/span/extension/comment.html +2 -2
  126. data/test/testcases/span/extension/ignored.html +1 -1
  127. data/test/testcases/span/text_substitutions/typography.html +1 -1
  128. data/test/testcases/span/text_substitutions/typography.html.19 +1 -1
  129. data/test/testcases/span/text_substitutions/typography.text +1 -1
  130. metadata +20 -5
  131. data/test/testcases/block/05_blockquote/only_first_quoted.html +0 -8
  132. data/test/testcases/block/05_blockquote/only_first_quoted.text +0 -4
@@ -49,6 +49,7 @@ module Kramdown
49
49
  options = opts.dup.merge(:parent => el)
50
50
  el.children.each_with_index do |inner_el, index|
51
51
  options[:index] = index
52
+ options[:result] = result
52
53
  result << send("convert_#{inner_el.type}", inner_el, options)
53
54
  end
54
55
  result
@@ -59,7 +60,7 @@ module Kramdown
59
60
  end
60
61
 
61
62
  def convert_blank(el, opts)
62
- ""
63
+ opts[:result] =~ /\n\n\Z|\A\Z/ ? "" : "\n"
63
64
  end
64
65
 
65
66
  def convert_text(el, opts)
@@ -76,12 +77,12 @@ module Kramdown
76
77
 
77
78
  def convert_standalone_image(el, opts, img)
78
79
  attrs = attribute_list(el)
79
- "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.options[:attr]['alt'])}}\n\\end{figure}#{attrs}\n"
80
+ "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.attr['alt'])}}\n\\end{figure}#{attrs}\n"
80
81
  end
81
82
 
82
83
  def convert_codeblock(el, opts)
83
- show_whitespace = el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bshow-whitespaces\b/
84
- lang = el.options[:attr] && el.options[:attr]['lang']
84
+ show_whitespace = el.attr['class'].to_s =~ /\bshow-whitespaces\b/
85
+ lang = el.attr['lang']
85
86
  if show_whitespace || lang
86
87
  result = "\\lstset{showspaces=%s,showtabs=%s}\n" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
87
88
  result += "\\lstset{language=#{lang}}\n" if lang
@@ -95,7 +96,7 @@ module Kramdown
95
96
 
96
97
  def latex_environment(type, el, text)
97
98
  attrs = attribute_list(el)
98
- "\\begin{#{type}}#{attrs}\n#{text}\n\\end{#{type}}#{attrs}\n"
99
+ "\\begin{#{type}}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
99
100
  end
100
101
 
101
102
  def convert_blockquote(el, opts)
@@ -112,7 +113,7 @@ module Kramdown
112
113
  }
113
114
  def convert_header(el, opts)
114
115
  type = HEADER_TYPES[el.options[:level]]
115
- if ((el.options[:attr] && (id = el.options[:attr]['id'])) ||
116
+ if ((id = el.attr['id']) ||
116
117
  (@doc.options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) &&
117
118
  (@doc.options[:toc_depth] <= 0 || el.options[:level] <= @doc.options[:toc_depth])
118
119
  "\\hypertarget{#{id}}{}\\#{type}{#{inner(el, opts)}}\\label{#{id}}\n\n"
@@ -213,7 +214,7 @@ module Kramdown
213
214
  end
214
215
 
215
216
  def convert_a(el, opts)
216
- url = el.options[:attr]['href']
217
+ url = el.attr['href']
217
218
  if url =~ /^#/
218
219
  "\\hyperlink{#{url[1..-1]}}{#{inner(el, opts)}}"
219
220
  else
@@ -222,12 +223,12 @@ module Kramdown
222
223
  end
223
224
 
224
225
  def convert_img(el, opts)
225
- if el.options[:attr]['src'] =~ /^(https?|ftps?):\/\//
226
+ if el.attr['src'] =~ /^(https?|ftps?):\/\//
226
227
  @doc.warnings << "Cannot include non-local image"
227
228
  ''
228
- elsif !el.options[:attr]['src'].empty?
229
+ elsif !el.options.attr['src'].empty?
229
230
  @doc.conversion_infos[:packages] << 'graphicx'
230
- "\\includegraphics{#{el.options[:attr]['src']}}"
231
+ "\\includegraphics{#{el.attr['src']}}"
231
232
  else
232
233
  @doc.warnings << "Cannot include image with empty path"
233
234
  ''
@@ -240,11 +241,15 @@ module Kramdown
240
241
 
241
242
  def convert_footnote(el, opts)
242
243
  @doc.conversion_infos[:packages] << 'fancyvrb'
243
- "\\footnote{#{inner(@doc.parse_infos[:footnotes][el.options[:name]][:content], opts)}}"
244
+ "\\footnote{#{inner(@doc.parse_infos[:footnotes][el.options[:name]][:content], opts).rstrip}}"
244
245
  end
245
246
 
246
247
  def convert_raw(el, opts)
247
- escape(el.value)
248
+ if !el.options[:type] || el.options[:type].empty? || el.options[:type].include?('latex')
249
+ el.value + (el.options[:category] == :block ? "\n" : '')
250
+ else
251
+ ''
252
+ end
248
253
  end
249
254
 
250
255
  def convert_em(el, opts)
@@ -561,7 +566,7 @@ module Kramdown
561
566
  end
562
567
 
563
568
  def attribute_list(el)
564
- attrs = (el.options[:attr] || {}).map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
569
+ attrs = el.attr.map {|k,v| v.nil? ? '' : " #{k}=\"#{v.to_s}\""}.compact.sort.join('')
565
570
  attrs = " % #{attrs}" if !attrs.empty?
566
571
  attrs
567
572
  end
@@ -136,10 +136,12 @@ module Kramdown
136
136
  # Many elements don't use this field.
137
137
  attr_accessor :value
138
138
 
139
+ # The attributes of the element. Uses an Utils::OrderedHash to retain the insertion order.
140
+ attr_reader :attr
141
+
139
142
  # The options hash for the element. It is used for storing arbitray options as well as the
140
143
  # following special contents:
141
144
  #
142
- # - *Attributes* of the element under the <tt>:attr</tt> key
143
145
  # - Category of the element, either <tt>:block</tt> or <tt>:span</tt>, under the
144
146
  # <tt>:category</tt> key. If this key is absent, it can be assumed that the element is in the
145
147
  # <tt>:span</tt> category.
@@ -149,15 +151,15 @@ module Kramdown
149
151
  attr_accessor :children
150
152
 
151
153
 
152
- # Create a new Element object of type +type+. The optional parameters +value+ and +options+ can
153
- # also be set in this constructor for convenience.
154
- def initialize(type, value = nil, options = {})
155
- @type, @value, @options = type, value, options
154
+ # Create a new Element object of type +type+. The optional parameters +value+, +attr+ and
155
+ # +options+ can also be set in this constructor for convenience.
156
+ def initialize(type, value = nil, attr = nil, options = {})
157
+ @type, @value, @attr, @options = type, value, Utils::OrderedHash.new(attr), options
156
158
  @children = []
157
159
  end
158
160
 
159
161
  def inspect #:nodoc:
160
- "<kd:#{@type}#{@value.nil? ? '' : ' ' + @value.inspect}#{options.empty? ? '' : ' ' + @options.inspect}#{@children.empty? ? '' : ' ' + @children.inspect}>"
162
+ "<kd:#{@type}#{@value.nil? ? '' : ' ' + @value.inspect} #{@attr.inspect}#{options.empty? ? '' : ' ' + @options.inspect}#{@children.empty? ? '' : ' ' + @children.inspect}>"
161
163
  end
162
164
 
163
165
  end
@@ -260,16 +260,6 @@ styles are directly applied to the code elements).
260
260
 
261
261
  Default: style
262
262
  Used by: HTML converter
263
- EOF
264
-
265
- define(:numeric_entities, Boolean, false, <<EOF)
266
- DEPRECATED: Defines whether entities are output using names or numeric values
267
-
268
- Note that this option is deprecated and replaced by the entities option.
269
- This option will be removed in a future release.
270
-
271
- Default: false
272
- Used by: HTML converter, kramdown converter
273
263
  EOF
274
264
 
275
265
  define(:entity_output, Symbol, :as_char, <<EOF)
@@ -292,6 +282,13 @@ and h2 headers but not for h3, h4, etc. A value of 0 uses all header levels.
292
282
 
293
283
  Default: 0
294
284
  Used by: HTML/Latex converter
285
+ EOF
286
+
287
+ define(:line_width, Integer, 72, <<EOF)
288
+ Defines the line width to be used when outputting a document
289
+
290
+ Default: 72
291
+ Used by: kramdown converter
295
292
  EOF
296
293
 
297
294
  end
@@ -39,7 +39,7 @@ module Kramdown
39
39
  HTML_INSTRUCTION_RE = /<\?(.*?)\?>/m
40
40
  HTML_ATTRIBUTE_RE = /\s*(#{REXML::Parsers::BaseParser::UNAME_STR})\s*=\s*(["'])(.*?)\2/m
41
41
  HTML_TAG_RE = /<((?>#{REXML::Parsers::BaseParser::UNAME_STR}))\s*((?>\s+#{REXML::Parsers::BaseParser::UNAME_STR}\s*=\s*(["']).*?\3)*)\s*(\/)?>/m
42
- HTML_TAG_CLOSE_RE = /<\/(#{REXML::Parsers::BaseParser::NAME_STR})\s*>/m
42
+ HTML_TAG_CLOSE_RE = /<\/(#{REXML::Parsers::BaseParser::UNAME_STR})\s*>/m
43
43
  HTML_ENTITY_RE = /&([\w:][\-\w\.:]*);|&#(\d+);|&\#x([0-9a-fA-F]+);/
44
44
 
45
45
 
@@ -80,10 +80,10 @@ module Kramdown
80
80
  def handle_html_start_tag
81
81
  name = @src[1]
82
82
  closed = !@src[4].nil?
83
- attrs = {}
83
+ attrs = Utils::OrderedHash.new
84
84
  @src[2].scan(HTML_ATTRIBUTE_RE).each {|attr,sep,val| attrs[attr] = val}
85
85
 
86
- el = Element.new(:html_element, name, :attr => attrs, :category => :block)
86
+ el = Element.new(:html_element, name, attrs, :category => :block)
87
87
  @tree.children << el
88
88
 
89
89
  if !closed && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
@@ -129,9 +129,9 @@ module Kramdown
129
129
  if result = @src.scan_until(HTML_RAW_START)
130
130
  add_text(result, @tree, :text)
131
131
  if result = @src.scan(HTML_COMMENT_RE)
132
- @tree.children << Element.new(:xml_comment, result, :category => :block, :parent_is_raw => true)
132
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :block)
133
133
  elsif result = @src.scan(HTML_INSTRUCTION_RE)
134
- @tree.children << Element.new(:xml_pi, result, :category => :block, :parent_is_raw => true)
134
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :block)
135
135
  elsif @src.scan(HTML_TAG_RE)
136
136
  handle_html_start_tag(&block)
137
137
  elsif @src.scan(HTML_TAG_CLOSE_RE)
@@ -242,7 +242,7 @@ module Kramdown
242
242
  elsif %w{mdash ndash hellip laquo raquo}.include?(val)
243
243
  Element.new(:typographic_sym, val.intern)
244
244
  else
245
- Element.new(:entity, entity(val), :original => src.matched)
245
+ Element.new(:entity, entity(val), nil, :original => src.matched)
246
246
  end
247
247
  else
248
248
  result << Element.new(:text, src.scan(/.*/m))
@@ -253,8 +253,7 @@ module Kramdown
253
253
 
254
254
  def process_html_element(el, do_conversion = true, preserve_text = false)
255
255
  el.options = {:category => HTML_SPAN_ELEMENTS.include?(el.value) ? :span : :block,
256
- :parse_type => HTML_PARSE_AS[el.value],
257
- :attr => el.options[:attr]
256
+ :parse_type => HTML_PARSE_AS[el.value]
258
257
  }
259
258
  process_children(el, do_conversion, preserve_text)
260
259
  end
@@ -263,15 +262,13 @@ module Kramdown
263
262
  el.children.delete_if {|c| c.type == :text}
264
263
  end
265
264
 
266
- SPAN_ELEMENTS = [:em, :strong, :br, :a, :img, :codespan, :entity, :smart_quote, :typographic_sym, :math]
267
-
268
265
  def wrap_text_children(el)
269
266
  tmp = []
270
267
  last_is_p = false
271
268
  el.children.each do |c|
272
269
  if c.options[:category] != :block || c.type == :text
273
270
  if !last_is_p
274
- tmp << Element.new(:p, nil, :transparent => true)
271
+ tmp << Element.new(:p, nil, nil, :transparent => true)
275
272
  last_is_p = true
276
273
  end
277
274
  tmp.last.children << c
@@ -306,7 +303,7 @@ module Kramdown
306
303
 
307
304
  def set_basics(el, type, category, opts = {})
308
305
  el.type = type
309
- el.options = {:category => category, :attr => el.options[:attr]}.merge(opts)
306
+ el.options = {:category => category}.merge(opts)
310
307
  el.value = nil
311
308
  end
312
309
 
@@ -316,7 +313,7 @@ module Kramdown
316
313
  end
317
314
 
318
315
  def convert_a(el)
319
- if el.options[:attr].has_key?('href')
316
+ if el.attr['href']
320
317
  set_basics(el, :a, :span)
321
318
  process_children(el)
322
319
  else
@@ -324,6 +321,16 @@ module Kramdown
324
321
  end
325
322
  end
326
323
 
324
+ def convert_b(el)
325
+ set_basics(el, :strong, :span)
326
+ process_children(el)
327
+ end
328
+
329
+ def convert_i(el)
330
+ set_basics(el, :em, :span)
331
+ process_children(el)
332
+ end
333
+
327
334
  def convert_h1(el)
328
335
  set_basics(el, :header, :block, :level => el.value[1..1].to_i)
329
336
  extract_text(el, el.options[:raw_text] = '')
@@ -366,6 +373,7 @@ module Kramdown
366
373
  set_basics(el, :codeblock, :block)
367
374
  end
368
375
  el.value = result.first.value
376
+ el.children.clear
369
377
  end
370
378
  end
371
379
  alias :convert_pre :convert_code
@@ -388,7 +396,7 @@ module Kramdown
388
396
  end
389
397
  calc_alignment.call(el)
390
398
  if el.children.first.type == :tr
391
- tbody = Element.new(:tbody, nil, :category => :block)
399
+ tbody = Element.new(:tbody, nil, nil, :category => :block)
392
400
  tbody.children = el.children
393
401
  el.children = [tbody]
394
402
  end
@@ -429,17 +437,17 @@ module Kramdown
429
437
  alias :convert_span :convert_div
430
438
 
431
439
  def is_math_tag?(el)
432
- el.options[:attr] && el.options[:attr]['class'].to_s =~ /\bmath\b/ &&
440
+ el.attr['class'].to_s =~ /\bmath\b/ &&
433
441
  el.children.size == 1 && el.children.first.type == :text
434
442
  end
435
443
 
436
444
  def handle_math_tag(el)
437
445
  set_basics(el, :math, (el.value == 'div' ? :block : :span))
438
446
  el.value = el.children.shift.value
439
- if el.options[:attr]['class'] =~ /^\s*math\s*$/
440
- el.options[:attr].delete('class')
447
+ if el.attr['class'] =~ /^\s*math\s*$/
448
+ el.attr.delete('class')
441
449
  else
442
- el.options[:attr]['class'].sub!(/\s?math/, '')
450
+ el.attr['class'].sub!(/\s?math/, '')
443
451
  end
444
452
  el.value.gsub!(/&(amp|quot|gt|lt);/) do |m|
445
453
  case m
@@ -462,11 +470,11 @@ module Kramdown
462
470
 
463
471
  while true
464
472
  if result = @src.scan(/\s*#{HTML_INSTRUCTION_RE}/)
465
- @tree.children << Element.new(:xml_pi, result.strip, :category => :block)
473
+ @tree.children << Element.new(:xml_pi, result.strip, nil, :category => :block)
466
474
  elsif result = @src.scan(/\s*#{HTML_DOCTYPE_RE}/)
467
- @tree.children << Element.new(:html_doctype, result.strip, :category => :block)
475
+ @tree.children << Element.new(:html_doctype, result.strip, nil, :category => :block)
468
476
  elsif result = @src.scan(/\s*#{HTML_COMMENT_RE}/)
469
- @tree.children << Element.new(:xml_comment, result.strip, :category => :block)
477
+ @tree.children << Element.new(:xml_comment, result.strip, nil, :category => :block)
470
478
  else
471
479
  break
472
480
  end
@@ -168,17 +168,28 @@ module Kramdown
168
168
  # Update the tree by parsing all <tt>:raw_text</tt> elements with the span level parser
169
169
  # (resets +@tree+, +@src+ and the +@stack+) and by updating the attributes from the IALs.
170
170
  def update_tree(element)
171
+ last_blank = nil
171
172
  element.children.map! do |child|
172
173
  if child.type == :raw_text
174
+ last_blank = nil
173
175
  @stack, @tree, @text_type = [], nil, :text
174
176
  @src = StringScanner.new(child.value)
175
177
  parse_spans(child)
176
178
  child.children
177
179
  elsif child.type == :eob
178
180
  []
181
+ elsif child.type == :blank
182
+ if last_blank
183
+ last_blank.value += child.value
184
+ []
185
+ else
186
+ last_blank = child
187
+ child
188
+ end
179
189
  else
190
+ last_blank = nil
180
191
  update_tree(child)
181
- update_attr_with_ial(child.options[:attr] ||= {}, child.options[:ial]) if child.options[:ial]
192
+ update_attr_with_ial(child.attr, child.options[:ial]) if child.options[:ial]
182
193
  child
183
194
  end
184
195
  end.flatten!
@@ -227,8 +238,13 @@ module Kramdown
227
238
  ial[:refs].each do |ref|
228
239
  update_attr_with_ial(attr, ref) if ref = @doc.parse_infos[:ald][ref]
229
240
  end if ial[:refs]
230
- attr['class'] = ((attr['class'] || '') + " #{ial['class']}").lstrip if ial['class']
231
- ial.each {|k,v| attr[k] = v if k.kind_of?(String) && k != 'class' }
241
+ ial.each do |k,v|
242
+ if k == 'class'
243
+ attr[k] = ((attr[k] || '') + " #{v}").lstrip
244
+ elsif k.kind_of?(String)
245
+ attr[k] = v
246
+ end
247
+ end
232
248
  end
233
249
 
234
250
  # Create a new block level element, taking care of applying a preceding block IAL if it exists.
@@ -32,6 +32,7 @@ module Kramdown
32
32
  abbrev_id, abbrev_text = @src[1], @src[2].strip
33
33
  warning("Duplicate abbreviation ID '#{abbrev_id}' - overwriting") if @doc.parse_infos[:abbrev_defs][abbrev_id]
34
34
  @doc.parse_infos[:abbrev_defs][abbrev_id] = abbrev_text
35
+ @tree.children << Element.new(:eob, :abbrev_def)
35
36
  true
36
37
  end
37
38
  define_parser(:abbrev_definition, ABBREV_DEFINITION_START)
@@ -43,8 +43,13 @@ module Kramdown
43
43
  # Update the +ial+ with the information from the inline attribute list +opts+.
44
44
  def update_ial_with_ial(ial, opts)
45
45
  (ial[:refs] ||= []) << opts[:refs]
46
- ial['class'] = ((ial['class'] || '') + " #{opts['class']}").lstrip if opts['class']
47
- opts.each {|k,v| ial[k] = v if k != :refs && k != 'class' }
46
+ opts.each do |k,v|
47
+ if k == 'class'
48
+ ial[k] = ((ial[k] || '') + " #{v}").lstrip
49
+ elsif k.kind_of?(String)
50
+ ial[k] = v
51
+ end
52
+ end
48
53
  end
49
54
 
50
55
 
@@ -53,7 +58,7 @@ module Kramdown
53
58
  ALD_ID_NAME = /\w#{ALD_ID_CHARS}*/
54
59
  ALD_TYPE_KEY_VALUE_PAIR = /(#{ALD_ID_NAME})=("|')((?:\\\}|\\\2|[^\}\2])*?)\2/
55
60
  ALD_TYPE_CLASS_NAME = /\.(#{ALD_ID_NAME})/
56
- ALD_TYPE_ID_NAME = /#(#{ALD_ID_NAME})/
61
+ ALD_TYPE_ID_NAME = /#(\w[\w:-]*)/
57
62
  ALD_TYPE_REF = /(#{ALD_ID_NAME})/
58
63
  ALD_TYPE_ANY = /(?:\A|\s)(?:#{ALD_TYPE_KEY_VALUE_PAIR}|#{ALD_TYPE_ID_NAME}|#{ALD_TYPE_CLASS_NAME}|#{ALD_TYPE_REF})(?=\s|\Z)/
59
64
  ALD_START = /^#{OPT_SPACE}\{:(#{ALD_ID_NAME}):(#{ALD_ANY_CHARS}+)\}\s*?\n/
@@ -61,24 +66,25 @@ module Kramdown
61
66
  # Parse the attribute list definition at the current location.
62
67
  def parse_ald
63
68
  @src.pos += @src.matched_size
64
- parse_attribute_list(@src[2], @doc.parse_infos[:ald][@src[1]] ||= {})
65
- @tree.children << Element.new(:eob)
69
+ parse_attribute_list(@src[2], @doc.parse_infos[:ald][@src[1]] ||= Utils::OrderedHash.new)
70
+ @tree.children << Element.new(:eob, :ald)
66
71
  true
67
72
  end
68
73
  define_parser(:ald, ALD_START)
69
74
 
70
75
 
71
- IAL_BLOCK_START = /^#{OPT_SPACE}\{:(?!:)(#{ALD_ANY_CHARS}+)\}\s*?\n/
76
+ IAL_BLOCK = /\{:(?!:|\/)(#{ALD_ANY_CHARS}+)\}\s*?\n/
77
+ IAL_BLOCK_START = /^#{OPT_SPACE}#{IAL_BLOCK}/
72
78
 
73
79
  # Parse the inline attribute list at the current location.
74
80
  def parse_block_ial
75
81
  @src.pos += @src.matched_size
76
82
  if @tree.children.last && @tree.children.last.type != :blank && @tree.children.last.type != :eob
77
- parse_attribute_list(@src[1], @tree.children.last.options[:ial] ||= {})
83
+ parse_attribute_list(@src[1], @tree.children.last.options[:ial] ||= Utils::OrderedHash.new)
84
+ @tree.children << Element.new(:eob, :ial) unless @src.check(IAL_BLOCK_START)
78
85
  else
79
- parse_attribute_list(@src[1], @block_ial = {})
86
+ parse_attribute_list(@src[1], @block_ial = Utils::OrderedHash.new)
80
87
  end
81
- @tree.children << Element.new(:eob) unless @src.check(IAL_BLOCK_START)
82
88
  true
83
89
  end
84
90
  define_parser(:block_ial, IAL_BLOCK_START)
@@ -90,10 +96,10 @@ module Kramdown
90
96
  def parse_span_ial
91
97
  @src.pos += @src.matched_size
92
98
  if @tree.children.last && @tree.children.last.type != :text
93
- attr = {}
99
+ attr = Utils::OrderedHash.new
94
100
  parse_attribute_list(@src[1], attr)
95
- update_ial_with_ial(@tree.children.last.options[:ial] ||= {}, attr)
96
- update_attr_with_ial(@tree.children.last.options[:attr] ||= {}, attr)
101
+ update_ial_with_ial(@tree.children.last.options[:ial] ||= Utils::OrderedHash.new, attr)
102
+ update_attr_with_ial(@tree.children.last.attr, attr)
97
103
  else
98
104
  warning("Ignoring span IAL because preceding element is just text")
99
105
  end
@@ -43,7 +43,7 @@ module Kramdown
43
43
  @src.pos += @src.matched_size
44
44
  href = @src[1]
45
45
  href= "mailto:#{href}" if @src[2].nil?
46
- el = Element.new(:a, nil, {:attr => {'href' => href}})
46
+ el = Element.new(:a, nil, {'href' => href})
47
47
  add_text(@src[1].sub(/^mailto:/, ''), el)
48
48
  @tree.children << el
49
49
  end