bean-kramdown 0.13.5
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.
- data/AUTHORS +1 -0
- data/CONTRIBUTERS +11 -0
- data/COPYING +24 -0
- data/ChangeLog +6683 -0
- data/GPL +674 -0
- data/README +43 -0
- data/VERSION +1 -0
- data/bin/kramdown +78 -0
- data/lib/kramdown.rb +23 -0
- data/lib/kramdown/compatibility.rb +49 -0
- data/lib/kramdown/converter.rb +41 -0
- data/lib/kramdown/converter/base.rb +169 -0
- data/lib/kramdown/converter/bean_html.rb +71 -0
- data/lib/kramdown/converter/html.rb +411 -0
- data/lib/kramdown/converter/kramdown.rb +428 -0
- data/lib/kramdown/converter/latex.rb +607 -0
- data/lib/kramdown/converter/toc.rb +82 -0
- data/lib/kramdown/document.rb +119 -0
- data/lib/kramdown/element.rb +524 -0
- data/lib/kramdown/error.rb +30 -0
- data/lib/kramdown/options.rb +373 -0
- data/lib/kramdown/parser.rb +39 -0
- data/lib/kramdown/parser/base.rb +136 -0
- data/lib/kramdown/parser/bean_kramdown.rb +25 -0
- data/lib/kramdown/parser/bean_kramdown/info_box.rb +52 -0
- data/lib/kramdown/parser/bean_kramdown/oembed.rb +230 -0
- data/lib/kramdown/parser/html.rb +570 -0
- data/lib/kramdown/parser/kramdown.rb +339 -0
- data/lib/kramdown/parser/kramdown/abbreviation.rb +71 -0
- data/lib/kramdown/parser/kramdown/autolink.rb +53 -0
- data/lib/kramdown/parser/kramdown/blank_line.rb +43 -0
- data/lib/kramdown/parser/kramdown/block_boundary.rb +46 -0
- data/lib/kramdown/parser/kramdown/blockquote.rb +51 -0
- data/lib/kramdown/parser/kramdown/codeblock.rb +63 -0
- data/lib/kramdown/parser/kramdown/codespan.rb +56 -0
- data/lib/kramdown/parser/kramdown/emphasis.rb +70 -0
- data/lib/kramdown/parser/kramdown/eob.rb +39 -0
- data/lib/kramdown/parser/kramdown/escaped_chars.rb +38 -0
- data/lib/kramdown/parser/kramdown/extensions.rb +204 -0
- data/lib/kramdown/parser/kramdown/footnote.rb +74 -0
- data/lib/kramdown/parser/kramdown/header.rb +68 -0
- data/lib/kramdown/parser/kramdown/horizontal_rule.rb +39 -0
- data/lib/kramdown/parser/kramdown/html.rb +169 -0
- data/lib/kramdown/parser/kramdown/html_entity.rb +44 -0
- data/lib/kramdown/parser/kramdown/image.rb +157 -0
- data/lib/kramdown/parser/kramdown/line_break.rb +38 -0
- data/lib/kramdown/parser/kramdown/link.rb +154 -0
- data/lib/kramdown/parser/kramdown/list.rb +240 -0
- data/lib/kramdown/parser/kramdown/math.rb +65 -0
- data/lib/kramdown/parser/kramdown/paragraph.rb +63 -0
- data/lib/kramdown/parser/kramdown/smart_quotes.rb +214 -0
- data/lib/kramdown/parser/kramdown/table.rb +178 -0
- data/lib/kramdown/parser/kramdown/typographic_symbol.rb +52 -0
- data/lib/kramdown/parser/markdown.rb +69 -0
- data/lib/kramdown/utils.rb +42 -0
- data/lib/kramdown/utils/entities.rb +348 -0
- data/lib/kramdown/utils/html.rb +85 -0
- data/lib/kramdown/utils/ordered_hash.rb +100 -0
- data/lib/kramdown/version.rb +28 -0
- metadata +140 -0
@@ -0,0 +1,63 @@
|
|
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/blank_line'
|
24
|
+
require 'kramdown/parser/kramdown/extensions'
|
25
|
+
require 'kramdown/parser/kramdown/eob'
|
26
|
+
require 'kramdown/parser/kramdown/list'
|
27
|
+
require 'kramdown/parser/kramdown/html'
|
28
|
+
|
29
|
+
module Kramdown
|
30
|
+
module Parser
|
31
|
+
class Kramdown
|
32
|
+
|
33
|
+
LAZY_END_HTML_SPAN_ELEMENTS = HTML_SPAN_ELEMENTS + %w{script}
|
34
|
+
LAZY_END_HTML_START = /<(?>(?!(?:#{LAZY_END_HTML_SPAN_ELEMENTS.join('|')})\b)#{REXML::Parsers::BaseParser::UNAME_STR})\s*(?>\s+#{REXML::Parsers::BaseParser::UNAME_STR}\s*=\s*(["']).*?\1)*\s*\/?>/m
|
35
|
+
LAZY_END_HTML_STOP = /<\/(?!(?:#{LAZY_END_HTML_SPAN_ELEMENTS.join('|')})\b)#{REXML::Parsers::BaseParser::UNAME_STR}\s*>/m
|
36
|
+
|
37
|
+
LAZY_END = /#{BLANK_LINE}|#{IAL_BLOCK_START}|#{EOB_MARKER}|^#{OPT_SPACE}#{LAZY_END_HTML_STOP}|^#{OPT_SPACE}#{LAZY_END_HTML_START}|\Z/
|
38
|
+
|
39
|
+
PARAGRAPH_START = /^#{OPT_SPACE}[^ \t].*?\n/
|
40
|
+
PARAGRAPH_MATCH = /^.*?\n/
|
41
|
+
PARAGRAPH_END = /#{LAZY_END}|#{DEFINITION_LIST_START}/
|
42
|
+
|
43
|
+
# Parse the paragraph at the current location.
|
44
|
+
def parse_paragraph
|
45
|
+
result = @src.scan(PARAGRAPH_MATCH)
|
46
|
+
while !@src.match?(self.class::PARAGRAPH_END)
|
47
|
+
result << @src.scan(PARAGRAPH_MATCH)
|
48
|
+
end
|
49
|
+
result.chomp!
|
50
|
+
if @tree.children.last && @tree.children.last.type == :p
|
51
|
+
@tree.children.last.children.first.value << "\n" << result
|
52
|
+
else
|
53
|
+
@tree.children << new_block_el(:p)
|
54
|
+
result.lstrip!
|
55
|
+
@tree.children.last.children << Element.new(@text_type, result)
|
56
|
+
end
|
57
|
+
true
|
58
|
+
end
|
59
|
+
define_parser(:paragraph, PARAGRAPH_START)
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,214 @@
|
|
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
|
+
# Parts of this file are based on code from Maruku by Andrea Censi.
|
24
|
+
# The needed license statements follow:
|
25
|
+
#
|
26
|
+
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
27
|
+
#
|
28
|
+
# Maruku is free software; you can redistribute it and/or modify
|
29
|
+
# it under the terms of the GNU General Public License as published by
|
30
|
+
# the Free Software Foundation; either version 2 of the License, or
|
31
|
+
# (at your option) any later version.
|
32
|
+
#
|
33
|
+
# Maruku is distributed in the hope that it will be useful,
|
34
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
35
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
36
|
+
# GNU General Public License for more details.
|
37
|
+
#
|
38
|
+
# You should have received a copy of the GNU General Public License
|
39
|
+
# along with Maruku; if not, write to the Free Software
|
40
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
41
|
+
#
|
42
|
+
# NOTA BENE:
|
43
|
+
#
|
44
|
+
# The following algorithm is a rip-off of RubyPants written by
|
45
|
+
# Christian Neukirchen.
|
46
|
+
#
|
47
|
+
# RubyPants is a Ruby port of SmartyPants written by John Gruber.
|
48
|
+
#
|
49
|
+
# This file is distributed under the GPL, which I guess is compatible
|
50
|
+
# with the terms of the RubyPants license.
|
51
|
+
#
|
52
|
+
# -- Andrea Censi
|
53
|
+
#
|
54
|
+
# = RubyPants -- SmartyPants ported to Ruby
|
55
|
+
#
|
56
|
+
# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
|
57
|
+
# Copyright (C) 2004 Christian Neukirchen
|
58
|
+
#
|
59
|
+
# Incooporates ideas, comments and documentation by Chad Miller
|
60
|
+
# Copyright (C) 2004 Chad Miller
|
61
|
+
#
|
62
|
+
# Original SmartyPants by John Gruber
|
63
|
+
# Copyright (C) 2003 John Gruber
|
64
|
+
#
|
65
|
+
#
|
66
|
+
# = RubyPants -- SmartyPants ported to Ruby
|
67
|
+
#
|
68
|
+
#
|
69
|
+
# [snip]
|
70
|
+
#
|
71
|
+
# == Authors
|
72
|
+
#
|
73
|
+
# John Gruber did all of the hard work of writing this software in
|
74
|
+
# Perl for Movable Type and almost all of this useful documentation.
|
75
|
+
# Chad Miller ported it to Python to use with Pyblosxom.
|
76
|
+
#
|
77
|
+
# Christian Neukirchen provided the Ruby port, as a general-purpose
|
78
|
+
# library that follows the *Cloth API.
|
79
|
+
#
|
80
|
+
#
|
81
|
+
# == Copyright and License
|
82
|
+
#
|
83
|
+
# === SmartyPants license:
|
84
|
+
#
|
85
|
+
# Copyright (c) 2003 John Gruber
|
86
|
+
# (http://daringfireball.net)
|
87
|
+
# All rights reserved.
|
88
|
+
#
|
89
|
+
# Redistribution and use in source and binary forms, with or without
|
90
|
+
# modification, are permitted provided that the following conditions
|
91
|
+
# are met:
|
92
|
+
#
|
93
|
+
# * Redistributions of source code must retain the above copyright
|
94
|
+
# notice, this list of conditions and the following disclaimer.
|
95
|
+
#
|
96
|
+
# * Redistributions in binary form must reproduce the above copyright
|
97
|
+
# notice, this list of conditions and the following disclaimer in
|
98
|
+
# the documentation and/or other materials provided with the
|
99
|
+
# distribution.
|
100
|
+
#
|
101
|
+
# * Neither the name "SmartyPants" nor the names of its contributors
|
102
|
+
# may be used to endorse or promote products derived from this
|
103
|
+
# software without specific prior written permission.
|
104
|
+
#
|
105
|
+
# This software is provided by the copyright holders and contributors
|
106
|
+
# "as is" and any express or implied warranties, including, but not
|
107
|
+
# limited to, the implied warranties of merchantability and fitness
|
108
|
+
# for a particular purpose are disclaimed. In no event shall the
|
109
|
+
# copyright owner or contributors be liable for any direct, indirect,
|
110
|
+
# incidental, special, exemplary, or consequential damages (including,
|
111
|
+
# but not limited to, procurement of substitute goods or services;
|
112
|
+
# loss of use, data, or profits; or business interruption) however
|
113
|
+
# caused and on any theory of liability, whether in contract, strict
|
114
|
+
# liability, or tort (including negligence or otherwise) arising in
|
115
|
+
# any way out of the use of this software, even if advised of the
|
116
|
+
# possibility of such damage.
|
117
|
+
#
|
118
|
+
# === RubyPants license
|
119
|
+
#
|
120
|
+
# RubyPants is a derivative work of SmartyPants and smartypants.py.
|
121
|
+
#
|
122
|
+
# Redistribution and use in source and binary forms, with or without
|
123
|
+
# modification, are permitted provided that the following conditions
|
124
|
+
# are met:
|
125
|
+
#
|
126
|
+
# * Redistributions of source code must retain the above copyright
|
127
|
+
# notice, this list of conditions and the following disclaimer.
|
128
|
+
#
|
129
|
+
# * Redistributions in binary form must reproduce the above copyright
|
130
|
+
# notice, this list of conditions and the following disclaimer in
|
131
|
+
# the documentation and/or other materials provided with the
|
132
|
+
# distribution.
|
133
|
+
#
|
134
|
+
# This software is provided by the copyright holders and contributors
|
135
|
+
# "as is" and any express or implied warranties, including, but not
|
136
|
+
# limited to, the implied warranties of merchantability and fitness
|
137
|
+
# for a particular purpose are disclaimed. In no event shall the
|
138
|
+
# copyright owner or contributors be liable for any direct, indirect,
|
139
|
+
# incidental, special, exemplary, or consequential damages (including,
|
140
|
+
# but not limited to, procurement of substitute goods or services;
|
141
|
+
# loss of use, data, or profits; or business interruption) however
|
142
|
+
# caused and on any theory of liability, whether in contract, strict
|
143
|
+
# liability, or tort (including negligence or otherwise) arising in
|
144
|
+
# any way out of the use of this software, even if advised of the
|
145
|
+
# possibility of such damage.
|
146
|
+
#
|
147
|
+
# == Links
|
148
|
+
#
|
149
|
+
# John Gruber:: http://daringfireball.net
|
150
|
+
# SmartyPants:: http://daringfireball.net/projects/smartypants
|
151
|
+
#
|
152
|
+
# Chad Miller:: http://web.chad.org
|
153
|
+
#
|
154
|
+
# Christian Neukirchen:: http://kronavita.de/chris
|
155
|
+
#
|
156
|
+
#++
|
157
|
+
#
|
158
|
+
|
159
|
+
module Kramdown
|
160
|
+
module Parser
|
161
|
+
class Kramdown
|
162
|
+
|
163
|
+
SQ_PUNCT = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
|
164
|
+
SQ_CLOSE = %![^\ \\\\\t\r\n\\[{(-]!
|
165
|
+
|
166
|
+
SQ_RULES = [
|
167
|
+
[/("|')(?=#{SQ_PUNCT}\B)/, [:rquote1]],
|
168
|
+
# Special case for double sets of quotes, e.g.:
|
169
|
+
# <p>He said, "'Quoted' words in a larger quote."</p>
|
170
|
+
[/(\s?)"'(?=\w)/, [1, :ldquo, :lsquo]],
|
171
|
+
[/(\s?)'"(?=\w)/, [1, :lsquo, :ldquo]],
|
172
|
+
# Special case for decade abbreviations (the '80s):
|
173
|
+
[/(\s?)'(?=\d\ds)/, [1, :rsquo]],
|
174
|
+
|
175
|
+
# Get most opening single/double quotes:
|
176
|
+
[/(\s)('|")(?=\w)/, [1, :lquote2]],
|
177
|
+
# Single/double closing quotes:
|
178
|
+
[/(#{SQ_CLOSE})('|")/, [1, :rquote2]],
|
179
|
+
# Special case for e.g. "<i>Custer</i>'s Last Stand."
|
180
|
+
[/("|')(\s|s\b|$)/, [:rquote1, 2]],
|
181
|
+
# Any remaining single quotes should be opening ones:
|
182
|
+
[/(.?)'/m, [1, :lsquo]],
|
183
|
+
[/(.?)"/m, [1, :ldquo]],
|
184
|
+
] #'"
|
185
|
+
|
186
|
+
SQ_SUBSTS = {
|
187
|
+
[:rquote1, '"'] => :rdquo,
|
188
|
+
[:rquote1, "'"] => :rsquo,
|
189
|
+
[:rquote2, '"'] => :rdquo,
|
190
|
+
[:rquote2, "'"] => :rsquo,
|
191
|
+
[:lquote1, '"'] => :ldquo,
|
192
|
+
[:lquote1, "'"] => :lsquo,
|
193
|
+
[:lquote2, '"'] => :ldquo,
|
194
|
+
[:lquote2, "'"] => :lsquo,
|
195
|
+
}
|
196
|
+
SMART_QUOTES_RE = /[^\\]?["']/
|
197
|
+
|
198
|
+
# Parse the smart quotes at current location.
|
199
|
+
def parse_smart_quotes
|
200
|
+
substs = SQ_RULES.find {|reg, subst| @src.scan(reg)}[1]
|
201
|
+
substs.each do |subst|
|
202
|
+
if subst.kind_of?(Integer)
|
203
|
+
add_text(@src[subst])
|
204
|
+
else
|
205
|
+
val = SQ_SUBSTS[[subst, @src[subst.to_s[-1,1].to_i]]] || subst
|
206
|
+
@tree.children << Element.new(:smart_quote, val)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
define_parser(:smart_quotes, SMART_QUOTES_RE, '[^\\\\]?["\']')
|
211
|
+
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,178 @@
|
|
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
|
+
TABLE_SEP_LINE = /^([+|: -]*?-[+|: -]*?)[ \t]*\n/
|
30
|
+
TABLE_HSEP_ALIGN = /[ ]?(:?)-+(:?)[ ]?/
|
31
|
+
TABLE_FSEP_LINE = /^[+|: =]*?=[+|: =]*?[ \t]*\n/
|
32
|
+
TABLE_ROW_LINE = /^(.*?)[ \t]*\n/
|
33
|
+
TABLE_PIPE_CHECK = /(?:\||.*?[^\\\n]\|)/
|
34
|
+
TABLE_LINE = /#{TABLE_PIPE_CHECK}.*?\n/
|
35
|
+
TABLE_START = /^#{OPT_SPACE}(?=\S)#{TABLE_LINE}/
|
36
|
+
|
37
|
+
# Parse the table at the current location.
|
38
|
+
def parse_table
|
39
|
+
return false if !after_block_boundary?
|
40
|
+
|
41
|
+
orig_pos = @src.pos
|
42
|
+
table = new_block_el(:table, nil, nil, :alignment => [])
|
43
|
+
leading_pipe = (@src.check(TABLE_LINE) =~ /^\s*\|/)
|
44
|
+
@src.scan(TABLE_SEP_LINE)
|
45
|
+
|
46
|
+
rows = []
|
47
|
+
has_footer = false
|
48
|
+
columns = 0
|
49
|
+
|
50
|
+
add_container = lambda do |type, force|
|
51
|
+
if !has_footer || type != :tbody || force
|
52
|
+
cont = Element.new(type)
|
53
|
+
cont.children, rows = rows, []
|
54
|
+
table.children << cont
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
while !@src.eos?
|
59
|
+
break if !@src.check(TABLE_LINE)
|
60
|
+
if @src.scan(TABLE_SEP_LINE) && !rows.empty?
|
61
|
+
if table.options[:alignment].empty? && !has_footer
|
62
|
+
add_container.call(:thead, false)
|
63
|
+
table.options[:alignment] = @src[1].scan(TABLE_HSEP_ALIGN).map do |left, right|
|
64
|
+
(left.empty? && right.empty? && :default) || (right.empty? && :left) || (left.empty? && :right) || :center
|
65
|
+
end
|
66
|
+
else # treat as normal separator line
|
67
|
+
add_container.call(:tbody, false)
|
68
|
+
end
|
69
|
+
elsif @src.scan(TABLE_FSEP_LINE)
|
70
|
+
add_container.call(:tbody, true) if !rows.empty?
|
71
|
+
has_footer = true
|
72
|
+
elsif @src.scan(TABLE_ROW_LINE)
|
73
|
+
trow = Element.new(:tr)
|
74
|
+
|
75
|
+
# parse possible code spans on the line and correctly split the line into cells
|
76
|
+
env = save_env
|
77
|
+
cells = []
|
78
|
+
@src[1].split(/(<code.*?>.*?<\/code>)/).each_with_index do |str, i|
|
79
|
+
if i % 2 == 1
|
80
|
+
(cells.empty? ? cells : cells.last) << str
|
81
|
+
else
|
82
|
+
reset_env(:src => StringScanner.new(str))
|
83
|
+
root = Element.new(:root)
|
84
|
+
parse_spans(root, nil, [:codespan])
|
85
|
+
|
86
|
+
root.children.each do |c|
|
87
|
+
if c.type == :raw_text
|
88
|
+
# Only on Ruby 1.9: f, *l = c.value.split(/(?<!\\)\|/).map {|t| t.gsub(/\\\|/, '|')}
|
89
|
+
f, *l = c.value.split(/\\\|/, -1).map {|t| t.split(/\|/, -1)}.inject([]) do |memo, t|
|
90
|
+
memo.last << "|#{t.shift}" if memo.size > 0
|
91
|
+
memo.concat(t)
|
92
|
+
end
|
93
|
+
(cells.empty? ? cells : cells.last) << f
|
94
|
+
cells.concat(l)
|
95
|
+
else
|
96
|
+
delim = (c.value.scan(/`+/).max || '') + '`'
|
97
|
+
tmp = "#{delim}#{' ' if delim.size > 1}#{c.value}#{' ' if delim.size > 1}#{delim}"
|
98
|
+
(cells.empty? ? cells : cells.last) << tmp
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
restore_env(env)
|
104
|
+
|
105
|
+
cells.shift if leading_pipe && cells.first.strip.empty?
|
106
|
+
cells.pop if cells.last.strip.empty?
|
107
|
+
cells.each do |cell_text|
|
108
|
+
tcell = Element.new(:td)
|
109
|
+
tcell.children << Element.new(:raw_text, cell_text.strip)
|
110
|
+
trow.children << tcell
|
111
|
+
end
|
112
|
+
columns = [columns, cells.length].max
|
113
|
+
rows << trow
|
114
|
+
else
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
if !before_block_boundary?
|
120
|
+
@src.pos = orig_pos
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
|
124
|
+
# Parse all lines of the table with the code span parser
|
125
|
+
env = save_env
|
126
|
+
reset_env(:src => StringScanner.new(extract_string(orig_pos...(@src.pos-1), @src)))
|
127
|
+
root = Element.new(:root)
|
128
|
+
parse_spans(root, nil, [:codespan])
|
129
|
+
restore_env(env)
|
130
|
+
|
131
|
+
# Check if each line has at least one unescaped backslash that is not inside a code span
|
132
|
+
pipe_on_line = false
|
133
|
+
while (c = root.children.shift)
|
134
|
+
lines = c.value.split(/\n/)
|
135
|
+
if c.type == :codespan
|
136
|
+
if lines.size > 2 || (lines.size == 2 && !pipe_on_line)
|
137
|
+
break
|
138
|
+
elsif lines.size == 2 && pipe_on_line
|
139
|
+
pipe_on_line = false
|
140
|
+
end
|
141
|
+
else
|
142
|
+
break if lines.size > 1 && !pipe_on_line && lines.first !~ /^#{TABLE_PIPE_CHECK}/
|
143
|
+
pipe_on_line = (lines.size > 1 ? false : pipe_on_line) || (lines.last =~ /^#{TABLE_PIPE_CHECK}/)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
@src.pos = orig_pos and return false if !pipe_on_line
|
147
|
+
|
148
|
+
add_container.call(has_footer ? :tfoot : :tbody, false) if !rows.empty?
|
149
|
+
|
150
|
+
if !table.children.any? {|el| el.type == :tbody}
|
151
|
+
warning("Found table without body - ignoring it")
|
152
|
+
@src.pos = orig_pos
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
|
156
|
+
# adjust all table rows to have equal number of columns, same for alignment defs
|
157
|
+
table.children.each do |kind|
|
158
|
+
kind.children.each do |row|
|
159
|
+
(columns - row.children.length).times do
|
160
|
+
row.children << Element.new(:td)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
if table.options[:alignment].length > columns
|
165
|
+
table.options[:alignment] = table.options[:alignment][0...columns]
|
166
|
+
else
|
167
|
+
table.options[:alignment] += [:default] * (columns - table.options[:alignment].length)
|
168
|
+
end
|
169
|
+
|
170
|
+
@tree.children << table
|
171
|
+
|
172
|
+
true
|
173
|
+
end
|
174
|
+
define_parser(:table, TABLE_START)
|
175
|
+
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|