motion-kramdown 0.5.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.
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