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
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2010 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown.
7
+ #
8
+ # kramdown is free software: you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation, either version 3 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
20
+ #++
21
+ #
22
+
23
+ require 'kramdown/parser/kramdown/attribute_list'
24
+ require 'kramdown/parser/kramdown/blank_line'
25
+ require 'kramdown/parser/kramdown/eob'
26
+
27
+ module Kramdown
28
+ module Parser
29
+ class Kramdown
30
+
31
+ BLOCK_BOUNDARY = /#{BLANK_LINE}|#{EOB_MARKER}|#{IAL_BLOCK_START}|\Z/
32
+
33
+ # Return +true+ if we are after a block boundary.
34
+ def after_block_boundary?
35
+ !@tree.children.last || @tree.children.last.type == :blank ||
36
+ (@tree.children.last.type == :eob && @tree.children.last.value.nil?) || @block_ial
37
+ end
38
+
39
+ # Return +true+ if we are before a block boundary.
40
+ def before_block_boundary?
41
+ @src.check(BLOCK_BOUNDARY)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -20,23 +20,27 @@
20
20
  #++
21
21
  #
22
22
 
23
+ require 'kramdown/parser/kramdown/blank_line'
24
+ require 'kramdown/parser/kramdown/attribute_list'
25
+ require 'kramdown/parser/kramdown/eob'
26
+
23
27
  module Kramdown
24
28
  module Parser
25
29
  class Kramdown
26
30
 
27
31
  BLOCKQUOTE_START = /^#{OPT_SPACE}> ?/
