kramdown 0.12.0 → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kramdown might be problematic. Click here for more details.

Files changed (41) hide show
  1. data/CONTRIBUTERS +2 -1
  2. data/ChangeLog +278 -0
  3. data/README +3 -0
  4. data/Rakefile +24 -0
  5. data/VERSION +1 -1
  6. data/benchmark/benchmark.sh +11 -2
  7. data/doc/documentation.page +2 -3
  8. data/doc/index.page +4 -4
  9. data/doc/installation.page +10 -11
  10. data/doc/syntax.page +17 -7
  11. data/doc/tests.page +1 -1
  12. data/lib/kramdown/converter/base.rb +9 -6
  13. data/lib/kramdown/converter/html.rb +2 -2
  14. data/lib/kramdown/converter/latex.rb +46 -26
  15. data/lib/kramdown/options.rb +42 -20
  16. data/lib/kramdown/parser/html.rb +50 -11
  17. data/lib/kramdown/parser/kramdown.rb +4 -4
  18. data/lib/kramdown/parser/kramdown/html_entity.rb +7 -2
  19. data/lib/kramdown/parser/kramdown/link.rb +9 -7
  20. data/lib/kramdown/parser/kramdown/list.rb +5 -4
  21. data/lib/kramdown/parser/kramdown/table.rb +24 -19
  22. data/lib/kramdown/utils/entities.rb +4 -0
  23. data/lib/kramdown/version.rb +1 -1
  24. data/man/man1/kramdown.1 +15 -14
  25. data/test/testcases/block/08_list/item_ial.html +3 -0
  26. data/test/testcases/block/08_list/item_ial.text +3 -0
  27. data/test/testcases/block/09_html/html_to_native/emphasis.html +2 -0
  28. data/test/testcases/block/09_html/html_to_native/emphasis.text +3 -0
  29. data/test/testcases/block/09_html/parse_as_raw.html +2 -0
  30. data/test/testcases/block/09_html/parse_as_raw.text +2 -0
  31. data/test/testcases/block/14_table/simple.html +19 -0
  32. data/test/testcases/block/14_table/simple.html.19 +19 -0
  33. data/test/testcases/block/14_table/simple.text +5 -0
  34. data/test/testcases/span/01_link/reference.html +3 -2
  35. data/test/testcases/span/01_link/reference.html.19 +3 -2
  36. data/test/testcases/span/01_link/reference.text +4 -1
  37. data/test/testcases/span/05_html/normal.html +2 -0
  38. data/test/testcases/span/05_html/normal.text +2 -0
  39. data/test/testcases/span/text_substitutions/entities.html +2 -0
  40. data/test/testcases/span/text_substitutions/entities.text +2 -0
  41. metadata +4 -4
data/doc/syntax.page CHANGED
@@ -688,12 +688,12 @@ There are four different line types that can be used in a table:
688
688
 
689
689
  A table row is any line that contains at least one pipe character and is not identified as any
690
690
  other type of table line! The table row is divided into individual table cells by pipe characters.
691
- An optional trailing pipe character is ignored.
691
+ An optional trailing pipe character is ignored. Note that literal pipe characters need to be
692
+ escaped *except* if they occur in code spans or HTML `<code>` elements!
692
693
 
693
694
  Header rows, footer rows and normal rows are all done using these table rows. Table cells can only
694
695
  contain a single line of text, no multi-line text is supported. The text of a table cell is parsed
695
- as span-level elements. Note that literal pipe characters need to be escaped except if they occur
696
- in code spans!
696
+ as span-level elements.
697
697
 
698
698
  Here are some example table rows:
699
699
 
@@ -1108,10 +1108,10 @@ Notes:
1108
1108
 
1109
1109
  To create a reference style link, you need to surround the link text with square brackets (as with
1110
1110
  inline links), followed by optional spaces/tabs/line breaks and then optionally followed with
1111
- another set of square brackets with the link identifier in them. A link identifier may only contain
1112
- numbers, letters, spaces (line breaks and tabs are converted to spaces, multiple spaces are
1113
- compressed to one) and punctuation characters (i.e. `_.:,;!?-`) and is not case sensitive. For
1114
- example:
1111
+ another set of square brackets with the link identifier in them. A link identifier may not contain a
1112
+ closing bracket and, when specified in a link definition, newline characters; it is also not case
1113
+ sensitive, line breaks and tabs are converted to spaces and multiple spaces are compressed into one.
1114
+ For example:
1115
1115
 
