motion-kramdown 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +84 -0
- data/lib/kramdown/compatibility.rb +36 -0
- data/lib/kramdown/converter/base.rb +259 -0
- data/lib/kramdown/converter/html.rb +461 -0
- data/lib/kramdown/converter/kramdown.rb +423 -0
- data/lib/kramdown/converter/latex.rb +600 -0
- data/lib/kramdown/converter/math_engine/itex2mml.rb +39 -0
- data/lib/kramdown/converter/math_engine/mathjax.rb +33 -0
- data/lib/kramdown/converter/math_engine/ritex.rb +38 -0
- data/lib/kramdown/converter/pdf.rb +624 -0
- data/lib/kramdown/converter/remove_html_tags.rb +53 -0
- data/lib/kramdown/converter/syntax_highlighter/coderay.rb +78 -0
- data/lib/kramdown/converter/syntax_highlighter/rouge.rb +37 -0
- data/lib/kramdown/converter/toc.rb +69 -0
- data/lib/kramdown/converter.rb +69 -0
- data/lib/kramdown/document.rb +144 -0
- data/lib/kramdown/element.rb +515 -0
- data/lib/kramdown/error.rb +17 -0
- data/lib/kramdown/options.rb +584 -0
- data/lib/kramdown/parser/base.rb +130 -0
- data/lib/kramdown/parser/gfm.rb +55 -0
- data/lib/kramdown/parser/html.rb +575 -0
- data/lib/kramdown/parser/kramdown/abbreviation.rb +67 -0
- data/lib/kramdown/parser/kramdown/autolink.rb +37 -0
- data/lib/kramdown/parser/kramdown/blank_line.rb +30 -0
- data/lib/kramdown/parser/kramdown/block_boundary.rb +33 -0
- data/lib/kramdown/parser/kramdown/blockquote.rb +39 -0
- data/lib/kramdown/parser/kramdown/codeblock.rb +56 -0
- data/lib/kramdown/parser/kramdown/codespan.rb +44 -0
- data/lib/kramdown/parser/kramdown/emphasis.rb +61 -0
- data/lib/kramdown/parser/kramdown/eob.rb +26 -0
- data/lib/kramdown/parser/kramdown/escaped_chars.rb +25 -0
- data/lib/kramdown/parser/kramdown/extensions.rb +201 -0
- data/lib/kramdown/parser/kramdown/footnote.rb +56 -0
- data/lib/kramdown/parser/kramdown/header.rb +59 -0
- data/lib/kramdown/parser/kramdown/horizontal_rule.rb +27 -0
- data/lib/kramdown/parser/kramdown/html.rb +160 -0
- data/lib/kramdown/parser/kramdown/html_entity.rb +33 -0
- data/lib/kramdown/parser/kramdown/line_break.rb +25 -0
- data/lib/kramdown/parser/kramdown/link.rb +139 -0
- data/lib/kramdown/parser/kramdown/list.rb +256 -0
- data/lib/kramdown/parser/kramdown/math.rb +54 -0
- data/lib/kramdown/parser/kramdown/paragraph.rb +54 -0
- data/lib/kramdown/parser/kramdown/smart_quotes.rb +174 -0
- data/lib/kramdown/parser/kramdown/table.rb +171 -0
- data/lib/kramdown/parser/kramdown/typographic_symbol.rb +44 -0
- data/lib/kramdown/parser/kramdown.rb +359 -0
- data/lib/kramdown/parser/markdown.rb +56 -0
- data/lib/kramdown/parser.rb +27 -0
- data/lib/kramdown/utils/configurable.rb +44 -0
- data/lib/kramdown/utils/entities.rb +347 -0
- data/lib/kramdown/utils/html.rb +75 -0
- data/lib/kramdown/utils/ordered_hash.rb +87 -0
- data/lib/kramdown/utils/string_scanner.rb +74 -0
- data/lib/kramdown/utils/unidecoder.rb +51 -0
- data/lib/kramdown/utils.rb +58 -0
- data/lib/kramdown/version.rb +15 -0
- data/lib/kramdown.rb +10 -0
- data/lib/motion-kramdown.rb +47 -0
- data/lib/rubymotion/encodings.rb +37 -0
- data/lib/rubymotion/rexml_shim.rb +25 -0
- data/lib/rubymotion/set.rb +1349 -0
- data/lib/rubymotion/version.rb +6 -0
- data/spec/document_tree.rb +48 -0
- data/spec/gfm_to_html.rb +95 -0
- data/spec/helpers/it_behaves_like.rb +27 -0
- data/spec/helpers/option_file.rb +46 -0
- data/spec/helpers/spec_options.rb +37 -0
- data/spec/helpers/tidy.rb +12 -0
- data/spec/html_to_html.rb +40 -0
- data/spec/html_to_kramdown_to_html.rb +46 -0
- data/spec/kramdown_to_xxx.rb +40 -0
- data/spec/test_location.rb +203 -0
- data/spec/test_string_scanner_kramdown.rb +19 -0
- data/spec/text_to_kramdown_to_html.rb +52 -0
- data/spec/text_to_latex.rb +33 -0
- 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
|