burr 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE.md +30 -0
- data/README.md +180 -0
- data/Rakefile +118 -0
- data/bin/burr +9 -0
- data/burr.gemspec +36 -0
- data/generators/Gemfile.txt +3 -0
- data/generators/config.yml +55 -0
- data/generators/contents/chapter1.md +7 -0
- data/generators/contents/chapter2.md +7 -0
- data/generators/stylesheets/pdf.css +569 -0
- data/generators/stylesheets/site.css +1 -0
- data/lib/burr.rb +56 -0
- data/lib/burr/book.rb +289 -0
- data/lib/burr/cli.rb +64 -0
- data/lib/burr/converter.rb +19 -0
- data/lib/burr/core_ext/blank.rb +107 -0
- data/lib/burr/dependency.rb +28 -0
- data/lib/burr/eeepub_ext/maker.rb +131 -0
- data/lib/burr/exporter.rb +137 -0
- data/lib/burr/exporters/epub.rb +163 -0
- data/lib/burr/exporters/pdf.rb +95 -0
- data/lib/burr/exporters/site.rb +101 -0
- data/lib/burr/generator.rb +41 -0
- data/lib/burr/kramdown_ext/converter.rb +145 -0
- data/lib/burr/kramdown_ext/options.rb +38 -0
- data/lib/burr/kramdown_ext/parser.rb +65 -0
- data/lib/burr/liquid_ext/block.rb +58 -0
- data/lib/burr/liquid_ext/extends.rb +114 -0
- data/lib/burr/plugin.rb +70 -0
- data/lib/burr/plugins/aside.rb +44 -0
- data/lib/burr/plugins/codeblock.rb +42 -0
- data/lib/burr/plugins/figure.rb +62 -0
- data/lib/burr/plugins/link.rb +47 -0
- data/lib/burr/plugins/parser_plugin.rb +18 -0
- data/lib/burr/plugins/table.rb +42 -0
- data/lib/burr/plugins/toc.rb +105 -0
- data/lib/burr/ruby_version_check.rb +4 -0
- data/lib/burr/server.rb +27 -0
- data/lib/burr/ui.rb +46 -0
- data/lib/burr/version.rb +8 -0
- data/resources/locales/labels/en.yml +45 -0
- data/resources/locales/labels/zh_CN.yml +46 -0
- data/resources/locales/titles/en.yml +21 -0
- data/resources/locales/titles/zh_CN.yml +21 -0
- data/resources/templates/epub/_layout.liquid +17 -0
- data/resources/templates/epub/acknowledgement.liquid +10 -0
- data/resources/templates/epub/afterword.liquid +10 -0
- data/resources/templates/epub/appendix.liquid +10 -0
- data/resources/templates/epub/author.liquid +10 -0
- data/resources/templates/epub/chapter.liquid +10 -0
- data/resources/templates/epub/conclusion.liquid +10 -0
- data/resources/templates/epub/cover.liquid +9 -0
- data/resources/templates/epub/dedication.liquid +10 -0
- data/resources/templates/epub/edition.liquid +1 -0
- data/resources/templates/epub/epilogue.liquid +1 -0
- data/resources/templates/epub/foreword.liquid +10 -0
- data/resources/templates/epub/glossary.liquid +1 -0
- data/resources/templates/epub/introduction.liquid +1 -0
- data/resources/templates/epub/license.liquid +1 -0
- data/resources/templates/epub/lof.liquid +24 -0
- data/resources/templates/epub/lot.liquid +24 -0
- data/resources/templates/epub/part.liquid +4 -0
- data/resources/templates/epub/preface.liquid +10 -0
- data/resources/templates/epub/prologue.liquid +1 -0
- data/resources/templates/epub/table.liquid +7 -0
- data/resources/templates/epub/title.liquid +3 -0
- data/resources/templates/epub/toc.liquid +10 -0
- data/resources/templates/pdf/_item.liquid +6 -0
- data/resources/templates/pdf/acknowledgement.liquid +1 -0
- data/resources/templates/pdf/afterword.liquid +1 -0
- data/resources/templates/pdf/appendix.liquid +4 -0
- data/resources/templates/pdf/author.liquid +1 -0
- data/resources/templates/pdf/blank.liquid +3 -0
- data/resources/templates/pdf/book.liquid +42 -0
- data/resources/templates/pdf/chapter.liquid +4 -0
- data/resources/templates/pdf/code.liquid +3 -0
- data/resources/templates/pdf/conclusion.liquid +1 -0
- data/resources/templates/pdf/cover.liquid +6 -0
- data/resources/templates/pdf/dedication.liquid +3 -0
- data/resources/templates/pdf/edition.liquid +1 -0
- data/resources/templates/pdf/epilogue.liquid +1 -0
- data/resources/templates/pdf/foreword.liquid +1 -0
- data/resources/templates/pdf/glossary.liquid +1 -0
- data/resources/templates/pdf/introduction.liquid +1 -0
- data/resources/templates/pdf/license.liquid +1 -0
- data/resources/templates/pdf/lof.liquid +24 -0
- data/resources/templates/pdf/lot.liquid +24 -0
- data/resources/templates/pdf/part.liquid +4 -0
- data/resources/templates/pdf/preface.liquid +1 -0
- data/resources/templates/pdf/prologue.liquid +1 -0
- data/resources/templates/pdf/table.liquid +7 -0
- data/resources/templates/pdf/title.liquid +3 -0
- data/resources/templates/pdf/toc.liquid +4 -0
- data/resources/templates/site/_layout.liquid +27 -0
- data/resources/templates/site/author.liquid +13 -0
- data/resources/templates/site/chapter.liquid +13 -0
- data/resources/templates/site/foreword.liquid +13 -0
- data/resources/templates/site/preface.liquid +13 -0
- metadata +232 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
module Burr
|
2
|
+
class PDF < Exporter
|
3
|
+
|
4
|
+
# Convert original contents into HTML
|
5
|
+
#
|
6
|
+
def parse_contents
|
7
|
+
parsed_items = []
|
8
|
+
self.book.items.each do |item|
|
9
|
+
self.book.current_item = item
|
10
|
+
|
11
|
+
self.run_plugins_of_type(:before_parse)
|
12
|
+
|
13
|
+
unless item['skip']
|
14
|
+
item['content'] = Burr::Converter.new(self.book).convert(item['original'])
|
15
|
+
end
|
16
|
+
|
17
|
+
self.run_plugins_of_type(:after_parse)
|
18
|
+
|
19
|
+
parsed_items << self.book.current_item
|
20
|
+
end
|
21
|
+
self.book.items = parsed_items
|
22
|
+
end
|
23
|
+
|
24
|
+
# Decorate the contents with template
|
25
|
+
#
|
26
|
+
def decorate_contents
|
27
|
+
|
28
|
+
decorated_items = []
|
29
|
+
|
30
|
+
self.book.items.each do |item|
|
31
|
+
self.book.current_item = item
|
32
|
+
|
33
|
+
self.run_plugins_of_type(:before_decorate)
|
34
|
+
|
35
|
+
item['content'] = self.book.render(self.book.template_for(item['element']), { 'item' => item, 'toc' => toc_html })
|
36
|
+
|
37
|
+
self.run_plugins_of_type(:after_decorate)
|
38
|
+
|
39
|
+
decorated_items << self.book.current_item
|
40
|
+
end
|
41
|
+
|
42
|
+
self.book.items = decorated_items
|
43
|
+
end
|
44
|
+
|
45
|
+
def assemble_book
|
46
|
+
base = File.join(self.book.outputs_dir, 'pdf')
|
47
|
+
html_path = File.join(base, "#{self.book.slug}.html")
|
48
|
+
pdf_path = File.join(base, "#{self.book.slug}-#{Time.new.strftime('%Y%m%d')}.pdf")
|
49
|
+
|
50
|
+
html = self.book.render(self.book.template_for('book'), {
|
51
|
+
'frontmatter' => self.frontmatter,
|
52
|
+
'bodymatter' => self.bodymatter,
|
53
|
+
'backmatter' => self.backmatter
|
54
|
+
})
|
55
|
+
File.open(html_path, 'w') { |f| f.puts html }
|
56
|
+
|
57
|
+
system "prince #{html_path} -o #{pdf_path}"
|
58
|
+
|
59
|
+
File.unlink html_path
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def toc_html
|
65
|
+
html = '<ol class="toc-list">'
|
66
|
+
self.book.items.each do |item|
|
67
|
+
# editions define the *tocable* items
|
68
|
+
if self.book.config['formats']['pdf']['toc']['elements'].include?(item['element'])
|
69
|
+
# item has several elements in its toc
|
70
|
+
if item['toc'].size > 0
|
71
|
+
item['toc'].each do |entry|
|
72
|
+
if entry['level'] <= self.book.config['formats'][self.book.format]['toc']['deep']
|
73
|
+
html << <<-LI1
|
74
|
+
<li class="#{ item['matter'] } #{ item['element'] } level-#{ entry['level'] }">
|
75
|
+
<a href="\##{ entry['id'] }">#{ entry['label'] } #{ entry['title'] }</a>
|
76
|
+
</li>
|
77
|
+
LI1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# empty or special item (anything different from 'chapter' and 'appendix')
|
83
|
+
elsif !%w(cover blank toc).include?(item['element'])
|
84
|
+
html << <<-LI2
|
85
|
+
<li class="#{ item['matter'] } #{ item['element'] } level-1">
|
86
|
+
<a href="\##{ item['id'] }">#{ item['label'] } #{ item['title'] }</a>
|
87
|
+
</li>
|
88
|
+
LI2
|
89
|
+
end
|
90
|
+
end
|
91
|
+
html << '</ol>'
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Burr
|
2
|
+
class Site < Exporter
|
3
|
+
|
4
|
+
# Convert original contents into HTML
|
5
|
+
#
|
6
|
+
def parse_contents
|
7
|
+
parsed_items = []
|
8
|
+
self.book.items.each do |item|
|
9
|
+
self.book.current_item = item
|
10
|
+
|
11
|
+
self.run_plugins_of_type(:before_parse)
|
12
|
+
|
13
|
+
unless item['skip']
|
14
|
+
item['content'] = Burr::Converter.new(self.book).convert(item['original'])
|
15
|
+
end
|
16
|
+
|
17
|
+
self.run_plugins_of_type(:after_parse)
|
18
|
+
|
19
|
+
parsed_items << self.book.current_item
|
20
|
+
end
|
21
|
+
self.book.items = parsed_items
|
22
|
+
end
|
23
|
+
|
24
|
+
# Decorate the contents with template
|
25
|
+
#
|
26
|
+
def decorate_contents
|
27
|
+
decorated_items = []
|
28
|
+
self.book.items.each do |item|
|
29
|
+
self.book.current_item = item
|
30
|
+
self.run_plugins_of_type(:before_decorate)
|
31
|
+
self.run_plugins_of_type(:after_decorate)
|
32
|
+
decorated_items << self.book.current_item
|
33
|
+
end
|
34
|
+
self.book.items = decorated_items
|
35
|
+
end
|
36
|
+
|
37
|
+
def assemble_book
|
38
|
+
[nil, *flatten_items(self.book.items), nil].each_cons(3) do |pre, item, nxt|
|
39
|
+
item['toc'] = item_toc_html(item['toc'])
|
40
|
+
|
41
|
+
unless pre.nil?
|
42
|
+
item['pre'] = ''
|
43
|
+
item['pre'] << "#{pre['label']}" unless pre['label'].blank?
|
44
|
+
item['pre'] << "#{pre['title']}"
|
45
|
+
item['pre_url'] = get_html_path_of(pre['element'], pre['file'])
|
46
|
+
end
|
47
|
+
|
48
|
+
unless nxt.nil?
|
49
|
+
item['nxt'] = ''
|
50
|
+
item['nxt'] << "#{nxt['label']}" unless nxt['label'].blank?
|
51
|
+
item['nxt'] << "#{nxt['title']}"
|
52
|
+
item['nxt_url'] = get_html_path_of(nxt['element'], nxt['file'])
|
53
|
+
end
|
54
|
+
|
55
|
+
File.open(get_html_path_of(item['element'], item['file'], false), 'w') do |f|
|
56
|
+
f.puts self.book.render(self.book.template_for(item['element']), { 'item' => item })
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Deletes item if item['element'] includes in %w(cover toc blank).
|
64
|
+
#
|
65
|
+
def flatten_items(items)
|
66
|
+
items.delete_if { |item| %w(cover toc blank).include?(item['element']) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Gets toc HTML for a single item.
|
70
|
+
#
|
71
|
+
def item_toc_html(toc)
|
72
|
+
# do not need the first element(level 1) in output
|
73
|
+
toc.shift
|
74
|
+
|
75
|
+
html = '<ol class="toc">'
|
76
|
+
toc.each do |entry|
|
77
|
+
next if entry['level'] - 1 > self.book.config['formats']['site']['toc']['deep']
|
78
|
+
html << <<-LI
|
79
|
+
<li class="level-#{ entry['level'] }">
|
80
|
+
<a href="\##{ entry['id'] }">#{ entry['label'] } #{ entry['title'] }</a>
|
81
|
+
</li>
|
82
|
+
LI
|
83
|
+
end
|
84
|
+
html << '</ol>'
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_html_path_of(element, path, relative = true)
|
88
|
+
base = File.join(self.book.outputs_dir, 'site')
|
89
|
+
basename = if path.blank?
|
90
|
+
element
|
91
|
+
else
|
92
|
+
path.split('.')[0..-2].join('.')
|
93
|
+
end
|
94
|
+
|
95
|
+
return "/#{ basename }.html" if relative
|
96
|
+
|
97
|
+
File.join(base, "#{ basename }.html")
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Burr
|
2
|
+
class Generator < Thor::Group
|
3
|
+
|
4
|
+
include Thor::Actions
|
5
|
+
|
6
|
+
def self.source_root
|
7
|
+
File.dirname(__FILE__) + '/../../generators'
|
8
|
+
end
|
9
|
+
|
10
|
+
def copy_gemfile
|
11
|
+
copy_file 'Gemfile.txt', 'Gemfile'
|
12
|
+
end
|
13
|
+
|
14
|
+
def copy_config_file
|
15
|
+
copy_file 'config.yml'
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_contents
|
19
|
+
directory 'contents'
|
20
|
+
end
|
21
|
+
|
22
|
+
def outputs_dir
|
23
|
+
empty_directory 'outputs/pdf'
|
24
|
+
empty_directory 'outputs/site'
|
25
|
+
empty_directory 'outputs/epub'
|
26
|
+
empty_directory 'outputs/mobi'
|
27
|
+
|
28
|
+
empty_directory 'outputs/site/figures'
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_stylesheets
|
32
|
+
copy_file 'stylesheets/pdf.css', 'outputs/pdf/style.css'
|
33
|
+
copy_file 'stylesheets/site.css', 'outputs/site/style.css'
|
34
|
+
end
|
35
|
+
|
36
|
+
def caches_dir
|
37
|
+
empty_directory 'caches/code'
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'burr/kramdown_ext/parser'
|
2
|
+
require 'pygments'
|
3
|
+
|
4
|
+
module Kramdown
|
5
|
+
module Converter
|
6
|
+
class Bshtml < Html
|
7
|
+
|
8
|
+
attr_accessor :book
|
9
|
+
|
10
|
+
def initialize(root, options)
|
11
|
+
super
|
12
|
+
@book = options[:register]
|
13
|
+
end
|
14
|
+
|
15
|
+
# Converts paragraph contents image with caption and normal paragraph.
|
16
|
+
#
|
17
|
+
def convert_p(el, indent)
|
18
|
+
if el.children.size == 1 && el.children.first.type == :img && !el.children.first.attr['caption'].nil?
|
19
|
+
convert_image_with_caption(el, indent)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Converts the codeblock to HTML, using pygments to highlight.
|
26
|
+
#
|
27
|
+
def convert_codeblock(el, indent)
|
28
|
+
attr = el.attr.dup
|
29
|
+
lang = extract_code_language!(attr)
|
30
|
+
lang = 'text' if lang.blank?
|
31
|
+
|
32
|
+
pyg_opts = {
|
33
|
+
:encoding => 'utf-8',
|
34
|
+
:cssclass => "highlight type-#{lang}"
|
35
|
+
}
|
36
|
+
caption = attr['caption']
|
37
|
+
file = attr['file']
|
38
|
+
code = ::Pygments.highlight(el.value, :lexer => lang, :options => pyg_opts).chomp << "\n"
|
39
|
+
output = '<div class="codeblock'
|
40
|
+
output << ' has-caption' if caption
|
41
|
+
output << '">'
|
42
|
+
if caption
|
43
|
+
caption_el = ::Kramdown::Parser::Bsmarkdown.parse(caption).first
|
44
|
+
caption_html = inner(caption_el.children.first, 0)
|
45
|
+
output << "<p class=\"caption\">#{caption_html}</p>"
|
46
|
+
end
|
47
|
+
output << "<p class=\"file\"><code>#{file}</code></p>" if file
|
48
|
+
output << "#{code}</div>"
|
49
|
+
end
|
50
|
+
|
51
|
+
# Converts headers
|
52
|
+
#
|
53
|
+
def convert_header(el, indent)
|
54
|
+
attr = el.attr.dup
|
55
|
+
item = self.book.current_item
|
56
|
+
if @options[:auto_ids] && !attr['id']
|
57
|
+
attr['id'] = generate_id(el.options[:raw_text])
|
58
|
+
end
|
59
|
+
#@toc << [el.options[:level], attr['id'], el.children] if attr['id'] && in_toc?(el)
|
60
|
+
unless attr['class'] == 'skip-toc'
|
61
|
+
item['toc'] << {
|
62
|
+
'level' => el.options[:level],
|
63
|
+
'title' => el.options[:raw_text]
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
level = output_header_level(el.options[:level])
|
68
|
+
format_as_block_html("h#{level}", attr, inner(el, indent), indent)
|
69
|
+
end
|
70
|
+
|
71
|
+
def convert_footnote(el, indent)
|
72
|
+
if self.book.format == 'pdf'
|
73
|
+
inline = format_as_span_html('span', { 'class'=> 'footnote', 'id' => "fn-#{ el.options[:name] }"}, inner(el.value, 0))
|
74
|
+
inline.sub!(/\s*<p>/, '').sub!(/<\/p>\n/, '')
|
75
|
+
else
|
76
|
+
number = @footnote_counter
|
77
|
+
@footnote_counter += 1
|
78
|
+
@footnotes << [el.options[:name], el.value]
|
79
|
+
"<sup class=\"footnote\" id=\"fnref-#{el.options[:name]}\"><a href=\"#fn-#{el.options[:name]}\" rel=\"footnote\">#{number}</a></sup>"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
::Kramdown::Parser::Bsmarkdown::BOXES.each do |box|
|
84
|
+
define_method("convert_#{box}_box") { |el, indent| <<-EOF }
|
85
|
+
#{' '*indent}<div class="#{box} box">\n#{inner(el, indent)}#{' '*indent}</div>\n
|
86
|
+
EOF
|
87
|
+
end
|
88
|
+
|
89
|
+
alias :orin_convert_table :convert_table
|
90
|
+
|
91
|
+
alias :convert_thead :orin_convert_table
|
92
|
+
alias :convert_tbody :orin_convert_table
|
93
|
+
alias :convert_tfoot :orin_convert_table
|
94
|
+
alias :convert_tr :orin_convert_table
|
95
|
+
|
96
|
+
def convert_table(el, indent)
|
97
|
+
caption = el.attr.delete('caption')
|
98
|
+
output = '<div class="table'
|
99
|
+
if caption
|
100
|
+
caption_el = ::Kramdown::Parser::Bsmarkdown.parse(caption).first
|
101
|
+
caption_html = inner(caption_el.children.first, 0)
|
102
|
+
output << " has-caption\"><p class=\"caption\">#{caption_html}</p>"
|
103
|
+
else
|
104
|
+
output << '">'
|
105
|
+
end
|
106
|
+
output << format_as_indented_block_html(el.type, el.attr, inner(el, indent), indent)
|
107
|
+
output << '</div>'
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return a HTML ordered list with the footnote content for the used footnotes.
|
111
|
+
def footnote_content
|
112
|
+
ol = Element.new(:ol)
|
113
|
+
ol.attr['start'] = @footnote_start if @footnote_start != 1
|
114
|
+
@footnotes.each do |name, data|
|
115
|
+
li = Element.new(:li, nil, {'id' => "fn-#{name}"})
|
116
|
+
li.children = Marshal.load(Marshal.dump(data.children))
|
117
|
+
ol.children << li
|
118
|
+
|
119
|
+
ref = Element.new(:raw, "<a href=\"#fnref-#{name}\" rel=\"reference\">↩</a>")
|
120
|
+
if li.children.last.type == :p
|
121
|
+
para = li.children.last
|
122
|
+
else
|
123
|
+
li.children << (para = Element.new(:p))
|
124
|
+
end
|
125
|
+
para.children << ref
|
126
|
+
end
|
127
|
+
(ol.children.empty? ? '' : format_as_indented_block_html('div', {:class => "footnotes"}, convert(ol, 2), 0))
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def convert_image_with_caption(el, indent)
|
133
|
+
img_el = el.children.first
|
134
|
+
caption = img_el.attr.delete('caption')
|
135
|
+
img_html = convert_img(img_el, indent)
|
136
|
+
|
137
|
+
caption_el = ::Kramdown::Parser::Bsmarkdown.parse(caption).first
|
138
|
+
caption_html = inner(caption_el.children.first, 0)
|
139
|
+
|
140
|
+
"<div class=\"figure\">#{img_html}<p class=\"caption\">#{caption_html}</p></div>"
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Kramdown
|
2
|
+
module Options
|
3
|
+
|
4
|
+
# Parse the given value +data+ as if it was a value for the option +name+ and return the parsed
|
5
|
+
# value with the correct type.
|
6
|
+
#
|
7
|
+
# If +data+ already has the correct type, it is just returned. Otherwise it is converted to a
|
8
|
+
# String and then to the correct type.
|
9
|
+
def self.parse(name, data)
|
10
|
+
raise ArgumentError, "No option named #{name} defined" if !@options.has_key?(name)
|
11
|
+
if !(@options[name].type === data)
|
12
|
+
data = data.to_s
|
13
|
+
data = if @options[name].type == String
|
14
|
+
data
|
15
|
+
elsif @options[name].type == Integer
|
16
|
+
Integer(data) rescue raise Kramdown::Error, "Invalid integer value for option '#{name}': '#{data}'"
|
17
|
+
elsif @options[name].type == Float
|
18
|
+
Float(data) rescue raise Kramdown::Error, "Invalid float value for option '#{name}': '#{data}'"
|
19
|
+
elsif @options[name].type == Symbol
|
20
|
+
data.strip!
|
21
|
+
data = data[1..-1] if data[0] == ?:
|
22
|
+
(data.empty? || data == 'nil' ? nil : data.to_sym)
|
23
|
+
elsif @options[name].type == Boolean
|
24
|
+
data.downcase.strip != 'false' && !data.empty?
|
25
|
+
else
|
26
|
+
data
|
27
|
+
end
|
28
|
+
end
|
29
|
+
data = @options[name].validator[data] if @options[name].validator
|
30
|
+
data
|
31
|
+
end
|
32
|
+
|
33
|
+
define(:register, Object, nil, 'Pass in another object.') do |r|
|
34
|
+
r
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Kramdown
|
2
|
+
module Parser
|
3
|
+
class Bsmarkdown < Kramdown
|
4
|
+
|
5
|
+
BOXES = %w(aside discussion error information question tip warning)
|
6
|
+
|
7
|
+
def initialize(source, options)
|
8
|
+
super
|
9
|
+
@block_parsers.unshift(:gfm_codeblock_fenced, *BOXES.map{ |b| :"#{b}_box" })
|
10
|
+
end
|
11
|
+
|
12
|
+
GFM_FENCED_CODEBLOCK_START = /^`{3,}/
|
13
|
+
GFM_FENCED_CODEBLOCK_MATCH = /^(`{3,})\s*?(\w+)?\s*?\n(.*?)^\1`*\s*?\n/m
|
14
|
+
|
15
|
+
# Parser the GitHub Flavored Markdown fenced code block.
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# ```ruby
|
20
|
+
# def hello
|
21
|
+
# puts 'Hello'
|
22
|
+
# end
|
23
|
+
# ```
|
24
|
+
#
|
25
|
+
def parse_gfm_codeblock_fenced
|
26
|
+
if @src.check(GFM_FENCED_CODEBLOCK_MATCH)
|
27
|
+
@src.pos += @src.matched_size
|
28
|
+
el = new_block_el(:codeblock, @src[3])
|
29
|
+
lang = @src[2].to_s.strip
|
30
|
+
el.attr['class'] = "language-#{lang}" unless lang.empty?
|
31
|
+
@tree.children << el
|
32
|
+
true
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
define_parser(:gfm_codeblock_fenced, GFM_FENCED_CODEBLOCK_START)
|
38
|
+
|
39
|
+
ASIDE_BOX_START = /^#{OPT_SPACE}A> ?/u
|
40
|
+
DISCUSSION_BOX_START = /^#{OPT_SPACE}D> ?/
|
41
|
+
ERROR_BOX_START = /^#{OPT_SPACE}E> ?/
|
42
|
+
INFORMATION_BOX_START = /^#{OPT_SPACE}I> ?/
|
43
|
+
QUESTION_BOX_START = /^#{OPT_SPACE}Q> ?/
|
44
|
+
TIP_BOX_START = /^#{OPT_SPACE}T> ?/
|
45
|
+
WARNING_BOX_START = /^#{OPT_SPACE}W> ?/
|
46
|
+
|
47
|
+
BOXES.each do |box|
|
48
|
+
define_method("parse_#{box}_box") do
|
49
|
+
result = @src.scan(PARAGRAPH_MATCH)
|
50
|
+
while !@src.match?(self.class::LAZY_END)
|
51
|
+
result << @src.scan(PARAGRAPH_MATCH)
|
52
|
+
end
|
53
|
+
result.gsub!(self.class.const_get("#{box.upcase}_BOX_START"), '')
|
54
|
+
|
55
|
+
el = new_block_el(:"#{box}_box")
|
56
|
+
@tree.children << el
|
57
|
+
parse_blocks(el, result)
|
58
|
+
true
|
59
|
+
end
|
60
|
+
define_parser(:"#{box}_box", self.const_get("#{box.upcase}_BOX_START"))
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|