1116
1116
  This is a [reference style link][linkid] to a page. And [this]
1117
1117
  [linkid] is also a link. As is [this][] and [THIS].
@@ -1144,6 +1144,16 @@ The link definition has the following structure:
1144
1144
  > allowed for consistency with the inline title.
1145
1145
  {: .markdown-difference}
1146
1146
 
1147
+ If you have some text that looks like a link definition but should really be a link and some text,
1148
+ you can escape the colon after the link identifier:
1149
+
1150
+ The next paragraph contains a link and some text.
1151
+
1152
+ [Room 100]\: There you should find everything you need!
1153
+
1154
+ [Room 100]: link_to_room_100.html
1155
+
1156
+
1147
1157
  ### Images
1148
1158
 
1149
1159
  Images can be specified via a syntax that is similar to the one used by links. The difference is
data/doc/tests.page CHANGED
@@ -45,7 +45,7 @@ different Ruby interpreters:
45
45
  ![ruby 1.8.6p399]({relocatable: img/graph-ruby-1.8.6-399.png})
46
46
  ![ruby 1.8.7p249]({relocatable: img/graph-ruby-1.8.7-249.png})
47
47
  ![ruby 1.8.7p302]({relocatable: img/graph-ruby-1.8.7-302.png})
48
- ![ruby 1.9.2p0]({relocatable: img/graph-ruby-1.9.2p0-0.png})
48
+ ![ruby 1.9.2p136]({relocatable: img/graph-ruby-1.9.2p136-136.png})
49
49
  ![jruby 1.5.3]({relocatable: img/graph-jruby-1.5.3-249.png})
50
50
 
51
51
  [Markdown Test Suite]: http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip
@@ -132,12 +132,7 @@ module Kramdown
132
132
  # Return +true+ if the header element +el+ should be used for the table of contents (as
133
133
  # specified by the +toc_levels+ option).
134
134
  def in_toc?(el)
135
- if @options[:toc_depth] >= 0
136
- warn('Option toc_depth is deprecated, use the new option toc_levels instead')
137
- @options[:toc_depth] == 0 || el.options[:level] <= @options[:toc_depth]
138
- else
139
- @options[:toc_levels].include?(el.options[:level])
140
- end
135
+ @options[:toc_levels].include?(el.options[:level])
141
136
  end
142
137
 
143
138
  # Generate an unique alpha-numeric ID from the the string +str+ for use as a header ID.
@@ -159,6 +154,14 @@ module Kramdown
159
154
  @options[:auto_id_prefix] + gen_id
160
155
  end
161
156
 
157
+ SMART_QUOTE_INDICES = {:lsquo => 0, :rsquo => 1, :ldquo => 2, :rdquo => 3} # :nodoc:
158
+
159
+ # Return the entity that represents the given smart_quote element.
160
+ def smart_quote_entity(el)
161
+ res = @options[:smart_quotes][SMART_QUOTE_INDICES[el.value]]
162
+ ::Kramdown::Utils::Entities.entity(res)
163
+ end
164
+
162
165
  end
163
166
 
164
167
  end
@@ -178,7 +178,7 @@ module Kramdown
178
178
  end
179
179
 
180
180
  # A list of all HTML tags that need to have a body (even if the body is empty).
181
- HTML_TAGS_WITH_BODY=['div', 'script', 'iframe', 'textarea'] # :nodoc:
181
+ HTML_TAGS_WITH_BODY=['div', 'script', 'iframe', 'textarea', 'a'] # :nodoc:
182
182
 
183
183
  def convert_html_element(el, indent)
184
184
  res = inner(el, indent)
@@ -311,7 +311,7 @@ module Kramdown
311
311
  end
312
312
 
313
313
  def convert_smart_quote(el, indent)
314
- entity_to_str(::Kramdown::Utils::Entities.entity(el.value.to_s))
314
+ entity_to_str(smart_quote_entity(el))
315
315
  end
316
316
 
317
317
  def convert_math(el, indent)
@@ -84,7 +84,7 @@ module Kramdown
84
84
  if el.children.size == 1 && el.children.first.type == :img && !(img = convert_img(el.children.first, opts)).empty?
85
85
  convert_standalone_image(el, opts, img)
86
86
  else
87
- "#{inner(el, opts)}\n\n"
87
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
88
88
  end
89
89
  end
90
90
 
@@ -92,20 +92,23 @@ module Kramdown
92
92
  # <tt>:img</tt> element.
93
93
  def convert_standalone_image(el, opts, img)
