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.
- 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
|