reverse_markdown 0.4.7 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -3
- data/LICENSE +13 -0
- data/README.md +69 -30
- data/bin/reverse_markdown +1 -1
- data/lib/reverse_markdown/cleaner.rb +66 -0
- data/lib/reverse_markdown/config.rb +20 -0
- data/lib/reverse_markdown/converters/a.rb +19 -0
- data/lib/reverse_markdown/converters/base.rb +24 -0
- data/lib/reverse_markdown/converters/blockquote.rb +13 -0
- data/lib/reverse_markdown/converters/br.rb +11 -0
- data/lib/reverse_markdown/converters/code.rb +11 -0
- data/lib/reverse_markdown/converters/div.rb +11 -0
- data/lib/reverse_markdown/converters/dump.rb +9 -0
- data/lib/reverse_markdown/converters/em.rb +21 -0
- data/lib/reverse_markdown/converters/h.rb +17 -0
- data/lib/reverse_markdown/converters/hr.rb +11 -0
- data/lib/reverse_markdown/converters/img.rb +14 -0
- data/lib/reverse_markdown/converters/li.rb +28 -0
- data/lib/reverse_markdown/converters/ol.rb +12 -0
- data/lib/reverse_markdown/converters/p.rb +11 -0
- data/lib/reverse_markdown/converters/pass_through.rb +14 -0
- data/lib/reverse_markdown/converters/pre.rb +17 -0
- data/lib/reverse_markdown/converters/strong.rb +21 -0
- data/lib/reverse_markdown/converters/table.rb +11 -0
- data/lib/reverse_markdown/converters/td.rb +13 -0
- data/lib/reverse_markdown/converters/text.rb +30 -0
- data/lib/reverse_markdown/converters/tr.rb +21 -0
- data/lib/reverse_markdown/converters.rb +22 -0
- data/lib/reverse_markdown/errors.rb +6 -2
- data/lib/reverse_markdown/version.rb +1 -1
- data/lib/reverse_markdown.rb +40 -8
- data/reverse_markdown.gemspec +3 -1
- data/spec/assets/lists.html +24 -0
- data/spec/assets/unknown_tags.html +9 -0
- data/spec/components/anchors_spec.rb +5 -5
- data/spec/components/basic_spec.rb +5 -5
- data/spec/components/code_spec.rb +5 -5
- data/spec/components/escapables_spec.rb +3 -3
- data/spec/components/from_the_wild_spec.rb +5 -5
- data/spec/components/html_fragment_spec.rb +2 -2
- data/spec/components/lists_spec.rb +18 -7
- data/spec/components/paragraphs_spec.rb +2 -2
- data/spec/components/quotation_spec.rb +5 -5
- data/spec/components/tables_spec.rb +2 -2
- data/spec/components/unknown_tags_spec.rb +21 -0
- data/spec/html_to_markdown_to_html_spec.rb +1 -2
- data/spec/lib/reverse_markdown/cleaner_spec.rb +76 -0
- data/spec/lib/reverse_markdown/converters/blockquote_spec.rb +18 -0
- data/spec/lib/reverse_markdown/converters/br_spec.rb +9 -0
- data/spec/lib/reverse_markdown_spec.rb +37 -0
- data/spec/spec_helper.rb +9 -1
- metadata +83 -24
- data/License-MIT +0 -7
- data/lib/reverse_markdown/mapper.rb +0 -241
- data/spec/mapper_spec.rb +0 -63
- data/spec/reverse_markdown_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d29227386b5418e906da5a4cb42ce616a2d3872b
|
4
|
+
data.tar.gz: 0f433297212a99d783573daac3fd3723e7d9f6a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23ff8eb3e7a76e197b5526fcdf6c14d899e1ec600f1b40046d5584e2c40d62a9f22734656b5843f38ec5fa53dcf3a31f7b9baa7c2962e993eb07e5d4486f3afa
|
7
|
+
data.tar.gz: 2e46ce50817a0e735329dd2e73a9d47943e284a7cfb440362c8f8b6b1cbbb4d9945a6c492bafe8bd34843b7987fe1556410d29d4f6b34ce1d7936c570759f5dc
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2014 Johannes Opper <xijo@gmx.de>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/README.md
CHANGED
@@ -1,69 +1,108 @@
|
|
1
1
|
# Summary
|
2
2
|
|
3
|
-
Transform
|
3
|
+
Transform html into markdown. Useful for example if you want to import html into your markdown based application.
|
4
4
|
|
5
|
-
[![Build Status](https://secure.travis-ci.org/xijo/reverse_markdown.png?branch=master)](https://travis-ci.org/xijo/reverse_markdown) [![Gem Version](https://badge.fury.io/rb/reverse_markdown.png)](http://badge.fury.io/rb/reverse_markdown)
|
5
|
+
[![Build Status](https://secure.travis-ci.org/xijo/reverse_markdown.png?branch=master)](https://travis-ci.org/xijo/reverse_markdown) [![Gem Version](https://badge.fury.io/rb/reverse_markdown.png)](http://badge.fury.io/rb/reverse_markdown) [![Code Climate](https://codeclimate.com/github/xijo/reverse_markdown.png)](https://codeclimate.com/github/xijo/reverse_markdown) [![Code Climate](https://codeclimate.com/github/xijo/reverse_markdown/coverage.png)](https://codeclimate.com/github/xijo/reverse_markdown)
|
6
6
|
|
7
|
-
|
7
|
+
## Migrate to 0.5.0
|
8
8
|
|
9
|
-
|
9
|
+
There were some breaking changes, please make sure you don't miss them:
|
10
10
|
|
11
|
-
|
11
|
+
1. Only ruby versions 2.0.0 or a above are supported
|
12
|
+
2. There is no `Mapper` class any more. Just use `ReverseMarkdown.convert(input, options)`
|
13
|
+
3. Config option `github_style_code_blocks` changed its name to `github_flavored`
|
12
14
|
|
13
|
-
|
15
|
+
Please open an issue and let me know about it if you have any trouble with the new version.
|
14
16
|
|
15
|
-
|
17
|
+
## Requirements
|
16
18
|
|
17
|
-
|
19
|
+
1. [Nokogiri](http://nokogiri.org/)
|
20
|
+
2. Ruby 2.0.0 or higher
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Install the gem
|
25
|
+
|
26
|
+
```sh
|
27
|
+
[sudo] gem install reverse_markdown
|
28
|
+
```
|
29
|
+
|
30
|
+
or add it to your Gemfile
|
18
31
|
|
19
32
|
```ruby
|
20
33
|
gem 'reverse_markdown'
|
21
34
|
```
|
22
35
|
|
23
|
-
|
36
|
+
## Features
|
24
37
|
|
25
|
-
|
38
|
+
- Supports all the established html tags like `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `p`, `em`, `strong`, `i`, `b`, `blockquote`, `code`, `img`, `a`, `hr`, `li`, `ol`, `ul`, `table`, `tr`, `th`, `td`, `br`
|
39
|
+
- Module based - if you miss a tag, just add it
|
40
|
+
- Can deal with nested lists
|
41
|
+
- Inline and block code is supported
|
42
|
+
- Supports blockquote
|
26
43
|
|
27
|
-
```ruby
|
28
|
-
ReverseMarkdown.parse content
|
29
|
-
````
|
30
44
|
|
31
|
-
|
45
|
+
# Usage
|
46
|
+
|
47
|
+
## Ruby
|
48
|
+
|
49
|
+
You can convert html content as string or Nokogiri document:
|
32
50
|
|
33
51
|
```ruby
|
34
|
-
|
35
|
-
ReverseMarkdown.
|
52
|
+
input = '<strong>feelings</strong>'
|
53
|
+
result = ReverseMarkdown.convert input
|
54
|
+
result.inspect # " **feelings** "
|
36
55
|
````
|
37
56
|
|
38
|
-
|
57
|
+
## Commandline
|
58
|
+
|
59
|
+
It's also possible to convert html files to markdown using the binary:
|
39
60
|
|
40
61
|
```sh
|
41
|
-
$ reverse_markdown file.html > file.
|
42
|
-
$ cat file.html | reverse_markdown > file.
|
62
|
+
$ reverse_markdown file.html > file.md
|
63
|
+
$ cat file.html | reverse_markdown > file.md
|
43
64
|
````
|
44
65
|
|
45
|
-
|
66
|
+
## Configuration
|
67
|
+
|
68
|
+
The following options are available:
|
69
|
+
|
70
|
+
- `ignore_unknown_tags` (default `true`) - bypass unknown tags instead of raising an `ReverseMarkdown::UnknownTagError`
|
71
|
+
- `github_flavored` (default `false`) - use [github flavored markdown](https://help.github.com/articles/github-flavored-markdown) (yet only code blocks are supported)
|
72
|
+
|
73
|
+
### As options
|
74
|
+
|
75
|
+
Just pass your chosen configuration options in after the input
|
46
76
|
|
47
77
|
```ruby
|
48
|
-
ReverseMarkdown.
|
49
|
-
|
78
|
+
ReverseMarkdown.convert(input, ignore_unknown_tags: false, github_flavored: true)
|
79
|
+
```
|
80
|
+
|
81
|
+
### Preconfigure
|
50
82
|
|
51
|
-
|
83
|
+
Or configure it block style on a initializer level
|
52
84
|
|
53
|
-
|
85
|
+
```ruby
|
86
|
+
ReverseMarkdown.config do |config|
|
87
|
+
config.ignore_unknown_tags = false
|
88
|
+
config.github_flavored = true
|
89
|
+
end
|
90
|
+
```
|
54
91
|
|
55
|
-
- supported tags: `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `p`, `em`, `strong`, `i`, `b`, `blockquote`, `code`, `img`, `a`, `hr`, `li`, `ol`, `ul`, `table`, `tr`, `th`, `td`
|
56
|
-
- nested lists
|
57
|
-
- inline and block code
|
58
92
|
|
59
|
-
#
|
93
|
+
# Related stuff
|
60
94
|
|
61
|
-
- [
|
95
|
+
- [html_massage](https://github.com/harlantwood/html_massage) - A gem by Harlan T. Wood to convert regular sites into markdown using reverse_markdown
|
96
|
+
- [word-to-markdown](https://github.com/benbalter/word-to-markdown) - Convert word docs into markdown while using reverse_markdown, by Ben Balter
|
62
97
|
- [markdown syntax](http://daringfireball.net/projects/markdown) - The markdown syntax specification
|
63
|
-
- [
|
98
|
+
- [github flavored markdown](https://help.github.com/articles/github-flavored-markdown) - Githubs extension to markdown
|
99
|
+
- [wmd-editor](http://wmd-editor.com) - Markdown flavored text editor
|
100
|
+
|
64
101
|
|
65
102
|
# Thanks
|
66
103
|
|
67
104
|
..to Ben Woosley for his improvements to the first version.
|
68
105
|
|
69
106
|
..to Harlan T. Wood for his help with the newer versions.
|
107
|
+
|
108
|
+
..and all contributors
|
data/bin/reverse_markdown
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
class Cleaner
|
3
|
+
|
4
|
+
def tidy(string)
|
5
|
+
clean_tag_borders(remove_leading_newlines(remove_newlines(remove_inner_whitespaces(string))))
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove_newlines(string)
|
9
|
+
string.gsub(/\n{3,}/, "\n\n")
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_leading_newlines(string)
|
13
|
+
string.gsub /\A\n\n?/, ''
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_inner_whitespaces(string)
|
17
|
+
string.each_line.inject("") do |memo, line|
|
18
|
+
memo + preserve_border_whitespaces(line) do
|
19
|
+
line.strip.gsub(/[ \t]{2,}/, ' ')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Find non-asterisk content that is enclosed by two or
|
25
|
+
# more asterisks. Ensure that only one whitespace occur
|
26
|
+
# in the border area.
|
27
|
+
# Same for underscores and brackets.
|
28
|
+
def clean_tag_borders(string)
|
29
|
+
result = string.gsub /\s?\*{2,}.*?\*{2,}\s?/ do |match|
|
30
|
+
preserve_border_whitespaces(match, default_border: ' ') do
|
31
|
+
match.strip.sub('** ', '**').sub(' **', '**')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
result = result.gsub /\s?\_{2,}.*?\_{2,}\s?/ do |match|
|
36
|
+
preserve_border_whitespaces(match, default_border: ' ') do
|
37
|
+
match.strip.sub('__ ', '__').sub(' __', '__')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
result.gsub /\s?\[.*?\]\s?/ do |match|
|
42
|
+
preserve_border_whitespaces(match) do
|
43
|
+
match.strip.sub('[ ', '[').sub(' ]', ']')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def preserve_border_whitespaces(string, default_border: '', &block)
|
51
|
+
string_start = present_or_default(string[/\A\s*/], default_border)
|
52
|
+
string_end = present_or_default(string[/\s*\Z/], default_border)
|
53
|
+
result = yield
|
54
|
+
string_start + result + string_end
|
55
|
+
end
|
56
|
+
|
57
|
+
def present_or_default(string, default)
|
58
|
+
if string.nil? || string.empty?
|
59
|
+
default
|
60
|
+
else
|
61
|
+
string
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
class Config
|
3
|
+
attr_accessor :ignore_unknown_tags, :github_flavored
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
reset
|
7
|
+
end
|
8
|
+
|
9
|
+
def apply(options = {})
|
10
|
+
options.each do |method, value|
|
11
|
+
__send__(:"#{method}=", value)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def reset
|
16
|
+
@ignore_unknown_tags = true
|
17
|
+
@github_flavored = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class A < Base
|
4
|
+
def convert(node)
|
5
|
+
name = treat_children(node)
|
6
|
+
href = node['href']
|
7
|
+
title = extract_title(node)
|
8
|
+
|
9
|
+
if href.to_s.start_with?('#') || href.to_s.empty? || name.empty?
|
10
|
+
name
|
11
|
+
else
|
12
|
+
" [#{name}](#{href}#{title})"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
register :a, A.new
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Base
|
4
|
+
def treat_children(node)
|
5
|
+
node.children.inject('') do |memo, child|
|
6
|
+
memo << treat(child)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def treat(node)
|
11
|
+
ReverseMarkdown::Converters.lookup(node.name).convert(node)
|
12
|
+
end
|
13
|
+
|
14
|
+
def escape_keychars(string)
|
15
|
+
string.gsub(/[\*\_]/, '*' => '\*', '_' => '\_')
|
16
|
+
end
|
17
|
+
|
18
|
+
def extract_title(node)
|
19
|
+
title = escape_keychars(node['title'].to_s)
|
20
|
+
title.empty? ? '' : %[ "#{title}"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Blockquote < Base
|
4
|
+
def convert(node)
|
5
|
+
content = treat_children(node).strip
|
6
|
+
content = ReverseMarkdown.cleaner.remove_newlines(content)
|
7
|
+
'> ' << content.lines.join('> ')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
register :blockquote, Blockquote.new
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Em < Base
|
4
|
+
def convert(node)
|
5
|
+
content = treat_children(node)
|
6
|
+
if content.strip.empty? || already_italic?(node)
|
7
|
+
content
|
8
|
+
else
|
9
|
+
"_#{content}_"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def already_italic?(node)
|
14
|
+
node.ancestors('italic').size > 0 || node.ancestors('em').size > 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
register :em, Em.new
|
19
|
+
register :i, Em.new
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class H < Base
|
4
|
+
def convert(node)
|
5
|
+
prefix = '#' * node.name[/\d/].to_i
|
6
|
+
["\n", prefix, ' ', treat_children(node), "\n"].join
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
register :h1, H.new
|
11
|
+
register :h2, H.new
|
12
|
+
register :h3, H.new
|
13
|
+
register :h4, H.new
|
14
|
+
register :h5, H.new
|
15
|
+
register :h6, H.new
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Li < Base
|
4
|
+
def convert(node)
|
5
|
+
content = treat_children(node)
|
6
|
+
indentation = indentation_for(node)
|
7
|
+
prefix = prefix_for(node)
|
8
|
+
"#{indentation}#{prefix}#{content}\n"
|
9
|
+
end
|
10
|
+
|
11
|
+
def prefix_for(node)
|
12
|
+
if node.parent.name == 'ol'
|
13
|
+
index = node.parent.xpath('li').index(node)
|
14
|
+
"#{index.to_i + 1}. "
|
15
|
+
else
|
16
|
+
'- '
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def indentation_for(node)
|
21
|
+
length = node.ancestors('ol').size + node.ancestors('ul').size
|
22
|
+
' ' * (length - 1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
register :li, Li.new
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class PassThrough < Base
|
4
|
+
def convert(node)
|
5
|
+
treat_children(node)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
register :document, PassThrough.new
|
10
|
+
register :html, PassThrough.new
|
11
|
+
register :body, PassThrough.new
|
12
|
+
register :span, PassThrough.new
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module ReverseMarkdown
|
4
|
+
module Converters
|
5
|
+
class Pre < Base
|
6
|
+
def convert(node)
|
7
|
+
if ReverseMarkdown.config.github_flavored
|
8
|
+
"```\n" << node.text.strip << "\n```\n"
|
9
|
+
else
|
10
|
+
"\n\n " << node.text.strip.lines.join(" ") << "\n\n"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
register :pre, Pre.new
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Strong < Base
|
4
|
+
def convert(node)
|
5
|
+
content = treat_children(node)
|
6
|
+
if content.strip.empty? || already_strong?(node)
|
7
|
+
content
|
8
|
+
else
|
9
|
+
"**#{content}**"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def already_strong?(node)
|
14
|
+
node.ancestors('strong').size > 0 || node.ancestors('b').size > 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
register :strong, Strong.new
|
19
|
+
register :b, Strong.new
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Text < Base
|
4
|
+
def convert(node, options = {})
|
5
|
+
if node.text.strip.empty?
|
6
|
+
treat_empty(node)
|
7
|
+
else
|
8
|
+
treat_text(node)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def treat_empty(node)
|
13
|
+
parent = node.parent.name.to_sym
|
14
|
+
if [:ol, :ul].include?(parent) # Otherwise the identation is broken
|
15
|
+
''
|
16
|
+
elsif node.text == ' ' # Regular whitespace text node
|
17
|
+
' '
|
18
|
+
else
|
19
|
+
''
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def treat_text(node)
|
24
|
+
escape_keychars node.text.tr("\n\t", '').squeeze(' ')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
register :text, Text.new
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ReverseMarkdown
|
2
|
+
module Converters
|
3
|
+
class Tr < Base
|
4
|
+
def convert(node)
|
5
|
+
content = treat_children(node).rstrip
|
6
|
+
result = "|#{content}\n"
|
7
|
+
table_header_row?(node) ? result + underline_for(node) : result
|
8
|
+
end
|
9
|
+
|
10
|
+
def table_header_row?(node)
|
11
|
+
node.element_children.all? {|child| child.name.to_sym == :th}
|
12
|
+
end
|
13
|
+
|
14
|
+
def underline_for(node)
|
15
|
+
"| " + (['---'] * node.element_children.size).join(' | ') + " |\n"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
register :tr, Tr.new
|
20
|
+
end
|
21
|
+
end
|