94
94
  attrs = attribute_list(el)
95
- "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.attr['alt'])}}\n\\end{figure}#{attrs}\n"
95
+ "\\begin{figure}#{attrs}\n\\begin{center}\n#{img}\n\\end{center}\n\\caption{#{escape(el.children.first.attr['alt'])}}\n#{latex_link_target(el, true)}\n\\end{figure}#{attrs}\n"
96
96
  end
97
97
 
98
98
  def convert_codeblock(el, opts)
99
99
  show_whitespace = el.attr['class'].to_s =~ /\bshow-whitespaces\b/
100
100
  lang = el.attr['lang']
101
101
  if show_whitespace || lang
102
- result = "\\lstset{showspaces=%s,showtabs=%s}\n" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
103
- result << "\\lstset{language=#{lang}}\n" if lang
104
- result << "\\lstset{basicstyle=\\ttfamily\\footnotesize}\\lstset{columns=fixed,frame=tlbr}\n"
102
+ options = []
103
+ options << "showspaces=%s,showtabs=%s" % (show_whitespace ? ['true', 'true'] : ['false', 'false'])
104
+ options << "language=#{lang}" if lang
105
+ options << "basicstyle=\\ttfamily\\footnotesize,columns=fixed,frame=tlbr"
106
+ id = el.attr['id']
107
+ options << "label=#{id}" if id
105
108
  attrs = attribute_list(el)
106
- result << "\\begin{lstlisting}#{attrs}\n#{el.value}\n\\end{lstlisting}#{attrs}\n"
109
+ "#{latex_link_target(el)}\\begin{lstlisting}[#{options.join(',')}]\n#{el.value}\n\\end{lstlisting}#{attrs}\n"
107
110
  else
108
- "\\begin{verbatim}#{el.value}\\end{verbatim}\n"
111
+ "#{latex_link_target(el)}\\begin{verbatim}#{el.value}\\end{verbatim}\n"
109
112
  end
110
113
  end
111
114
 
@@ -117,7 +120,7 @@ module Kramdown
117
120
  type = @options[:latex_headers][el.options[:level] - 1]
118
121
  if ((id = el.attr['id']) ||
119
122
  (@options[:auto_ids] && (id = generate_id(el.options[:raw_text])))) && in_toc?(el)
120
- "\\hypertarget{#{id}}{}\\#{type}{#{inner(el, opts)}}\\label{#{id}}\n\n"
123
+ "\\#{type}{#{inner(el, opts)}}\\hypertarget{#{id}}{}\\label{#{id}}\n\n"
121
124
  else
122
125
  "\\#{type}*{#{inner(el, opts)}}\n\n"
123
126
  end
@@ -125,7 +128,7 @@ module Kramdown
125
128
 
126
129
  def convert_hr(el, opts)
127
130
  attrs = attribute_list(el)
128
- "\\begin{center}#{attrs}\n\\rule{3in}{0.4pt}\n\\end{center}#{attrs}\n"
131
+ "#{latex_link_target(el)}\\begin{center}#{attrs}\n\\rule{3in}{0.4pt}\n\\end{center}#{attrs}\n"
129
132
  end
130
133
 
131
134
  def convert_ul(el, opts)
@@ -143,7 +146,7 @@ module Kramdown
143
146
  end
144
147
 
145
148
  def convert_li(el, opts)
146
- "\\item #{inner(el, opts).sub(/\n+\Z/, '')}\n"
149
+ "\\item #{latex_link_target(el, true)}#{inner(el, opts).sub(/\n+\Z/, '')}\n"
147
150
  end
148
151
 
149
152
  def convert_dt(el, opts)
@@ -151,7 +154,7 @@ module Kramdown
151
154
  end
152
155
 
153
156
  def convert_dd(el, opts)
154
- "#{inner(el, opts)}\n\n"
157
+ "#{latex_link_target(el)}#{inner(el, opts)}\n\n"
155
158
  end
156
159
 
157
160
  def convert_html_element(el, opts)
@@ -179,7 +182,7 @@ module Kramdown
179
182
  def convert_table(el, opts)
180
183
  align = el.options[:alignment].map {|a| TABLE_ALIGNMENT_CHAR[a]}.join('|')
181
184
  attrs = attribute_list(el)
182
- "\\begin{tabular}{|#{align}|}#{attrs}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}#{attrs}\n\n"
185
+ "#{latex_link_target(el)}\\begin{tabular}{|#{align}|}#{attrs}\n\\hline\n#{inner(el, opts)}\\hline\n\\end{tabular}#{attrs}\n\n"
183
186
  end
