motion-kramdown 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +84 -0
  3. data/lib/kramdown/compatibility.rb +36 -0
  4. data/lib/kramdown/converter/base.rb +259 -0
  5. data/lib/kramdown/converter/html.rb +461 -0
  6. data/lib/kramdown/converter/kramdown.rb +423 -0
  7. data/lib/kramdown/converter/latex.rb +600 -0
  8. data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
  9. data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
  10. data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
  11. data/lib/kramdown/converter/pdf.rb +624 -0
  12. data/lib/kramdown/converter/remove_html_tags.rb +53 -0
  13. data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
  14. data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
  15. data/lib/kramdown/converter/toc.rb +69 -0
  16. data/lib/kramdown/converter.rb +69 -0
  17. data/lib/kramdown/document.rb +144 -0
  18. data/lib/kramdown/element.rb +515 -0
  19. data/lib/kramdown/error.rb +17 -0
  20. data/lib/kramdown/options.rb +584 -0
  21. data/lib/kramdown/parser/base.rb +130 -0
  22. data/lib/kramdown/parser/gfm.rb +55 -0
  23. data/lib/kramdown/parser/html.rb +575 -0
  24. data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
  25. data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
  26. data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
  27. data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
  28. data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
  29. data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
  30. data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
  31. data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
  32. data/lib/kramdown/parser/kramdown/eob.rb +26 -0
  33. data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
  34. data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
  35. data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/header.rb +59 -0
  37. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
  38. data/lib/kramdown/parser/kramdown/html.rb +160 -0
  39. data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
  40. data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
  41. data/lib/kramdown/parser/kramdown/link.rb +139 -0
  42. data/lib/kramdown/parser/kramdown/list.rb +256 -0
  43. data/lib/kramdown/parser/kramdown/math.rb +54 -0
  44. data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
  45. data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
  46. data/lib/kramdown/parser/kramdown/table.rb +171 -0
  47. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
  48. data/lib/kramdown/parser/kramdown.rb +359 -0
  49. data/lib/kramdown/parser/markdown.rb +56 -0
  50. data/lib/kramdown/parser.rb +27 -0
  51. data/lib/kramdown/utils/configurable.rb +44 -0
  52. data/lib/kramdown/utils/entities.rb +347 -0
  53. data/lib/kramdown/utils/html.rb +75 -0
  54. data/lib/kramdown/utils/ordered_hash.rb +87 -0
  55. data/lib/kramdown/utils/string_scanner.rb +74 -0
  56. data/lib/kramdown/utils/unidecoder.rb +51 -0
  57. data/lib/kramdown/utils.rb +58 -0
  58. data/lib/kramdown/version.rb +15 -0
  59. data/lib/kramdown.rb +10 -0
  60. data/lib/motion-kramdown.rb +47 -0
  61. data/lib/rubymotion/encodings.rb +37 -0
  62. data/lib/rubymotion/rexml_shim.rb +25 -0
  63. data/lib/rubymotion/set.rb +1349 -0
  64. data/lib/rubymotion/version.rb +6 -0
  65. data/spec/document_tree.rb +48 -0
  66. data/spec/gfm_to_html.rb +95 -0
  67. data/spec/helpers/it_behaves_like.rb +27 -0
  68. data/spec/helpers/option_file.rb +46 -0
  69. data/spec/helpers/spec_options.rb +37 -0
  70. data/spec/helpers/tidy.rb +12 -0
  71. data/spec/html_to_html.rb +40 -0
  72. data/spec/html_to_kramdown_to_html.rb +46 -0
  73. data/spec/kramdown_to_xxx.rb +40 -0
  74. data/spec/test_location.rb +203 -0
  75. data/spec/test_string_scanner_kramdown.rb +19 -0
  76. data/spec/text_to_kramdown_to_html.rb +52 -0
  77. data/spec/text_to_latex.rb +33 -0
  78. metadata +164 -0
