bean-kramdown 0.13.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/AUTHORS +1 -0
  2. data/CONTRIBUTERS +11 -0
  3. data/COPYING +24 -0
  4. data/ChangeLog +6683 -0
  5. data/GPL +674 -0
  6. data/README +43 -0
  7. data/VERSION +1 -0
  8. data/bin/kramdown +78 -0
  9. data/lib/kramdown.rb +23 -0
  10. data/lib/kramdown/compatibility.rb +49 -0
  11. data/lib/kramdown/converter.rb +41 -0
  12. data/lib/kramdown/converter/base.rb +169 -0
  13. data/lib/kramdown/converter/bean_html.rb +71 -0
  14. data/lib/kramdown/converter/html.rb +411 -0
  15. data/lib/kramdown/converter/kramdown.rb +428 -0
  16. data/lib/kramdown/converter/latex.rb +607 -0
  17. data/lib/kramdown/converter/toc.rb +82 -0
  18. data/lib/kramdown/document.rb +119 -0
  19. data/lib/kramdown/element.rb +524 -0
  20. data/lib/kramdown/error.rb +30 -0
  21. data/lib/kramdown/options.rb +373 -0
  22. data/lib/kramdown/parser.rb +39 -0
  23. data/lib/kramdown/parser/base.rb +136 -0
  24. data/lib/kramdown/parser/bean_kramdown.rb +25 -0
  25. data/lib/kramdown/parser/bean_kramdown/info_box.rb +52 -0
  26. data/lib/kramdown/parser/bean_kramdown/oembed.rb +230 -0
  27. data/lib/kramdown/parser/html.rb +570 -0
  28. data/lib/kramdown/parser/kramdown.rb +339 -0
  29. data/lib/kramdown/parser/kramdown/abbreviation.rb +71 -0
  30. data/lib/kramdown/parser/kramdown/autolink.rb +53 -0
  31. data/lib/kramdown/parser/kramdown/blank_line.rb +43 -0
  32. data/lib/kramdown/parser/kramdown/block_boundary.rb +46 -0
  33. data/lib/kramdown/parser/kramdown/blockquote.rb +51 -0
  34. data/lib/kramdown/parser/kramdown/codeblock.rb +63 -0
  35. data/lib/kramdown/parser/kramdown/codespan.rb +56 -0
  36. data/lib/kramdown/parser/kramdown/emphasis.rb +70 -0
  37. data/lib/kramdown/parser/kramdown/eob.rb +39 -0
  38. data/lib/kramdown/parser/kramdown/escaped_chars.rb +38 -0
  39. data/lib/kramdown/parser/kramdown/extensions.rb +204 -0
  40. data/lib/kramdown/parser/kramdown/footnote.rb +74 -0
  41. data/lib/kramdown/parser/kramdown/header.rb +68 -0
  42. data/lib/kramdown/parser/kramdown/horizontal_rule.rb +39 -0
  43. data/lib/kramdown/parser/kramdown/html.rb +169 -0
  44. data/lib/kramdown/parser/kramdown/html_entity.rb +44 -0
  45. data/lib/kramdown/parser/kramdown/image.rb +157 -0
  46. data/lib/kramdown/parser/kramdown/line_break.rb +38 -0
  47. data/lib/kramdown/parser/kramdown/link.rb +154 -0
  48. data/lib/kramdown/parser/kramdown/list.rb +240 -0
  49. data/lib/kramdown/parser/kramdown/math.rb +65 -0
  50. data/lib/kramdown/parser/kramdown/paragraph.rb +63 -0
  51. data/lib/kramdown/parser/kramdown/smart_quotes.rb +214 -0
  52. data/lib/kramdown/parser/kramdown/table.rb +178 -0
  53. data/lib/kramdown/parser/kramdown/typographic_symbol.rb +52 -0
  54. data/lib/kramdown/parser/markdown.rb +69 -0
  55. data/lib/kramdown/utils.rb +42 -0
  56. data/lib/kramdown/utils/entities.rb +348 -0
  57. data/lib/kramdown/utils/html.rb +85 -0
  58. data/lib/kramdown/utils/ordered_hash.rb +100 -0
  59. data/lib/kramdown/version.rb +28 -0
  60. metadata +140 -0
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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/extensions'
24
+ require 'kramdown/parser/kramdown/blank_line'
25
+ require 'kramdown/parser/kramdown/codeblock'
26
+
27
+ module Kramdown
28
+ module Parser
29
+ class Kramdown
30
+
31
+ FOOTNOTE_DEFINITION_START = /^#{OPT_SPACE}\[\^(#{ALD_ID_NAME})\]:\s*?(.*?\n#{CODEBLOCK_MATCH})/
32
+
33
+ # Parse the foot note definition at the current location.
34
+ def parse_footnote_definition
35
+ @src.pos += @src.matched_size
36
+
37
+ el = Element.new(:footnote_def)
38
+ parse_blocks(el, @src[2].gsub(INDENT, ''))
39
+ warning("Duplicate footnote name '#{@src[1]}' - overwriting") if @footnotes[@src[1]]
40
+ (@footnotes[@src[1]] = {})[:content] = el
41
+ @tree.children << Element.new(:eob, :footnote_def)
42
+ true
43
+ end
44
+ define_parser(:footnote_definition, FOOTNOTE_DEFINITION_START)
45
+
46
+
47
+ FOOTNOTE_MARKER_START = /\[\^(#{ALD_ID_NAME})\]/
48
+
49
+ # Parse the footnote marker at the current location.
50
+ def parse_footnote_marker
51
+ @src.pos += @src.matched_size
52
+ fn_def = @footnotes[@src[1]]
53
+ if fn_def
54
+ valid = fn_def[:marker] && fn_def[:stack][0..-2].zip(fn_def[:stack][1..-1]).all? do |par, child|
55
+ par.children.include?(child)
56
+ end
57
+ if !fn_def[:marker] || !valid
58
+ fn_def[:marker] = Element.new(:footnote, fn_def[:content], nil, :name => @src[1])
59
+ fn_def[:stack] = [@stack.map {|s| s.first}, @tree, fn_def[:marker]].flatten.compact
60
+ @tree.children << fn_def[:marker]
61
+ else
62
+ warning("Footnote marker '#{@src[1]}' already appeared in document, ignoring newly found marker")
63
+ add_text(@src.matched)
64
+ end
65
+ else
66
+ warning("Footnote definition for '#{@src[1]}' not found")
67
+ add_text(@src.matched)
68
+ end
69
+ end
70
+ define_parser(:footnote_marker, FOOTNOTE_MARKER_START, '\[')
71
+
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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/block_boundary'
24
+
25
+ module Kramdown
26
+ module Parser
27
+ class Kramdown
28
+
29
+ HEADER_ID=/(?:[ \t]\{#(\w[\w-]*)\})?/
30
+ SETEXT_HEADER_START = /^(#{OPT_SPACE}[^ \t].*?)#{HEADER_ID}[ \t]*?\n(-|=)+\s*?\n/
31
+
32
+ # Parse the Setext header at the current location.
33
+ def parse_setext_header
34
+ return false if !after_block_boundary?
35
+
36
+ @src.pos += @src.matched_size
37
+ text, id, level = @src[1], @src[2], @src[3]
38
+ text.strip!
39
+ el = new_block_el(:header, nil, nil, :level => (level == '-' ? 2 : 1), :raw_text => text)
40
+ add_text(text, el)
41
+ el.attr['id'] = id if id
42
+ @tree.children << el
43
+ true
44
+ end
45
+ define_parser(:setext_header, SETEXT_HEADER_START)
46
+
47
+
48
+ ATX_HEADER_START = /^\#{1,6}/
49
+ ATX_HEADER_MATCH = /^(\#{1,6})(.+?)\s*?#*#{HEADER_ID}\s*?\n/
50
+
51
+ # Parse the Atx header at the current location.
52
+ def parse_atx_header
53
+ return false if !after_block_boundary?
54
+
55
+ @src.scan(ATX_HEADER_MATCH)
56
+ level, text, id = @src[1], @src[2], @src[3]
57
+ text.strip!
58
+ el = new_block_el(:header, nil, nil, :level => level.length, :raw_text => text)
59
+ add_text(text, el)
60
+ el.attr['id'] = id if id
61
+ @tree.children << el
62
+ true
63
+ end
64
+ define_parser(:atx_header, ATX_HEADER_START)
65
+
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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
+ module Kramdown
24
+ module Parser
25
+ class Kramdown
26
+
27
+ HR_START = /^#{OPT_SPACE}(\*|-|_)[ \t]*\1[ \t]*\1(\1|[ \t])*\n/
28
+
29
+ # Parse the horizontal rule at the current location.
30
+ def parse_horizontal_rule
31
+ @src.pos += @src.matched_size
32
+ @tree.children << new_block_el(:hr)
33
+ true
34
+ end
35
+ define_parser(:horizontal_rule, HR_START)
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,169 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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/html'
24
+
25
+ module Kramdown
26
+ module Parser
27
+ class Kramdown
28
+
29
+ include Kramdown::Parser::Html::Parser
30
+
31
+ # Mapping of markdown attribute value to content model. I.e. :raw when "0", :default when "1"
32
+ # (use default content model for the HTML element), :span when "span", :block when block and
33
+ # for everything else +nil+ is returned.
34
+ HTML_MARKDOWN_ATTR_MAP = {"0" => :raw, "1" => :default, "span" => :span, "block" => :block}
35
+
36
+ TRAILING_WHITESPACE = /[ \t]*\n/
37
+
38
+ def handle_kramdown_html_tag(el, closed)
39
+ el.options[:ial] = @block_ial if @block_ial
40
+
41
+ content_model = if @tree.type != :html_element || @tree.options[:content_model] != :raw
42
+ (@options[:parse_block_html] ? HTML_CONTENT_MODEL[el.value] : :raw)
43
+ else
44
+ :raw
45
+ end
46
+ if val = HTML_MARKDOWN_ATTR_MAP[el.attr.delete('markdown')]
47
+ content_model = (val == :default ? HTML_CONTENT_MODEL[el.value] : val)
48
+ end
49
+
50
+ @src.scan(TRAILING_WHITESPACE) if content_model == :block
51
+ el.options[:content_model] = content_model
52
+
53
+ if !closed
54
+ if content_model == :block
55
+ if !parse_blocks(el)
56
+ warning("Found no end tag for '#{el.value}' - auto-closing it")
57
+ end
58
+ elsif content_model == :span
59
+ curpos = @src.pos
60
+ if @src.scan_until(/(?=<\/#{el.value}\s*>)/mi)
61
+ add_text(extract_string(curpos...@src.pos, @src), el)
62
+ @src.scan(HTML_TAG_CLOSE_RE)
63
+ else
64
+ add_text(@src.rest, el)
65
+ @src.terminate
66
+ warning("Found no end tag for '#{el.value}' - auto-closing it")
67
+ end
68
+ else
69
+ parse_raw_html(el, &method(:handle_kramdown_html_tag))
70
+ end
71
+ @src.scan(TRAILING_WHITESPACE) unless (@tree.type == :html_element && @tree.options[:content_model] == :raw)
72
+ end
73
+ end
74
+
75
+
76
+ HTML_BLOCK_START = /^#{OPT_SPACE}<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/
77
+
78
+ # Parse the HTML at the current position as block-level HTML.
79
+ def parse_block_html
80
+ if result = @src.scan(HTML_COMMENT_RE)
81
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :block)
82
+ @src.scan(TRAILING_WHITESPACE)
83
+ true
84
+ elsif result = @src.scan(HTML_INSTRUCTION_RE)
85
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :block)
86
+ @src.scan(TRAILING_WHITESPACE)
87
+ true
88
+ else
89
+ if result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1].downcase)
90
+ @src.pos += @src.matched_size
91
+ handle_html_start_tag(&method(:handle_kramdown_html_tag))
92
+ Kramdown::Parser::Html::ElementConverter.convert(@root, @tree.children.last) if @options[:html_to_native]
93
+ true
94
+ elsif result = @src.check(/^#{OPT_SPACE}#{HTML_TAG_CLOSE_RE}/) && !HTML_SPAN_ELEMENTS.include?(@src[1].downcase)
95
+ name = @src[1].downcase
96
+
97
+ if @tree.type == :html_element && @tree.value == name
98
+ @src.pos += @src.matched_size
99
+ throw :stop_block_parsing, :found
100
+ else
101
+ false
102
+ end
103
+ else
104
+ false
105
+ end
106
+ end
107
+ end
108
+ define_parser(:block_html, HTML_BLOCK_START)
109
+
110
+
111
+ HTML_SPAN_START = /<(#{REXML::Parsers::BaseParser::UNAME_STR}|\?|!--|\/)/
112
+
113
+ # Parse the HTML at the current position as span-level HTML.
114
+ def parse_span_html
115
+ if result = @src.scan(HTML_COMMENT_RE)
116
+ @tree.children << Element.new(:xml_comment, result, nil, :category => :span)
117
+ elsif result = @src.scan(HTML_INSTRUCTION_RE)
118
+ @tree.children << Element.new(:xml_pi, result, nil, :category => :span)
119
+ elsif result = @src.scan(HTML_TAG_CLOSE_RE)
120
+ warning("Found invalidly used HTML closing tag for '#{@src[1]}'")
121
+ add_text(result)
122
+ elsif result = @src.scan(HTML_TAG_RE)
123
+ tag_name = @src[1].downcase
124
+ if HTML_BLOCK_ELEMENTS.include?(tag_name)
125
+ warning("Found block HTML tag '#{tag_name}' in span-level text")
126
+ add_text(result)
127
+ return
128
+ end
129
+
130
+ attrs = Utils::OrderedHash.new
131
+ @src[2].scan(HTML_ATTRIBUTE_RE).each {|name,sep,val| attrs[name.downcase] = (val || '').gsub(/\n+/, ' ')}
132
+
133
+ do_parsing = (HTML_CONTENT_MODEL[tag_name] == :raw || @tree.options[:content_model] == :raw ? false : @options[:parse_span_html])
134
+ if val = HTML_MARKDOWN_ATTR_MAP[attrs.delete('markdown')]
135
+ if val == :block
136
+ warning("Cannot use block-level parsing in span-level HTML tag - using default mode")
137
+ elsif val == :span
138
+ do_parsing = true
139
+ elsif val == :default
140
+ do_parsing = HTML_CONTENT_MODEL[tag_name] != :raw
141
+ elsif val == :raw
142
+ do_parsing = false
143
+ end
144
+ end
145
+
146
+ el = Element.new(:html_element, tag_name, attrs, :category => :span, :content_model => (do_parsing ? :span : :raw))
147
+ @tree.children << el
148
+ stop_re = /<\/#{Regexp.escape(tag_name)}\s*>/i
149
+ if !@src[4] && HTML_ELEMENTS_WITHOUT_BODY.include?(el.value)
150
+ warning("The HTML tag '#{el.value}' cannot have any content - auto-closing it")
151
+ elsif !@src[4]
152
+ if parse_spans(el, stop_re, (do_parsing ? nil : [:span_html]))
153
+ @src.scan(stop_re)
154
+ else
155
+ warning("Found no end tag for '#{el.value}' - auto-closing it")
156
+ add_text(@src.rest, el)
157
+ @src.terminate
158
+ end
159
+ end
160
+ Kramdown::Parser::Html::ElementConverter.convert(@root, el) if @options[:html_to_native]
161
+ else
162
+ add_text(@src.getch)
163
+ end
164
+ end
165
+ define_parser(:span_html, HTML_SPAN_START, '<')
166
+
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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/html'
24
+
25
+ module Kramdown
26
+ module Parser
27
+ class Kramdown
28
+
29
+ # Parse the HTML entity at the current location.
30
+ def parse_html_entity
31
+ @src.pos += @src.matched_size
32
+ begin
33
+ @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity(@src[1] || (@src[2] && @src[2].to_i) || @src[3].hex),
34
+ nil, :original => @src.matched)
35
+ rescue ::Kramdown::Error
36
+ @tree.children << Element.new(:entity, ::Kramdown::Utils::Entities.entity('amp'))
37
+ add_text(@src.matched[1..-1])
38
+ end
39
+ end
40
+ define_parser(:html_entity, Kramdown::Parser::Html::Constants::HTML_ENTITY_RE, '&')
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,157 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ #--
4
+ # Copyright (C) 2009-2012 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
+ #
24
+ # This file has been edited to suit the needs of The Beans Group Ltd. Changes were made to the types of media availbable
25
+ # images keep their ! however new types are ? for oembedd etc
26
+ # If you wish to change the types of media you need to change the IMAGE_START constant to include your special symbol
27
+ # for the new media object as well as change the reg ex on the parser definition towards the bottom of this file.
28
+ #
29
+
30
+ module Kramdown
31
+ module Parser
32
+ class Kramdown
33
+
34
+ # Normalize the link identifier.
35
+ def normalize_link_id(id)
36
+ id.gsub(/(\s|\n)+/, ' ').downcase
37
+ end
38
+
39
+ IMAGE_DEFINITION_START = /^#{OPT_SPACE}\[([^\n\]]+)\]:[ \t]*(?:<(.*?)>|([^'"\n]*?\S[^'"\n]*?))[ \t]*?(?:\n?[ \t]*?(["'])(.+?)\4[ \t]*?)?\n/
40
+
41
+ # Parse the link definition at the current location.
42
+ def parse_image_definition
43
+ @src.pos += @src.matched_size
44
+ link_id, link_url, link_title = normalize_link_id(@src[1]), @src[2] || @src[3], @src[5]
45
+ warning("Duplicate link ID '#{link_id}' - overwriting") if @link_defs[link_id]
46
+ @link_defs[link_id] = [link_url, link_title]
47
+ @tree.children << Element.new(:eob, :link_def)
48
+ true
49
+ end
50
+ define_parser(:image_definition, IMAGE_DEFINITION_START)
51
+
52
+
53
+ # This helper methods adds the approriate attributes to the element +el+ of type +a+ or +img+
54
+ # and the element itself to the @tree.
55
+ def add_link(el, href, title, alt_text = nil)
56
+ # figure
57
+ fig = Element.new :figure
58
+ fig.attr['role'] = "img"
59
+ el.attr['src'] = href
60
+ el.attr['alt'] = alt_text
61
+ el.children.clear
62
+ fig.children << el
63
+ if title
64
+ # unique figure id
65
+ fig_id = rand(1000)
66
+ fig.attr['aria-labelledby'] = fig_id
67
+ el.attr['title'] = title
68
+ fig_cap = Element.new(:figCaption, title)
69
+ fig_cap.attr['id'] = fig_id
70
+ fig.children << fig_cap
71
+ end
72
+ @tree.children << fig
73
+ end
74
+
75
+ IMAGE_BRACKET_STOP_RE = /(\])|!?\[/
76
+ IMAGE_PAREN_STOP_RE = /(\()|(\))|\s(?=['"])/
77
+ IMAGE_INLINE_ID_RE = /\s*?\[([^\]]+)?\]/
78
+ IMAGE_INLINE_TITLE_RE = /\s*?(["'])(.+?)\1\s*?\)/
79
+ IMAGE_START = /!\[(?=[^^])/
80
+
81
+ # Parse the link at the current scanner position. This method is used to parse normal links as
82
+ # well as image links.
83
+ def parse_image
84
+ result = @src.scan(IMAGE_START)
85
+ reset_pos = @src.pos
86
+ link_type = :img
87
+
88
+ el = Element.new(link_type)
89
+
90
+ count = 1
91
+ found = parse_spans(el, IMAGE_BRACKET_STOP_RE) do
92
+ count = count + (@src[1] ? -1 : 1)
93
+ count - el.children.select {|c| c.type == :img}.size == 0
94
+ end
95
+ if !found
96
+ @src.pos = reset_pos
97
+ add_text(result)
98
+ return
99
+ end
100
+ alt_text = extract_string(reset_pos...@src.pos, @src)
101
+ @src.scan(IMAGE_BRACKET_STOP_RE)
102
+
103
+ # reference style link or no link url
104
+ if @src.scan(IMAGE_INLINE_ID_RE) || !@src.check(/\(/)
105
+ link_id = normalize_link_id(@src[1] || alt_text)
106
+ if @link_defs.has_key?(link_id)
107
+ add_link(el, @link_defs[link_id].first, @link_defs[link_id].last, alt_text)
108
+ else
109
+ warning("No link definition for link ID '#{link_id}' found")
110
+ @src.pos = reset_pos
111
+ add_text(result)
112
+ end
113
+ return
114
+ end
115
+
116
+ # link url in parentheses
117
+ if @src.scan(/\(<(.*?)>/)
118
+ link_url = @src[1]
119
+ if @src.scan(/\)/)
120
+ add_link(el, link_url, nil, alt_text)
121
+ return
122
+ end
123
+ else
124
+ link_url = ''
125
+ nr_of_brackets = 0
126
+ while temp = @src.scan_until(IMAGE_PAREN_STOP_RE)
127
+ link_url << temp
128
+ if @src[2]
129
+ nr_of_brackets -= 1
130
+ break if nr_of_brackets == 0
131
+ elsif @src[1]
132
+ nr_of_brackets += 1
133
+ else
134
+ break
135
+ end
136
+ end
137
+ link_url = link_url[1..-2]
138
+ link_url.strip!
139
+
140
+ if nr_of_brackets == 0
141
+ add_link(el, link_url, nil, alt_text)
142
+ return
143
+ end
144
+ end
145
+
146
+ if @src.scan(IMAGE_INLINE_TITLE_RE)
147
+ add_link(el, link_url, @src[2], alt_text)
148
+ else
149
+ @src.pos = reset_pos
150
+ add_text(result)
151
+ end
152
+ end
153
+ define_parser(:image, IMAGE_START, '!\[')
154
+
155
+ end
156
+ end
157
+ end