184
187
 
185
188
  def convert_thead(el, opts)
@@ -227,7 +230,7 @@ module Kramdown
227
230
  ''
228
231
  elsif !el.options.attr['src'].empty?
229
232
  @data[:packages] << 'graphicx'
230
- "\\includegraphics{#{el.attr['src']}}"
233
+ "#{latex_link_target(el)}\\includegraphics{#{el.attr['src']}}"
231
234
  else
232
235
  warning("Cannot include image with empty path")
233
236
  ''
@@ -235,7 +238,7 @@ module Kramdown
235
238
  end
236
239
 
237
240
  def convert_codespan(el, opts)
238
- "{\\tt #{escape(el.value)}}"
241
+ "{\\tt #{latex_link_target(el)}#{escape(el.value)}}"
239
242
  end
240
243
 
241
244
  def convert_footnote(el, opts)
@@ -252,11 +255,11 @@ module Kramdown
252
255
  end
253
256
 
254
257
  def convert_em(el, opts)
255
- "\\emph{#{inner(el, opts)}}"
258
+ "\\emph{#{latex_link_target(el)}#{inner(el, opts)}}"
256
259
  end
257
260
 
258
261
  def convert_strong(el, opts)
259
- "\\textbf{#{inner(el, opts)}}"
262
+ "\\textbf{#{latex_link_target(el)}#{inner(el, opts)}}"
260
263
  end
261
264
 
262
265
  # Inspired by Maruku: entity conversion table based on the one from htmltolatex
@@ -387,8 +390,8 @@ module Kramdown
387
390
  34 => ['"'],
388
391
  39 => ['\''],
389
392
  169 => ['\copyright'],
390
- 60 => ['\textless{}'],
391
- 62 => ['\textgreater{}'],
393
+ 60 => ['\textless'],
394
+ 62 => ['\textgreater'],
392
395
  338 => ['\OE'],
393
396
  339 => ['\oe'],
394
397
  352 => ['\v{S}'],
@@ -503,20 +506,27 @@ module Kramdown
503
506
  253 => ['\\\'y'],
504
507
  254 => ['\thorn', 'wasysym'],
505
508
  255 => ['\"y'],
509
+ 8201 => ['\thinspace'],
510
+ 8194 => ['\hskip .5em\relax'],
511
+ 8195 => ['\quad'],
506
512
  } # :nodoc:
507
- ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k] = v.unshift(v.shift + '{}')}
513
+ ENTITY_CONV_TABLE.each {|k,v| ENTITY_CONV_TABLE[k][0].insert(-1, '{}')}
508
514
 
509
- def convert_entity(el, opts)
510
- text, package = ENTITY_CONV_TABLE[el.value.code_point]
515
+ def entity_to_latex(entity)
516
+ text, package = ENTITY_CONV_TABLE[entity.code_point]
511
517
  if text
512
518
  @data[:packages] << package if package
513
519
  text
514
520
  else
515
- warning("Couldn't find entity in substitution table!")
521
+ warning("Couldn't find entity with code #{entity.code_point} in substitution table!")
516
522
  ''
517
523
  end
518
524
  end
519
525
 