28
- BLOCKQUOTE_MATCH = /(^#{OPT_SPACE}>.*?\n)+/
32
+ BLOCKQUOTE_MATCH = /(^.*\n)+?(?=#{BLANK_LINE}|#{IAL_BLOCK_START}|#{EOB_MARKER}|^#{OPT_SPACE}#{LAZY_END_HTML_STOP}|^#{OPT_SPACE}#{LAZY_END_HTML_START}|\Z)/
29
33
 
30
34
  # Parse the blockquote at the current location.
31
35
  def parse_blockquote
32
- result = @src.scan(BLOCKQUOTE_MATCH).gsub(BLOCKQUOTE_START, '')
33
36
  el = new_block_el(:blockquote)
34
37
  @tree.children << el
35
- parse_blocks(el, result)
38
+ parse_blocks(el, @src.scan(BLOCKQUOTE_MATCH).gsub!(BLOCKQUOTE_START, ''))
36
39
  true
37
40
  end
38
41
  define_parser(:blockquote, BLOCKQUOTE_START)
39
42
 
43
+
40
44
  end
41
45
  end
42
46
  end
@@ -21,18 +21,20 @@
21
21
  #
22
22
 
23
23
  require 'kramdown/parser/kramdown/blank_line'
24
+ require 'kramdown/parser/kramdown/attribute_list'
25
+ require 'kramdown/parser/kramdown/eob'
26
+ require 'kramdown/parser/kramdown/paragraph'
24
27
 
25
28
  module Kramdown
26
29
  module Parser
27
30
  class Kramdown
28
31
 
29
32
  CODEBLOCK_START = INDENT
30
- CODEBLOCK_LINE = /(?:#{INDENT}.*?\S.*?\n)+/
31
- CODEBLOCK_MATCH = /(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*/
33
+ CODEBLOCK_MATCH = /(?:#{BLANK_LINE}?(?:#{INDENT}[ \t]*\S.*\n)+(?:(?!#{BLANK_LINE} {0,3}\S|#{IAL_BLOCK_START}|#{EOB_MARKER}|^#{OPT_SPACE}#{LAZY_END_HTML_STOP}|^#{OPT_SPACE}#{LAZY_END_HTML_START})^[ \t]*\S.*\n)*)*/
32
34
 
33
35
  # Parse the indented codeblock at the current location.
34
36
  def parse_codeblock
35
- @tree.children << new_block_el(:codeblock, @src.scan(CODEBLOCK_MATCH).gsub!(INDENT, ''))
37
+ @tree.children << new_block_el(:codeblock, @src.scan(CODEBLOCK_MATCH).gsub(/\n( {0,3}\S)/, ' \\1').gsub!(INDENT, ''))
36
38
  true
37
39
  end
38
40
  define_parser(:codeblock, CODEBLOCK_START)
@@ -24,7 +24,7 @@ module Kramdown
24
24
  module Parser
25
25
  class Kramdown
26
26
 
27
- ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}#!:|"'\$-])/
27
+ ESCAPED_CHARS = /\\([\\.*_+`()\[\]{}#!:|"'\$=-])/
28
28
 
29
29
  # Parse the backslash-escaped character at the current location.
30
30
  def parse_escaped_chars
@@ -27,12 +27,19 @@ module Kramdown
27
27
  class Kramdown
28
28
 
29
29
  def parse_extension_start_tag(type)
30
+ orig_pos = @src.pos
30
31
  @src.pos += @src.matched_size
31
32
 
33
+ error_block = lambda do |msg|
34
+ warning(msg)
35
+ @src.pos = orig_pos
36
+ add_text(@src.scan(/./)) if type == :span
37
+ false
38
+ end
39
+
32
40
  if @src[4] || @src.matched == '{:/}'
33
41
  name = (@src[4] ? "for '#{@src[4]}' " : '')
34
- warning("Invalid extension stop tag #{name}found - ignoring it")
35
- return
42
+ return error_block.call("Invalid extension stop tag #{name}found - ignoring it")
36
43
  end
37
44
 
38
45
  ext = @src[1]
@@ -46,19 +53,25 @@ module Kramdown
46
53
  body = result.sub!(stop_re, '')
47
54
  body.chomp! if type == :block
48
55
  else
49
- warning("No stop tag for extension '#{ext}' found - treating it as extension without body")
56
+ return error_block.call("No stop tag for extension '#{ext}' found - ignoring it")
50
57
  end
51
58
  end
52
59
 
53
- handle_extension(ext, opts, body, type)
60
+ if !handle_extension(ext, opts, body, type)
61
+ error_block.call("Invalid extension with name '#{ext}' specified - ignoring it")
62
+ else
63
+ true
64
+ end
54
65
  end
55
66
 
56
67
  def handle_extension(name, opts, body, type)
57
68
  case name
58
69
  when 'comment'
59
- @tree.children << Element.new(:comment, body, :category => type) if body.kind_of?(String)
70
+ @tree.children << Element.new(:comment, body, nil, :category => type) if body.kind_of?(String)
71
+ true
60
72
  when 'nomarkdown'
61
- @tree.children << Element.new(:raw, body, :category => type) if body.kind_of?(String)
73
+ @tree.children << Element.new(:raw, body, nil, :category => type, :type => opts['type'].to_s.split(/\s+/)) if body.kind_of?(String)
74
+ true
62
75
  when 'options'
63
76
  opts.select do |k,v|
64
77
  k = k.to_sym
@@ -71,8 +84,10 @@ module Kramdown
71
84
  end.each do |k,v|
72
85
  warning("Unknown kramdown option '#{k}'")
73
86
  end
87
+ @tree.children << Element.new(:eob, :extension) if type == :block
88
+ true
74
89
  else
75
- warning("Invalid extension name '#{name}' specified - ignoring extension")
90
+ false
76
91
  end
77
92
  end
78
93
 
@@ -86,7 +101,6 @@ module Kramdown
86
101
  # Parse the extension block at the current location.
87
102
  def parse_block_extension
88
103
  parse_extension_start_tag(:block)
89
- true
90
104
  end
91
105
  define_parser(:block_extension, EXT_BLOCK_START)
92
106
 
@@ -28,7 +28,7 @@ module Kramdown
28
28
  module Parser
29
29
  class Kramdown
30
30
 
31
- FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n(?:#{BLANK_LINE}?#{CODEBLOCK_LINE})*)/
31
+ FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n#{CODEBLOCK_MATCH})/
32
32
 
33
33
  # Parse the foot note definition at the current location.
34
34
  def parse_footnote_definition
@@ -38,6 +38,7 @@ module Kramdown
38
38
  parse_blocks(el, @src[2].gsub(INDENT, ''))
39
39
  warning("Duplicate footnote name '#{@src[1]}' - overwriting") if @doc.parse_infos[:footnotes][@src[1]]
40
40
  (@doc.parse_infos[:footnotes][@src[1]] = {})[:content] = el
41
+ @tree.children << Element.new(:eob, :footnote_def)
41
42
  true
42
43
  end
43
44
  define_parser(:footnote_definition, FOOTNOTE_DEFINITION_START)
@@ -54,7 +55,7 @@ module Kramdown
54
55
  par.children.include?(child)
55
56
  end
56
57
  if !fn_def[:marker] || !valid
57
- fn_def[:marker] = Element.new(:footnote, nil, :name => @src[1])
58
+ fn_def[:marker] = Element.new(:footnote, nil, nil, :name => @src[1])
58
59
  fn_def[:marker].options[:stack] = [@stack.map {|s| s.first}, @tree, fn_def[:marker]].flatten.compact
59
60
  @tree.children << fn_def[:marker]
60
61
  else
@@ -20,6 +20,8 @@
20
20
  #++
21
21
  #
22
22
 
23
+ require 'kramdown/parser/kramdown/block_boundary'
24
+
23
25
  module Kramdown
24
26
  module Parser
25
27
  class Kramdown
@@ -29,14 +31,13 @@ module Kramdown
29
31
 
30
32
  # Parse the Setext header at the current location.
31
33
  def parse_setext_header
32
- if @tree.children.last && @tree.children.last.type != :blank
33
- return false
34
- end
34
+ return false if !after_block_boundary?
35
+
35
36
  @src.pos += @src.matched_size
36
37
  text, id, level = @src[1].strip, @src[2], @src[3]
37
- el = new_block_el(:header, nil, :level => (level == '-' ? 2 : 1), :raw_text => text)
38
+ el = new_block_el(:header, nil, nil, :level => (level == '-' ? 2 : 1), :raw_text => text)
38
39
  add_text(text, el)
39
- el.options[:attr] = {'id' => id} if id
40
+ el.attr['id'] = id if id
40
41
  @tree.children << el
41
42
  true
42
43
  end
@@ -48,14 +49,13 @@ module Kramdown
48
49
 
49
50
  # Parse the Atx header at the current location.
50
51
  def parse_atx_header
51
- if @tree.children.last && @tree.children.last.type != :blank
52
- return false
53
- end
52
+ return false if !after_block_boundary?
53
+
54
54
  result = @src.scan(ATX_HEADER_MATCH)
55
55
  level, text, id = @src[1], @src[2].strip, @src[3]
56
- el = new_block_el(:header, nil, :level => level.length, :raw_text => text)
56
+ el = new_block_el(:header, nil, nil, :level => level.length, :raw_text => text)
57
57
  add_text(text, el)
58
- el.options[:attr] = {'id' => id} if id
58
+ el.attr['id'] = id if id
59
59
  @tree.children << el
60
60
  true
61
61
  end
@@ -34,13 +34,11 @@ module Kramdown
34
34
  else
35
35
  :raw
36
36
  end
37
- if val = html_parse_type(el.options[:attr].delete('markdown'))
37
+ if val = html_parse_type(el.attr.delete('markdown'))
38
38
  parse_type = (val == :default ? HTML_PARSE_AS[el.value] : val)
39
39
  end
40
40
 
41
41
  @src.scan(/[ \t]*\n/) if parse_type == :block
42
- el.options[:outer_element] = true if @tree.type != :html_element
43
- el.options[:parent_is_raw] = true if @tree.type == :html_element && @tree.options[:parse_type] == :raw
44
42
  el.options[:parse_type] = parse_type
45
43
 
46
44
  if !closed
@@ -87,11 +85,11 @@ module Kramdown
87
85
  # Parse the HTML at the current position as block level HTML.
88
86
  def parse_block_html
89
87
  if result = @src.scan(HTML_COMMENT_RE)
90
- @tree.children << Element.new(:xml_comment, result, :category => :block)
88
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :block)
91
89
  @src.scan(/[ \t]*\n/)
92
90
  true
93
91
  elsif result = @src.scan(HTML_INSTRUCTION_RE)
94
- @tree.children << Element.new(:xml_pi, result, :category => :block)
92
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :block)
95
93
  @src.scan(/[ \t]*\n/)
96
94
  true
97
95
  else
@@ -101,14 +99,13 @@ module Kramdown
101
99
  Kramdown::Parser::Html::ElementConverter.new(@doc).process(@tree.children.last) if @doc.options[:html_to_native]
102
100
  true
103
101
  elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1])
104
- @src.pos += @src.matched_size
105
102
  name = @src[1]
106
103
 
107
104
  if @tree.type == :html_element && @tree.value == name
105
+ @src.pos += @src.matched_size
108
106
  throw :stop_block_parsing, :found
109
107
  else
110
- warning("Found invalidly used HTML closing tag for '#{name}' - ignoring it")
111
- true
108
+ false
112
109
  end
113
110
  else
114
111
  false
@@ -123,16 +120,21 @@ module Kramdown
123
120
  # Parse the HTML at the current position as span level HTML.
124
121
  def parse_span_html
125
122
  if result = @src.scan(HTML_COMMENT_RE)
126
- @tree.children << Element.new(:xml_comment, result, :category => :span)
123
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :span)
127
124
  elsif result = @src.scan(HTML_INSTRUCTION_RE)
128
- @tree.children << Element.new(:xml_pi, result, :category => :span)
125
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :span)
129
126
  elsif result = @src.scan(HTML_TAG_CLOSE_RE)
130
- warning("Found invalidly used HTML closing tag for '#{@src[1]}' - ignoring it")
127
+ warning("Found invalidly used HTML closing tag for '#{@src[1]}'")
128
+ add_text(result)
131
129
  elsif result = @src.scan(HTML_TAG_RE)
132
- return if HTML_BLOCK_ELEMENTS.include?(@src[1])
130
+ if HTML_BLOCK_ELEMENTS.include?(@src[1])
131
+ warning("Found block HTML tag '#{@src[1]}' in span level text")
132
+ add_text(result)
133
+ return
134
+ end
133
135
 
134
136
  reset_pos = @src.pos
135
- attrs = {}
137
+ attrs = Utils::OrderedHash.new
136
138
  @src[2].scan(HTML_ATTRIBUTE_RE).each {|name,sep,val| attrs[name] = val.gsub(/\n+/, ' ')}
137
139
 
138
140
  do_parsing = (HTML_PARSE_AS_RAW.include?(@src[1]) || @tree.options[:parse_type] == :raw ? false : @doc.options[:parse_span_html])
@@ -148,7 +150,7 @@ module Kramdown
148
150
  end
149
151
  end
150
152
 
151
- el = Element.new(:html_element, @src[1], :attr => attrs, :category => :span, :parse_type => (do_parsing ? :span : :raw))
153
+ el = Element.new(:html_element, @src[1], attrs, :category => :span, :parse_type => (do_parsing ? :span : :raw))
152
154
  @tree.children << el
153
155
  stop_re = /<\/#{Regexp.escape(@src[1])}\s*>/
154
156
  if !@src[4] && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
@@ -30,7 +30,7 @@ module Kramdown
30
30
  def parse_html_entity
31
31
  @src.pos += @src.matched_size
32
32
  @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex),
33
- :original => @src.matched)
33
+ nil, :original => @src.matched)
34
34
  end
35
35
  define_parser(:html_entity, Kramdown::Parser::Html::Constants::HTML_ENTITY_RE, '&')
36
36
 
@@ -27,7 +27,7 @@ module Kramdown
27
27
  PUNCTUATION_CHARS = "_.:,;!?-"
28
28
  LINK_ID_CHARS = /[a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
29
29
  LINK_ID_NON_CHARS = /[^a-zA-Z0-9 #{PUNCTUATION_CHARS}]/
30
- LINK_DEFINITION_START = /^#{OPT_SPACE}\[(#{LINK_ID_CHARS}+)\]:[ \t]*(?:<(.*?)>|([^\s]+))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
30
+ LINK_DEFINITION_START = /^#{OPT_SPACE}\[(#{LINK_ID_CHARS}+)\]:[ \t]*(?:<(.*?)>|([^'"\n]*?\S[^'"\n]*?))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
31
31
 
32
32
  # Parse the link definition at the current location.
33
33
  def parse_link_definition
@@ -35,6 +35,7 @@ module Kramdown
35
35
  link_id, link_url, link_title = @src[1].downcase, @src[2] || @src[3], @src[5]
36
36
  warning("Duplicate link ID '#{link_id}' - overwriting") if @doc.parse_infos[:link_defs][link_id]
37
37
  @doc.parse_infos[:link_defs][link_id] = [link_url, link_title]
38
+ @tree.children << Element.new(:eob, :link_def)
38
39
  true
39
40
  end
40
41
  define_parser(:link_definition, LINK_DEFINITION_START)
@@ -43,15 +44,14 @@ module Kramdown
43
44
  # This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
44
45
  # and the element itself to the <tt>@tree</tt>.
45
46
  def add_link(el, href, title, alt_text = nil)
46
- el.options[:attr] ||= {}
47
- el.options[:attr]['title'] = title if title
48
47
  if el.type == :a
49
- el.options[:attr]['href'] = href
48
+ el.attr['href'] = href
50
49
  else
51
- el.options[:attr]['src'] = href
52
- el.options[:attr]['alt'] = alt_text
50
+ el.attr['src'] = href
51
+ el.attr['alt'] = alt_text
53
52
  el.children.clear
54
53
  end
54
+ el.attr['title'] = title if title
55
55
  @tree.children << el
56
56
  end
57
57
 
@@ -120,7 +120,7 @@ module Kramdown
120
120
  end
121
121
  else
122
122
  link_url = ''
123
- re = /\(|\)|\s/
123
+ re = /\(|\)|\s(?=['"])/
124
124
  nr_of_brackets = 0
125
125
  while temp = @src.scan_until(re)
126
126
  link_url += temp
@@ -134,7 +134,7 @@ module Kramdown
134
134
  break if nr_of_brackets == 0
135
135
  end
136
136
  end
137
- link_url = link_url[1..-2]
137
+ link_url = link_url[1..-2].strip
138
138
 
139
139
  if nr_of_brackets == 0
140
140
  add_link(el, link_url, nil, alt_text)
@@ -29,10 +29,12 @@ module Kramdown
29
29
  module Parser
30
30
  class Kramdown
31
31
 
32
+ LIST_ITEM_IAL = /^\s*(#{IAL_SPAN_START})?\s*\n/
33
+
32
34
  # Used for parsing the first line of a list item or a definition, i.e. the line with list item
33
35
  # marker or the definition marker.
34
36
  def parse_first_list_line(indentation, content)
35
- if content =~ /^\s*(#{IAL_SPAN_START})?\s*\n/
37
+ if content =~ LIST_ITEM_IAL
36
38
  indentation = 4
37
39
  else
38
40
  while content =~ /^ *\t/
@@ -44,8 +46,9 @@ module Kramdown
44
46
  content.sub!(/^\s*/, '')
45
47
 
46
48
  indent_re = /^ {#{indentation}}/
47
- content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*?\n/
48
- [content, indentation, content_re, indent_re]
49
+ content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*\S.*\n/
50
+ lazy_re = /(?!^ {0,#{[indentation, 3].min}}(?:#{IAL_BLOCK}|#{LAZY_END_HTML_STOP}|#{LAZY_END_HTML_START})).*\S.*\n/
51
+ [content, indentation, content_re, lazy_re, indent_re]
49
52
  end
50
53
 
51
54
 
@@ -55,24 +58,23 @@ module Kramdown
55
58
 
56
59
  # Parse the ordered or unordered list at the current location.
57
60
  def parse_list
58
- if @tree.children.last && @tree.children.last.type == :p # last element must not be a paragraph
59
- return false
60
- end
61
-
62
61
  type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
63
62
  list = new_block_el(type)
64
63
 
65
64
  item = nil
66
- indent_re = nil
67
- content_re = nil
65
+ content_re, lazy_re, indent_re = nil
68
66
  eob_found = false
69
67
  nested_list_found = false
68
+ last_is_blank = false
70
69
  while !@src.eos?
71
- if @src.check(HR_START)
70
+ if last_is_blank && @src.check(HR_START)
71
+ break
72
+ elsif @src.scan(EOB_MARKER)
73
+ eob_found = true
72
74
  break
73
75
  elsif @src.scan(list_start_re)
74
76
  item = Element.new(:li)
75
- item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
77
+ item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
76
78
  list.children << item
77
79
 
78
80
  item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match|
@@ -82,26 +84,21 @@ module Kramdown
82
84
 
83
85
  list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
84
86
  /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
85
- nested_list_found = false
86
- elsif result = @src.scan(content_re)
87
+ nested_list_found = (item.value =~ LIST_START)
88
+ last_is_blank = false
89
+ elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
87
90
  result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) }
88
91
  result.sub!(indent_re, '')
89
92
  if !nested_list_found && result =~ LIST_START
90
- parse_blocks(item, item.value)
91
- if item.children.length == 1 && item.children.first.type == :p
92
- item.value = ''
93
- else
94
- item.children.clear
95
- end
93
+ item.value << "^\n"
96
94
  nested_list_found = true
97
95
  end
98
96
  item.value << result
97
+ last_is_blank = false
99
98
  elsif result = @src.scan(BLANK_LINE)
100
99
  nested_list_found = true
100
+ last_is_blank = true
101
101
  item.value << result
102
- elsif @src.scan(EOB_MARKER)
103
- eob_found = true
104
- break
105
102
  else
106
103
  break
107
104
  end
@@ -113,12 +110,17 @@ module Kramdown
113
110
  list.children.each do |it|
114
111
  temp = Element.new(:temp)
115
112
  parse_blocks(temp, it.value)
116
- it.children += temp.children
113
+ it.children = temp.children
117
114
  it.value = nil
118
115
  next if it.children.size == 0
119
116
 
120
- if it.children.first.type == :p && (it.children.length < 2 || it.children[1].type != :blank ||
121
- (it == list.children.last && it.children.length == 2 && !eob_found)) &&
117
+ # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
118
+ it.children.delete_at(1) if it.children.first.type == :p &&
119
+ it.children.length >= 2 && it.children[1].type == :eob && it.children.first.options[:ial]
120
+
121
+ if it.children.first.type == :p &&
122
+ (it.children.length < 2 || it.children[1].type != :blank ||
123
+ (it == list.children.last && it.children.length == 2 && !eob_found)) &&
122
124
  (list.children.last != it || list.children.size == 1 ||
123
125
  list.children[0..-2].any? {|cit| cit.children.first.type != :p || cit.children.first.options[:transparent]})
124
126
  it.children.first.children.first.value += "\n" if it.children.size > 1 && it.children[1].type != :blank
@@ -163,14 +165,14 @@ module Kramdown
163
165
  end
164
166
 
165
167
  item = nil
166
- indent_re = nil
167
- content_re = nil
168
+ content_re, lazy_re, indent_re = nil
168
169
  def_start_re = DEFINITION_LIST_START
170
+ last_is_blank = false
169
171
  while !@src.eos?
170
172
  if @src.scan(def_start_re)
171
173
  item = Element.new(:dd)
172
174
  item.options[:first_as_para] = first_as_para
173
- item.value, indentation, content_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
175
+ item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
174
176
  deflist.children << item
175
177
 
176
178
  item.value.sub!(/^#{IAL_SPAN_START}\s*/) do |match|
@@ -180,14 +182,19 @@ module Kramdown
180
182
 
181
183
  def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/
182
184
  first_as_para = false
183
- elsif result = @src.scan(content_re)
185
+ last_is_blank = false
186
+ elsif @src.check(EOB_MARKER)
187
+ break
188
+ elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
184
189
  result.sub!(/^(\t+)/) { " "*4*($1 ? $1.length : 0) }
185
190
  result.sub!(indent_re, '')
186
191
  item.value << result
187
192
  first_as_para = false
193
+ last_is_blank = false
188
194
  elsif result = @src.scan(BLANK_LINE)
189
195
  first_as_para = true
190
196
  item.value << result
197
+ last_is_blank = true
191
198
  else
192
199
  break
193
200
  end