@@ -0,0 +1,27 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+ module Parser
12
+ class Kramdown
13
+
14
+ HR_START = /^#{OPT_SPACE}(\*|-|_)[ \t]*\1[ \t]*\1(\1|[ \t])*\n/
15
+
16
+ # Parse the horizontal rule at the current location.
17
+ def parse_horizontal_rule
18
+ start_line_number = @src.current_line_number
19
+ @src.pos += @src.matched_size
20
+ @tree.children << new_block_el(:hr, nil, nil, :location => start_line_number)
21
+ true
22
+ end
23
+ define_parser(:horizontal_rule, HR_START)
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,160 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/html'
11
+
12
+ module Kramdown
13
+ module Parser
14
+ class Kramdown
15
+
16
+ include Kramdown::Parser::Html::Parser
17
+
18
+ # Mapping of markdown attribute value to content model. I.e. :raw when "0", :default when "1"
19
+ # (use default content model for the HTML element), :span when "span", :block when block and
20
+ # for everything else +nil+ is returned.
21
+ HTML_MARKDOWN_ATTR_MAP = {"0" => :raw, "1" => :default, "span" => :span, "block" => :block}
22
+
23
+ TRAILING_WHITESPACE = /[ \t]*\n/
24
+
25
+ def handle_kramdown_html_tag(el, closed, handle_body)
26
+ el.options[:ial] = @block_ial if @block_ial
27
+
28
+ content_model = if @tree.type != :html_element || @tree.options[:content_model] != :raw
29
+ (@options[:parse_block_html] ? HTML_CONTENT_MODEL[el.value] : :raw)
30
+ else
31
+ :raw
32
+ end
33
+ if val = HTML_MARKDOWN_ATTR_MAP[el.attr.delete('markdown')]
34
+ content_model = (val == :default ? HTML_CONTENT_MODEL[el.value] : val)
35
+ end
36
+
37
+ @src.scan(TRAILING_WHITESPACE) if content_model == :block
38
+ el.options[:content_model] = content_model
39
+ el.options[:is_closed] = closed
40
+
41
+ if !closed && handle_body
42
+ if content_model == :block
43
+ if !parse_blocks(el)
44
+ warning("Found no end tag for '#{el.value}' (line #{el.options[:location]}) - auto-closing it")
45
+ end
46
+ elsif content_model == :span
47
+ curpos = @src.pos
48
+ if @src.scan_until(/(?=<\/#{el.value}\s*>)/mi)
49
+ add_text(extract_string(curpos...@src.pos, @src), el)
50
+ @src.scan(HTML_TAG_CLOSE_RE)
51
+ else
52
+ add_text(@src.rest, el)
53
+ @src.terminate
54
+ warning("Found no end tag for '#{el.value}' (line #{el.options[:location]}) - auto-closing it")
55
+ end
56
+ else
57
+ parse_raw_html(el, &method(:handle_kramdown_html_tag))
58
+ end
59
+ @src.scan(TRAILING_WHITESPACE) unless (@tree.type == :html_element && @tree.options[:content_model] == :raw)
60
+ end
61
+ end
62
+
63
+
64
+ HTML_BLOCK_START = /^#{OPT_SPACE}<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/
65
+
66
+ # Parse the HTML at the current position as block-level HTML.
67
+ def parse_block_html
68
+ line = @src.current_line_number
69
+ if result = @src.scan(HTML_COMMENT_RE)
70
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :block, :location => line)
71
+ @src.scan(TRAILING_WHITESPACE)
72
+ true
73
+ elsif result = @src.scan(HTML_INSTRUCTION_RE)
74
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :block, :location => line)
75
+ @src.scan(TRAILING_WHITESPACE)
76
+ true
77
+ else
78
+ if result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_RE}/) && !Kramdown::Parser::Html::HTML_SPAN_ELEMENTS.include?(@src[1].downcase) # RM
79
+ @src.pos += @src.matched_size
80
+ handle_html_start_tag(line, &method(:handle_kramdown_html_tag))
81
+ Kramdown::Parser::Html::ElementConverter.convert(@root, @tree.children.last) if @options[:html_to_native]
82
+ true
83
+ elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !Kramdown::Parser::Html::HTML_SPAN_ELEMENTS.include?(@src[1].downcase) # RM
84
+ name = @src[1].downcase
85
+
86
+ if @tree.type == :html_element && @tree.value == name
87
+ @src.pos += @src.matched_size
88
+ throw :stop_block_parsing, :found
89
+ else
90
+ false
91
+ end
92
+ else
93
+ false
94
+ end
95
+ end
96
+ end
97
+ define_parser(:block_html, HTML_BLOCK_START)
98
+
99
+
100
+ HTML_SPAN_START = /<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/
101
+
102
+ # Parse the HTML at the current position as span-level HTML.
103
+ def parse_span_html
104
+ line = @src.current_line_number
105
+ if result = @src.scan(HTML_COMMENT_RE)
106
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :span, :location => line)
107
+ elsif result = @src.scan(HTML_INSTRUCTION_RE)
108
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :span, :location => line)
109
+ elsif result = @src.scan(HTML_TAG_CLOSE_RE)
110
+ warning("Found invalidly used HTML closing tag for '#{@src[1]}' on line #{line}")
111
+ add_text(result)
112
+ elsif result = @src.scan(HTML_TAG_RE)
113
+ tag_name = @src[1].downcase
114
+ if HTML_BLOCK_ELEMENTS.include?(tag_name)
115
+ warning("Found block HTML tag '#{tag_name}' in span-level text on line #{line}")
116
+ add_text(result)
117
+ return
118
+ end
119
+
120
+ attrs = Utils::OrderedHash.new
121
+ @src[2].scan(HTML_ATTRIBUTE_RE).each {|name,sep,val| attrs[name.downcase] = (val || '').gsub(/\n+/, ' ')}
122
+
123
+ do_parsing = (HTML_CONTENT_MODEL[tag_name] == :raw || @tree.options[:content_model] == :raw ? false : @options[:parse_span_html])
124
+ if val = HTML_MARKDOWN_ATTR_MAP[attrs.delete('markdown')]
125
+ if val == :block
126
+ warning("Cannot use block-level parsing in span-level HTML tag (line #{line}) - using default mode")
127
+ elsif val == :span
128
+ do_parsing = true
129
+ elsif val == :default
130
+ do_parsing = HTML_CONTENT_MODEL[tag_name] != :raw
131
+ elsif val == :raw
132
+ do_parsing = false
133
+ end
134
+ end
135
+
136
+ el = Element.new(:html_element, tag_name, attrs, :category => :span, :location => line,
137
+ :content_model => (do_parsing ? :span : :raw), :is_closed => !!@src[4])
138
+ @tree.children << el
139
+ stop_re = /<\/#{Regexp.escape(tag_name)}\s*>/i
140
+ if !@src[4] && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
141
+ warning("The HTML tag '#{el.value}' on line #{line} cannot have any content - auto-closing it")
142
+ elsif !@src[4]
143
+ if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html]))
144
+ @src.scan(stop_re)
145
+ else
146
+ warning("Found no end tag for '#{el.value}' (line #{line}) - auto-closing it")
147
+ add_text(@src.rest, el)
148
+ @src.terminate
149
+ end
150
+ end
151
+ Kramdown::Parser::Html::ElementConverter.convert(@root, el) if @options[:html_to_native]
152
+ else
153
+ add_text(@src.getch)
154
+ end
155
+ end
156
+ define_parser(:span_html, HTML_SPAN_START, '<')
157
+
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/html'
11
+
12
+ module Kramdown
13
+ module Parser
14
+ class Kramdown
15
+
16
+ # Parse the HTML entity at the current location.
17
+ def parse_html_entity
18
+ start_line_number = @src.current_line_number
19
+ @src.pos += @src.matched_size
20
+ begin
21
+ @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex),
22
+ nil, :original => @src.matched, :location => start_line_number)
23
+ rescue ::Kramdown::Error
24
+ @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('amp'),
25
+ nil, :location => start_line_number)
26
+ add_text(@src.matched[1..-1])
27
+ end
28
+ end
29
+ define_parser(:html_entity, Kramdown::Parser::Html::Constants::HTML_ENTITY_RE, '&')
30
+
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,25 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ module Kramdown
11
+ module Parser
12
+ class Kramdown
13
+
14
+ LINE_BREAK = /( |\\\\)(?=\n)/
15
+
16
+ # Parse the line break at the current location.
17
+ def parse_line_break
18
+ @tree.children << Element.new(:br, nil, nil, :location => @src.current_line_number)
19
+ @src.pos += @src.matched_size
20
+ end
21
+ define_parser(:line_break, LINE_BREAK, '( |\\\\)(?=\n)')
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,139 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/kramdown/escaped_chars'
11
+
12
+ module Kramdown
13
+ module Parser
14
+ class Kramdown
15
+
16
+ # Normalize the link identifier.
17
+ def normalize_link_id(id)
18
+ id.gsub(/[\s]+/, ' ').downcase
19
+ end
20
+
21
+ LINK_DEFINITION_START = /^#{OPT_SPACE}\[([^\n\]]+)\]:[ \t]*(?:<(.*?)>|([^'"\n]*?\S[^'"\n]*?))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
22
+
23
+ # Parse the link definition at the current location.
24
+ def parse_link_definition
25
+ @src.pos += @src.matched_size
26
+ link_id, link_url, link_title = normalize_link_id(@src[1]), @src[2] || @src[3], @src[5]
27
+ warning("Duplicate link ID '#{link_id}' on line #{@src.current_line_number} - overwriting") if @link_defs[link_id]
28
+ @link_defs[link_id] = [link_url, link_title]
29
+ @tree.children << Element.new(:eob, :link_def)
30
+ true
31
+ end
32
+ define_parser(:link_definition, LINK_DEFINITION_START)
33
+
34
+
35
+ # This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
36
+ # and the element itself to the @tree.
37
+ def add_link(el, href, title, alt_text = nil)
38
+ if el.type == :a
39
+ el.attr['href'] = href
40
+ else
41
+ el.attr['src'] = href
42
+ el.attr['alt'] = alt_text
43
+ el.children.clear
44
+ end
45
+ el.attr['title'] = title if title
46
+ @tree.children << el
47
+ end
48
+
49
+ LINK_BRACKET_STOP_RE = /(\])|!?\[/
50
+ LINK_PAREN_STOP_RE = /(\()|(\))|\s(?=['"])/
51
+ LINK_INLINE_ID_RE = /\s*?\[([^\]]+)?\]/
52
+ LINK_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/m
53
+ LINK_START = /!?\[(?=[^^])/
54
+
55
+ # Parse the link at the current scanner position. This method is used to parse normal links as
56
+ # well as image links.
57
+ def parse_link
58
+ start_line_number = @src.current_line_number
59
+ result = @src.scan(LINK_START)
60
+ cur_pos = @src.pos
61
+ saved_pos = @src.save_pos
62
+
63
+ link_type = (result =~ /^!/ ? :img : :a)
64
+
65
+ # no nested links allowed
66
+ if link_type == :a && (@tree.type == :img || @tree.type == :a || @stack.any? {|t,s| t && (t.type == :img || t.type == :a)})
67
+ add_text(result)
68
+ return
69
+ end
70
+ el = Element.new(link_type, nil, nil, :location => start_line_number)
71
+
72
+ count = 1
73
+ found = parse_spans(el, LINK_BRACKET_STOP_RE) do
74
+ count = count + (@src[1] ? -1 : 1)
75
+ count - el.children.select {|c| c.type == :img}.size == 0
76
+ end
77
+ if !found || (link_type == :a && el.children.empty?)
78
+ @src.revert_pos(saved_pos)
79
+ add_text(result)
80
+ return
81
+ end
82
+ alt_text = extract_string(cur_pos...@src.pos, @src).gsub(ESCAPED_CHARS, '\1')
83
+ @src.scan(LINK_BRACKET_STOP_RE)
84
+
85
+ # reference style link or no link url
86
+ if @src.scan(LINK_INLINE_ID_RE) || !@src.check(/\(/)
87
+ link_id = normalize_link_id(@src[1] || alt_text)
88
+ if @link_defs.has_key?(link_id)
89
+ add_link(el, @link_defs[link_id].first, @link_defs[link_id].last, alt_text)
90
+ else
91
+ warning("No link definition for link ID '#{link_id}' found on line #{start_line_number}")
92
+ @src.revert_pos(saved_pos)
93
+ add_text(result)
94
+ end
95
+ return
96
+ end
97
+
98
+ # link url in parentheses
99
+ if @src.scan(/\(<(.*?)>/)
100
+ link_url = @src[1]
101
+ if @src.scan(/\)/)
102
+ add_link(el, link_url, nil, alt_text)
103
+ return
104
+ end
105
+ else
106
+ link_url = ''
107
+ nr_of_brackets = 0
108
+ while temp = @src.scan_until(LINK_PAREN_STOP_RE)
109
+ link_url << temp
110
+ if @src[2]
111
+ nr_of_brackets -= 1
112
+ break if nr_of_brackets == 0
113
+ elsif @src[1]
114
+ nr_of_brackets += 1
115
+ else
116
+ break
117
+ end
118
+ end
119
+ link_url = link_url[1..-2]
120
+ link_url.strip!
121
+
122
+ if nr_of_brackets == 0
123
+ add_link(el, link_url, nil, alt_text)
124
+ return
125
+ end
126
+ end
127
+
128
+ if @src.scan(LINK_INLINE_TITLE_RE)
129
+ add_link(el, link_url, @src[2], alt_text)
130
+ else
131
+ @src.revert_pos(saved_pos)
132
+ add_text(result)
133
+ end
134
+ end
135
+ define_parser(:link, LINK_START, '!?\[')
136
+
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,256 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/kramdown/blank_line'
11
+ # RM require 'kramdown/parser/kramdown/eob'
12
+ # RM require 'kramdown/parser/kramdown/horizontal_rule'
13
+ # RM require 'kramdown/parser/kramdown/extensions'
14
+
15
+ module Kramdown
16
+ module Parser
17
+ class Kramdown
18
+
19
+ LIST_ITEM_IAL = /^\s*(?:\{:(?!(?:#{ALD_ID_NAME})?:|\/)(#{ALD_ANY_CHARS}+)\})\s*/
20
+ LIST_ITEM_IAL_CHECK = /^#{LIST_ITEM_IAL}?\s*\n/
21
+
22
+ # Used for parsing the first line of a list item or a definition, i.e. the line with list item
23
+ # marker or the definition marker.
24
+ def parse_first_list_line(indentation, content)
25
+ if content =~ self.class::LIST_ITEM_IAL_CHECK
26
+ indentation = 4
27
+ else
28
+ while content =~ /^ *\t/
29
+ temp = content.scan(/^ */).first.length + indentation
30
+ # content.sub!(/^( *)(\t+)/) {$1 << " "*(4 - (temp % 4) + ($2.length - 1)*4)}
31
+ content = content.sub(/^( *)(\t+)/) {$1 << " "*(4 - (temp % 4) + ($2.length - 1)*4)}
32
+ end
33
+ indentation += content.scan(/^ */).first.length
34
+ end
35
+ content = content.sub(/^\s*/, '')
36
+
37
+ indent_re = /^ {#{indentation}}/
38
+ content_re = /^(?:(?:\t| {4}){#{indentation / 4}} {#{indentation % 4}}|(?:\t| {4}){#{indentation / 4 + 1}}).*\S.*\n/
39
+ lazy_re = /(?!^ {0,#{[indentation, 3].min}}(?:#{IAL_BLOCK}|#{LAZY_END_HTML_STOP}|#{LAZY_END_HTML_START})).*\S.*\n/
40
+ [content, indentation, content_re, lazy_re, indent_re]
41
+ end
42
+
43
+
44
+ LIST_START_UL = /^(#{OPT_SPACE}[+*-])([\t| ].*?\n)/
45
+ LIST_START_OL = /^(#{OPT_SPACE}\d+\.)([\t| ].*?\n)/
46
+ LIST_START = /#{LIST_START_UL}|#{LIST_START_OL}/
47
+
48
+ # Parse the ordered or unordered list at the current location.
49
+ def parse_list
50
+ start_line_number = @src.current_line_number
51
+ type, list_start_re = (@src.check(LIST_START_UL) ? [:ul, LIST_START_UL] : [:ol, LIST_START_OL])
52
+ list = new_block_el(type, nil, nil, :location => start_line_number)
53
+
54
+ item = nil
55
+ content_re, lazy_re, indent_re = nil
56
+ eob_found = false
57
+ nested_list_found = false
58
+ last_is_blank = false
59
+ while !@src.eos?
60
+ start_line_number = @src.current_line_number
61
+ if last_is_blank && @src.check(HR_START)
62
+ break
63
+ elsif @src.scan(EOB_MARKER)
64
+ eob_found = true
65
+ break
66
+ elsif @src.scan(list_start_re)
67
+ item = Element.new(:li, nil, nil, :location => start_line_number)
68
+ item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
69
+ list.children << item
70
+
71
+ #------------------------------------------------------------------------------
72
+ # RM don't know why, but using the item.options[:ial] directly in the block
73
+ # caused an object to be autoreleased one too many times in RubyMotion,
74
+ # crashing the app. So use this workaround
75
+ item_options = (item.options[:ial] ||= {}) # RM
76
+ item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
77
+ parse_attribute_list($1, item_options) # RM
78
+ ''
79
+ end
80
+ item.options[:ial] = item_options # RM
81
+
82
+ list_start_re = (type == :ul ? /^( {0,#{[3, indentation - 1].min}}[+*-])([\t| ].*?\n)/ :
83
+ /^( {0,#{[3, indentation - 1].min}}\d+\.)([\t| ].*?\n)/)
84
+ nested_list_found = (item.value =~ LIST_START)
85
+ last_is_blank = false
86
+ item.value = [item.value]
87
+ elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
88
+ result.sub!(/^(\t+)/) { " "*($1 ? 4*$1.length : 0) }
89
+ result.sub!(indent_re, '')
90
+ if !nested_list_found && result =~ LIST_START
91
+ item.value << ''
92
+ nested_list_found = true
93
+ end
94
+ item.value.last << result
95
+ last_is_blank = false
96
+ elsif result = @src.scan(BLANK_LINE)
97
+ nested_list_found = true
98
+ last_is_blank = true
99
+ item.value.last << result
100
+ else
101
+ break
102
+ end
103
+ end
104
+
105
+ @tree.children << list
106
+
107
+ last = nil
108
+ list.children.each do |it|
109
+ temp = Element.new(:temp, nil, nil, :location => it.options[:location])
110
+
111
+ env = save_env
112
+ location = it.options[:location]
113
+ it.value.each do |val|
114
+ @src = ::Kramdown::Utils::StringScanner.new(val, location)
115
+ parse_blocks(temp)
116
+ location = @src.current_line_number
117
+ end
118
+ restore_env(env)
119
+
120
+ it.children = temp.children
121
+ it.value = nil
122
+ next if it.children.size == 0
123
+
124
+ # Handle the case where an EOB marker is inserted by a block IAL for the first paragraph
125
+ it.children.delete_at(1) if it.children.first.type == :p &&
126
+ it.children.length >= 2 && it.children[1].type == :eob && it.children.first.options[:ial]
127
+
128
+ if it.children.first.type == :p &&
129
+ (it.children.length < 2 || it.children[1].type != :blank ||
130
+ (it == list.children.last && it.children.length == 2 && !eob_found)) &&
131
+ (list.children.last != it || list.children.size == 1 ||
132
+ list.children[0..-2].any? {|cit| !cit.children.first || cit.children.first.type != :p || cit.children.first.options[:transparent]})
133
+ it.children.first.children.first.value << "\n" if it.children.size > 1 && it.children[1].type != :blank
134
+ it.children.first.options[:transparent] = true
135
+ end
136
+
137
+ if it.children.last.type == :blank
138
+ last = it.children.pop
139
+ else
140
+ last = nil
141
+ end
142
+ end
143
+
144
+ @tree.children << last if !last.nil? && !eob_found
145
+
146
+ true
147
+ end
148
+ define_parser(:list, LIST_START)
149
+
150
+
151
+ DEFINITION_LIST_START = /^(#{OPT_SPACE}:)([\t| ].*?\n)/
152
+
153
+ # Parse the ordered or unordered list at the current location.
154
+ def parse_definition_list
155
+ children = @tree.children
156
+ if !children.last || (children.length == 1 && children.last.type != :p ) ||
157
+ (children.length >= 2 && children[-1].type != :p && (children[-1].type != :blank || children[-1].value != "\n" || children[-2].type != :p))
158
+ return false
159
+ end
160
+
161
+ first_as_para = false
162
+ deflist = new_block_el(:dl)
163
+ para = @tree.children.pop
164
+ if para.type == :blank
165
+ para = @tree.children.pop
166
+ first_as_para = true
167
+ end
168
+ deflist.options[:location] = para.options[:location] # take location from preceding para which is the first definition term
169
+ para.children.first.value.split(/\n/).each do |term|
170
+ el = Element.new(:dt, nil, nil, :location => @src.current_line_number)
171
+ el.children << Element.new(:raw_text, term)
172
+ deflist.children << el
173
+ end
174
+ deflist.options[:ial] = para.options[:ial]
175
+
176
+ item = nil
177
+ content_re, lazy_re, indent_re = nil
178
+ def_start_re = DEFINITION_LIST_START
179
+ last_is_blank = false
180
+ while !@src.eos?
181
+ start_line_number = @src.current_line_number
182
+ if @src.scan(def_start_re)
183
+ item = Element.new(:dd, nil, nil, :location => start_line_number)
184
+ item.options[:first_as_para] = first_as_para
185
+ item.value, indentation, content_re, lazy_re, indent_re = parse_first_list_line(@src[1].length, @src[2])
186
+ deflist.children << item
187
+
188
+ #------------------------------------------------------------------------------
189
+ # RM don't know why, but using the item.options[:ial] directly in the block
190
+ # caused an object to be autoreleased one too many times in RubyMotion,
191
+ # crashing the app. So use this workaround
192
+ item_options = (item.options[:ial] ||= {}) # RM
193
+ item.value.sub!(self.class::LIST_ITEM_IAL) do |match|
194
+ parse_attribute_list($1, item_options) # RM
195
+ ''
196
+ end
197
+ item.options[:ial] = item_options # RM
198
+
199
+ def_start_re = /^( {0,#{[3, indentation - 1].min}}:)([\t| ].*?\n)/
200
+ first_as_para = false
201
+ last_is_blank = false
202
+ elsif @src.check(EOB_MARKER)
203
+ break
204
+ elsif (result = @src.scan(content_re)) || (!last_is_blank && (result = @src.scan(lazy_re)))
205
+ result.sub!(/^(\t+)/) { " "*($1 ? 4*$1.length : 0) }
206
+ result.sub!(indent_re, '')
207
+ item.value << result
208
+ first_as_para = false
209
+ last_is_blank = false
210
+ elsif result = @src.scan(BLANK_LINE)
211
+ first_as_para = true
212
+ item.value << result
213
+ last_is_blank = true
214
+ else
215
+ break
216
+ end
217
+ end
218
+
219
+ last = nil
220
+ deflist.children.each do |it|
221
+ next if it.type == :dt
222
+
223
+ parse_blocks(it, it.value)
224
+ it.value = nil
225
+ next if it.children.size == 0
226
+
227
+ if it.children.last.type == :blank
228
+ last = it.children.pop
229
+ else
230
+ last = nil
231
+ end
232
+
233
+ if it.children.first && it.children.first.type == :p && !it.options.delete(:first_as_para)
234
+ it.children.first.children.first.value << "\n" if it.children.size > 1
235
+ it.children.first.options[:transparent] = true
236
+ end
237
+ end
238
+
239
+ if @tree.children.length >= 1 && @tree.children.last.type == :dl
240
+ @tree.children[-1].children.concat(deflist.children)
241
+ elsif @tree.children.length >= 2 && @tree.children[-1].type == :blank && @tree.children[-2].type == :dl
242
+ @tree.children.pop
243
+ @tree.children[-1].children.concat(deflist.children)
244
+ else
245
+ @tree.children << deflist
246
+ end
247
+
248
+ @tree.children << last if !last.nil?
249
+
250
+ true
251
+ end
252
+ define_parser(:definition_list, DEFINITION_LIST_START)
253
+
254
+ end
255
+ end
256
+ end
@@ -0,0 +1,54 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2014 Thomas Leitner <t_leitner@gmx.at>
5
+ #
6
+ # This file is part of kramdown which is licensed under the MIT.
7
+ #++
8
+ #
9
+
10
+ # RM require 'kramdown/parser/kramdown/block_boundary'
11
+
12
+ module Kramdown
13
+ module Parser
14
+ class Kramdown
15
+
16
+ BLOCK_MATH_START = /^#{OPT_SPACE}(\\)?\$\$(.*?)\$\$(\s*?\n)?/m
17
+
18
+ # Parse the math block at the current location.
19
+ def parse_block_math
20
+ start_line_number = @src.current_line_number
21
+ if !after_block_boundary?
22
+ return false
23
+ elsif @src[1]
24
+ @src.scan(/^#{OPT_SPACE}\\/) if @src[3]
25
+ return false
26
+ end
27
+
28
+ saved_pos = @src.save_pos
29
+ @src.pos += @src.matched_size
30
+ data = @src[2]
31
+ if before_block_boundary?
32
+ @tree.children << new_block_el(:math, data, nil, :category => :block, :location => start_line_number)
33
+ true
34
+ else
35
+ @src.revert_pos(saved_pos)
36
+ false
37
+ end
38
+ end
39
+ define_parser(:block_math, BLOCK_MATH_START)
40
+
41
+
42
+ INLINE_MATH_START = /\$\$(.*?)\$\$/m
43
+
44
+ # Parse the inline math at the current location.
45
+ def parse_inline_math
46
+ start_line_number = @src.current_line_number
47
+ @src.pos += @src.matched_size
48
+ @tree.children << Element.new(:math, @src[1], nil, :category => :span, :location => start_line_number)
49
+ end
50
+ define_parser(:inline_math, INLINE_MATH_START, '\$')
51
+
52
+ end
53
+ end
54
+ end