526
+ def convert_entity(el, opts)
527
+ entity_to_latex(el.value)
528
+ end
529
+
520
530
  TYPOGRAPHIC_SYMS = {
521
531
  :mdash => '---', :ndash => '--', :hellip => '\ldots{}',
522
532
  :laquo_space => '\guillemotleft{}~', :raquo_space => '~\guillemotright{}',
@@ -526,10 +536,9 @@ module Kramdown
526
536
  TYPOGRAPHIC_SYMS[el.value]
527
537
  end
528
538
 
529
- SMART_QUOTE_SYMS = {:lsquo => '`', :rsquo => '\'', :ldquo => '``', :rdquo => '\'\''} # :nodoc:
530
539
  def convert_smart_quote(el, opts)
531
- res = SMART_QUOTE_SYMS[el.value]
532
- res << "{}" if (nel = opts[:parent].children[opts[:index]+1]) && nel.type == :smart_quote
540
+ res = entity_to_latex(smart_quote_entity(el)).chomp('{}')
541
+ res << "{}" if ((nel = opts[:parent].children[opts[:index]+1]) && nel.type == :smart_quote) || res =~ /\w$/
533
542
  res
534
543
  end
535
544
 
@@ -556,7 +565,18 @@ module Kramdown
556
565
  # environments.
557
566
  def latex_environment(type, el, text)
558
567
  attrs = attribute_list(el)
559
- "\\begin{#{type}}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
568
+ "\\begin{#{type}}#{latex_link_target(el)}#{attrs}\n#{text.rstrip}\n\\end{#{type}}#{attrs}\n"
569
+ end
570
+
571
+ # Return a string containing a valid <tt>\hypertarget</tt> command if the element has an ID
572
+ # defined, or +nil+ otherwise. If the parameter +add_label+ is +true+, a <tt>\label</tt>
573
+ # command will also be used additionally to the <tt>\hypertarget</tt> command.
574
+ def latex_link_target(el, add_label = false)
575
+ if (id = el.attr['id'])
576
+ "\\hypertarget{#{id}}{}" << (add_label ? "\\label{#{id}}" : '')
577
+ else
578
+ nil
579
+ end
560
580
  end
561
581
 
562
582
  # Return a LaTeX comment containing all attributes as <tt>key="value"</tt> pairs.
@@ -119,6 +119,31 @@ module Kramdown
119
119
  data
120
120
  end
121
121
 
122
+ # ----------------------------
123
+ # :section: Option Validators
124
+ #
125
+ # This sections contains all pre-defined option validators.
126
+ # ----------------------------
127
+
128
+ # Ensures that the option value +val+ for the option called +name+ is a valid array. The
129
+ # parameter +val+ can be
130
+ #
131
+ # - a comma separated string which is split into an array of values
132
+ # - or an array.
133
+ #
134
+ # Additionally, the array is checked for the correct size.
135
+ def self.simple_array_validator(val, name, size)
136
+ if String === val
137
+ val = val.split(/,/)
138
+ elsif !(Array === val)
139
+ raise Kramdown::Error, "Invalid type #{val.class} for option #{name}"
140
+ end
141
+ if val.size != size
142
+ raise Kramdown::Error, "Option #{name} needs exactly #{size} values"
143
+ end
144
+ val
145
+ end
146
+
122
147
  # ----------------------------
123
148
  # :section: Option Definitions
124
149
  #
@@ -141,7 +166,7 @@ file still cannot be found, the templates name is interpreted as a
141
166
  template name that is provided by kramdown (without the converter
142
167
  extension).
143
168
 
144
- kramdown provides a default template named 'default' for each converter.
169
+ kramdown provides a default template named 'document' for each converter.
145
170
 
146
171
  Default: ''
147
172
  Used by: all converters
@@ -277,17 +302,6 @@ on Ruby 1.9).
277
302
 
278
303
  Default: :as_char
279
304
  Used by: HTML converter, kramdown converter
280
- EOF
281
-
282
- define(:toc_depth, Integer, -1, <<EOF)
283
- DEPRECATED: Defines the maximum level of headers which will be used to generate the table of
284
- contents. For instance, with a value of 2, toc entries will be generated for h1
285
- and h2 headers but not for h3, h4, etc. A value of 0 uses all header levels.
286
-
287
- Use option toc_levels instead!
288
-
289
- Default: -1
290
- Used by: HTML/Latex converter
291
305
  EOF
292
306
 
293
307
  define(:toc_levels, Object, (1..6).to_a, <<EOF) do |val|
@@ -335,14 +349,22 @@ separating them with commas.
335
349
  Default: section,subsection,subsubsection,paragraph,subparagraph,subsubparagraph
336
350
  Used by: Latex converter
337
351
  EOF
338
- if String === val
339
- val = val.split(/,/)
340
- elsif !(Array === val)
341
- raise Kramdown::Error, "Invalid type #{val.class} for option latex_headers"
342
- end
343
- if val.size != 6
344
- raise Kramdown::Error, "Option latex_headers needs exactly six LaTeX commands"
345
- end
352
+ simple_array_validator(val, :latex_headers, 6)
353
+ end
354
+
355
+ define(:smart_quotes, Object, %w{lsquo rsquo ldquo rdquo}, <<EOF) do |val|
356
+ Defines the HTML entity names or code points for smart quote output
357
+
358
+ The entities identified by entity name or code point that should be
359
+ used for, in order, a left single quote, a right single quote, a left
360
+ double and a right double quote are specified by separating them with
361
+ commas.
362
+
363
+ Default: lsquo,rsquo,ldquo,rdquo
364
+ Used by: HTML/Latex converter
365
+ EOF
366
+ val = simple_array_validator(val, :smart_quotes, 4)
367
+ val.map! {|v| Integer(v) rescue v}
346
368
  val
347
369
  end
348
370
 
@@ -260,7 +260,12 @@ module Kramdown
260
260
  elsif %w{mdash ndash hellip laquo raquo}.include?(val)
261
261
  Element.new(:typographic_sym, val.intern)
262
262
  else
263
- Element.new(:entity, entity(val), nil, :original => src.matched)
263
+ begin
264
+ Element.new(:entity, entity(val), nil, :original => src.matched)
265
+ rescue ::Kramdown::Error
266
+ src.pos -= src.matched_size - 1
267
+ Element.new(:entity, ::Kramdown::Utils::Entities.entity('amp'))
268
+ end
264
269
  end
265
270
  else
266
271
  result << Element.new(:text, src.rest)
@@ -339,14 +344,19 @@ module Kramdown
339
344
  end
340
345
  end
341
346
 
342
- def convert_b(el)
343
- set_basics(el, :strong)
344
- process_children(el)
347
+ EMPHASIS_TYPE_MAP = {'em' => :em, 'i' => :em, 'strong' => :strong, 'b' => :strong}
348
+ def convert_em(el)
349
+ text = ''
350
+ extract_text(el, text)
351
+ if text =~ /\A\s/ || text =~ /\s\z/
352
+ process_html_element(el, false)
353
+ else
354
+ set_basics(el, EMPHASIS_TYPE_MAP[el.value])
355
+ process_children(el)
356
+ end
345
357
  end
346
-
347
- def convert_i(el)
348
- set_basics(el, :em)
349
- process_children(el)
358
+ %w{b strong i}.each do |i|
359
+ alias_method("convert_#{i}".to_sym, :convert_em)
350
360
  end
351
361
 
352
362
  def convert_h1(el)
@@ -405,15 +415,25 @@ module Kramdown
405
415
  set_basics(el, :table)
406
416
  el.options[:alignment] = []
407
417
 
418
+ nr_cols = 0
408
419
  calc_alignment = lambda do |c|
409
- if c.type == :tr && el.options[:alignment].empty?
410
- el.options[:alignment] = [:default] * c.children.length
420
+ align = c.attr['align']
421
+ if c.type == :html_element && c.value == 'col' && (align.nil? || %w{left right center}.include?(align))
422
+ el.options[:alignment] << (align.nil? ? :default : align.to_sym)
423
+ elsif c.type == :tr
424
+ nr_cols = c.children.length
411
425
  break
412
426
  else
413
427
  c.children.each {|cc| calc_alignment.call(cc)}
414
428
  end
415
429
  end
416
430
  calc_alignment.call(el)
431
+ if el.options[:alignment].length > nr_cols
432
+ el.options[:alignment][nr_cols..-1] = []
433
+ else
434
+ el.options[:alignment] += [:default] * (nr_cols - el.options[:alignment].length)
435
+ end
436
+ el.children.delete_if {|c| c.type == :html_element}
417
437
 
418
438
  change_th_type = lambda do |c|
419
439
  if c.type == :th
@@ -446,12 +466,31 @@ module Kramdown
446
466
  end
447
467
  check_cells.call(el)
448
468
 
469
+ nr_cells = 0
470
+ check_nr_cells = lambda do |t|
471
+ if t.value == 'tr'
472
+ count = t.children.select {|cc| cc.value == 'th' || cc.value == 'td'}.length
473
+ if count != nr_cells
474
+ if nr_cells == 0
475
+ nr_cells = count
476
+ else
477
+ nr_cells = -1
478
+ break
479
+ end
480
+ end
481
+ else
482
+ t.children.each {|cc| check_nr_cells.call(cc)}
483
+ end
484
+ end
485
+ check_nr_cells.call(el)
486
+ return false if nr_cells == -1
487
+
449
488
  check_rows = lambda do |t, type|
450
489
  t.children.all? {|r| (r.value == 'tr' || r.type == :text) && r.children.all? {|c| c.value == type || c.type == :text}}
451
490
  end
452
491
  check_rows.call(el, 'td') ||
453
492
  (el.children.all? do |t|
454
- t.type == :text || (t.value == 'thead' && check_rows.call(t, 'th')) ||
493
+ t.type == :text || t.value == 'col' || (t.value == 'thead' && check_rows.call(t, 'th')) ||
455
494
  ((t.value == 'tfoot' || t.value == 'tbody') && check_rows.call(t, 'td'))
456
495
  end && el.children.any? {|t| t.value == 'tbody'})
457
496
  end