burr 0.0.2

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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +30 -0
  6. data/README.md +180 -0
  7. data/Rakefile +118 -0
  8. data/bin/burr +9 -0
  9. data/burr.gemspec +36 -0
  10. data/generators/Gemfile.txt +3 -0
  11. data/generators/config.yml +55 -0
  12. data/generators/contents/chapter1.md +7 -0
  13. data/generators/contents/chapter2.md +7 -0
  14. data/generators/stylesheets/pdf.css +569 -0
  15. data/generators/stylesheets/site.css +1 -0
  16. data/lib/burr.rb +56 -0
  17. data/lib/burr/book.rb +289 -0
  18. data/lib/burr/cli.rb +64 -0
  19. data/lib/burr/converter.rb +19 -0
  20. data/lib/burr/core_ext/blank.rb +107 -0
  21. data/lib/burr/dependency.rb +28 -0
  22. data/lib/burr/eeepub_ext/maker.rb +131 -0
  23. data/lib/burr/exporter.rb +137 -0
  24. data/lib/burr/exporters/epub.rb +163 -0
  25. data/lib/burr/exporters/pdf.rb +95 -0
  26. data/lib/burr/exporters/site.rb +101 -0
  27. data/lib/burr/generator.rb +41 -0
  28. data/lib/burr/kramdown_ext/converter.rb +145 -0
  29. data/lib/burr/kramdown_ext/options.rb +38 -0
  30. data/lib/burr/kramdown_ext/parser.rb +65 -0
  31. data/lib/burr/liquid_ext/block.rb +58 -0
  32. data/lib/burr/liquid_ext/extends.rb +114 -0
  33. data/lib/burr/plugin.rb +70 -0
  34. data/lib/burr/plugins/aside.rb +44 -0
  35. data/lib/burr/plugins/codeblock.rb +42 -0
  36. data/lib/burr/plugins/figure.rb +62 -0
  37. data/lib/burr/plugins/link.rb +47 -0
  38. data/lib/burr/plugins/parser_plugin.rb +18 -0
  39. data/lib/burr/plugins/table.rb +42 -0
  40. data/lib/burr/plugins/toc.rb +105 -0
  41. data/lib/burr/ruby_version_check.rb +4 -0
  42. data/lib/burr/server.rb +27 -0
  43. data/lib/burr/ui.rb +46 -0
  44. data/lib/burr/version.rb +8 -0
  45. data/resources/locales/labels/en.yml +45 -0
  46. data/resources/locales/labels/zh_CN.yml +46 -0
  47. data/resources/locales/titles/en.yml +21 -0
  48. data/resources/locales/titles/zh_CN.yml +21 -0
  49. data/resources/templates/epub/_layout.liquid +17 -0
  50. data/resources/templates/epub/acknowledgement.liquid +10 -0
  51. data/resources/templates/epub/afterword.liquid +10 -0
  52. data/resources/templates/epub/appendix.liquid +10 -0
  53. data/resources/templates/epub/author.liquid +10 -0
  54. data/resources/templates/epub/chapter.liquid +10 -0
  55. data/resources/templates/epub/conclusion.liquid +10 -0
  56. data/resources/templates/epub/cover.liquid +9 -0
  57. data/resources/templates/epub/dedication.liquid +10 -0
  58. data/resources/templates/epub/edition.liquid +1 -0
  59. data/resources/templates/epub/epilogue.liquid +1 -0
  60. data/resources/templates/epub/foreword.liquid +10 -0
  61. data/resources/templates/epub/glossary.liquid +1 -0
  62. data/resources/templates/epub/introduction.liquid +1 -0
  63. data/resources/templates/epub/license.liquid +1 -0
  64. data/resources/templates/epub/lof.liquid +24 -0
  65. data/resources/templates/epub/lot.liquid +24 -0
  66. data/resources/templates/epub/part.liquid +4 -0
  67. data/resources/templates/epub/preface.liquid +10 -0
  68. data/resources/templates/epub/prologue.liquid +1 -0
  69. data/resources/templates/epub/table.liquid +7 -0
  70. data/resources/templates/epub/title.liquid +3 -0
  71. data/resources/templates/epub/toc.liquid +10 -0
  72. data/resources/templates/pdf/_item.liquid +6 -0
  73. data/resources/templates/pdf/acknowledgement.liquid +1 -0
  74. data/resources/templates/pdf/afterword.liquid +1 -0
  75. data/resources/templates/pdf/appendix.liquid +4 -0
  76. data/resources/templates/pdf/author.liquid +1 -0
  77. data/resources/templates/pdf/blank.liquid +3 -0
  78. data/resources/templates/pdf/book.liquid +42 -0
  79. data/resources/templates/pdf/chapter.liquid +4 -0
  80. data/resources/templates/pdf/code.liquid +3 -0
  81. data/resources/templates/pdf/conclusion.liquid +1 -0
  82. data/resources/templates/pdf/cover.liquid +6 -0
  83. data/resources/templates/pdf/dedication.liquid +3 -0
  84. data/resources/templates/pdf/edition.liquid +1 -0
  85. data/resources/templates/pdf/epilogue.liquid +1 -0
  86. data/resources/templates/pdf/foreword.liquid +1 -0
  87. data/resources/templates/pdf/glossary.liquid +1 -0
  88. data/resources/templates/pdf/introduction.liquid +1 -0
  89. data/resources/templates/pdf/license.liquid +1 -0
  90. data/resources/templates/pdf/lof.liquid +24 -0
  91. data/resources/templates/pdf/lot.liquid +24 -0
  92. data/resources/templates/pdf/part.liquid +4 -0
  93. data/resources/templates/pdf/preface.liquid +1 -0
  94. data/resources/templates/pdf/prologue.liquid +1 -0
  95. data/resources/templates/pdf/table.liquid +7 -0
  96. data/resources/templates/pdf/title.liquid +3 -0
  97. data/resources/templates/pdf/toc.liquid +4 -0
  98. data/resources/templates/site/_layout.liquid +27 -0
  99. data/resources/templates/site/author.liquid +13 -0
  100. data/resources/templates/site/chapter.liquid +13 -0
  101. data/resources/templates/site/foreword.liquid +13 -0
  102. data/resources/templates/site/preface.liquid +13 -0
  103. metadata +232 -0
@@ -0,0 +1,58 @@
1
+ # steal from https://github.com/silas/liquid-blocks
2
+
3
+ module Burr
4
+ class BlockDrop < ::Liquid::Drop
5
+ def initialize(block)
6
+ @block = block
7
+ end
8
+
9
+ def super
10
+ @block.call_super(@context)
11
+ end
12
+ end
13
+
14
+ class BlockTag < ::Liquid::Block
15
+ Syntax = /(\w)+/
16
+
17
+ attr_accessor :parent
18
+ attr_reader :name
19
+
20
+ def initialize(tag_name, markup, tokens)
21
+ if markup =~ Syntax
22
+ @name = $1
23
+ else
24
+ raise Liquid::SyntaxError.new("Syntax Error in 'block' - Valid syntax: block [name]")
25
+ end
26
+
27
+ super if tokens
28
+ end
29
+
30
+ def render(context)
31
+ context.stack do
32
+ context['block'] = BlockDrop.new(self)
33
+
34
+ render_all(@nodelist, context)
35
+ end
36
+ end
37
+
38
+ def add_parent(nodelist)
39
+ if parent
40
+ parent.add_parent(nodelist)
41
+ else
42
+ self.parent = BlockTag.new(@tag_name, @name, nil)
43
+ parent.nodelist = nodelist
44
+ end
45
+ end
46
+
47
+ def call_super(context)
48
+ if parent
49
+ parent.render(context)
50
+ else
51
+ ''
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+
58
+ Liquid::Template.register_tag(:block, Burr::BlockTag)
@@ -0,0 +1,114 @@
1
+ # steal from https://github.com/silas/liquid-blocks
2
+
3
+ module Burr
4
+ class ExtendsTag < ::Liquid::Block
5
+ Syntax = /(#{Liquid::QuotedFragment}+)/
6
+
7
+ def initialize(tag_name, markup, tokens)
8
+ if markup =~ Syntax
9
+ @template_name = $1
10
+ else
11
+ raise Liquid::SyntaxError.new("Syntax Error in 'extends' - Valid syntax: extends [template]")
12
+ end
13
+
14
+ super
15
+
16
+ @blocks = @nodelist.inject({}) do |m, node|
17
+ m[node.name] = node if node.is_a?(Burr::BlockTag); m
18
+ end
19
+ end
20
+
21
+ def parse(tokens)
22
+ parse_all(tokens)
23
+ end
24
+
25
+ def render(context)
26
+ template = load_template(context)
27
+ parent_blocks = find_blocks(template.root)
28
+
29
+ @blocks.each do |name, block|
30
+ if pb = parent_blocks[name]
31
+ pb.parent = block.parent
32
+ pb.add_parent(pb.nodelist)
33
+ pb.nodelist = block.nodelist
34
+ else
35
+ if is_extending?(template)
36
+ template.root.nodelist << block
37
+ end
38
+ end
39
+ end
40
+
41
+ template.render(context)
42
+ end
43
+
44
+ private
45
+
46
+ def parse_all(tokens)
47
+ @nodelist ||= []
48
+ @nodelist.clear
49
+
50
+ while token = tokens.shift
51
+ case token
52
+ when /^#{Liquid::TagStart}/
53
+ if token =~ /^#{Liquid::TagStart}\s*(\w+)\s*(.*)?#{Liquid::TagEnd}$/
54
+ # fetch the tag from registered blocks
55
+ if tag = Liquid::Template.tags[$1]
56
+ @nodelist << tag.new($1, $2, tokens)
57
+ else
58
+ # this tag is not registered with the system
59
+ # pass it to the current block for special handling or error reporting
60
+ unknown_tag($1, $2, tokens)
61
+ end
62
+ else
63
+ raise Liquid::SyntaxError, "Tag '#{token}' was not properly terminated with regexp: #{Liquid::TagEnd.inspect}"
64
+ end
65
+ when /^#{Liquid::VariableStart}/
66
+ @nodelist << create_variable(token)
67
+ when ''
68
+ # pass
69
+ else
70
+ @nodelist << token
71
+ end
72
+ end
73
+ end
74
+
75
+ def load_template(context)
76
+ base = File.join('templates', context.registers[:book].format, "_#{ @template_name[1..-2] }.liquid")
77
+ default = File.join(context.registers[:book].gem_dir, 'resources', base)
78
+ custom = File.join(context.registers[:book].root_dir, base)
79
+
80
+ if File.exist?(custom)
81
+ path = custom
82
+ elsif !File.exist?(custom) && File.exist?(default)
83
+ path = default
84
+ else
85
+ raise "#{ @template_name[1..-2] } missing!"
86
+ end
87
+
88
+ Liquid::Template.parse(File.read(path))
89
+ end
90
+
91
+ def find_blocks(node, blocks={})
92
+ if node.respond_to?(:nodelist) && !node.nodelist.nil?
93
+ node.nodelist.inject(blocks) do |b, node|
94
+ if node.is_a?(Burr::BlockTag)
95
+ b[node.name] = node
96
+ else
97
+ find_blocks(node, b)
98
+ end
99
+
100
+ b
101
+ end
102
+ end
103
+
104
+ blocks
105
+ end
106
+
107
+ def is_extending?(template)
108
+ template.root.nodelist.any? { |node| node.is_a?(ExtendsTag) }
109
+ end
110
+
111
+ end
112
+ end
113
+
114
+ Liquid::Template.register_tag(:extends, Burr::ExtendsTag)
@@ -0,0 +1,70 @@
1
+ module Burr
2
+
3
+ # steal from jekyll: https://github.com/mojombo/jekyll/blob/master/lib/jekyll/plugin.rb
4
+ class Plugin
5
+
6
+ PRIORITIES = {
7
+ :lowest => -100,
8
+ :low => -10,
9
+ :normal => 0,
10
+ :high => 10,
11
+ :highest => 100
12
+ }
13
+
14
+ VALIDS = [:before_parse, :after_parse, :before_decorate, :after_decorate]
15
+
16
+ class << self
17
+
18
+ # Install a hook so that subclasses are recorded. This method is only
19
+ # ever called by Ruby itself.
20
+ def inherited(base)
21
+ subclasses << base
22
+ subclasses.sort!
23
+ end
24
+
25
+ # The list of Classes that have been subclassed.
26
+ #
27
+ # @return An array of Class objects.
28
+ def subclasses
29
+ @subclasses ||= []
30
+ end
31
+
32
+ # Get or set the priority of this plugin. When called without an
33
+ # argument it returns the priority. When an argument is given, it will
34
+ # set the priority.
35
+ #
36
+ # @param [Symbol] priority The priority (default: nil). Valid options are:
37
+ # :lowest, :low, :normal, :high, :highest
38
+ # @return The Symbol priority.
39
+ def priority(priority=nil)
40
+ @priority ||= nil
41
+ if priority && PRIORITIES.has_key?(priority)
42
+ @priority = priority
43
+ end
44
+ @priority || :normal
45
+ end
46
+
47
+ # Spaceship is priority [higher -> lower]
48
+ #
49
+ # other - The class to be compared.
50
+ #
51
+ # Returns -1, 0, 1.
52
+ def <=>(other)
53
+ PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
54
+ end
55
+
56
+ end
57
+
58
+ attr_accessor :book
59
+
60
+ # Initialize a new plugin. This should be overridden by the subclass.
61
+ #
62
+ # book - The book object.
63
+ #
64
+ # Returns a new instance.
65
+ def initialize(book)
66
+ @book = book
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,44 @@
1
+ module Burr
2
+ class AsidePlugin < Plugin
3
+
4
+ # Do something after parsed the item content.
5
+ def after_parse
6
+ add_label
7
+ end
8
+
9
+ private
10
+
11
+ # Adds label and id to Aside block.
12
+ #
13
+ def add_label
14
+ item = self.book.current_item
15
+ return unless self.book.config['formats']["#{self.book.format}"]['label']['elements'].include?('aside')
16
+
17
+ counter = 1
18
+ number = if item['number']
19
+ item['number']
20
+ else
21
+ item['element']
22
+ end
23
+
24
+ dom = ::Nokogiri::HTML::DocumentFragment.parse(item['content'])
25
+ dom.css('div.aside').each do |aside|
26
+ # add id
27
+ aside['id'] = "aside-#{number}-#{counter}"
28
+
29
+ # add label
30
+ caption = aside.css('h4').first
31
+ unless caption.blank?
32
+ label = self.book.render_label('aside', { 'item' => { 'number' => number, 'counter' => counter } })
33
+ caption_html = "<span>#{label}</span>#{caption.inner_html}"
34
+ caption.children = ::Nokogiri::HTML.fragment(caption_html, 'utf-8')
35
+ end
36
+
37
+ counter += 1
38
+ end
39
+
40
+ item['content'] = dom.to_xhtml
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ module Burr
2
+ class CodeblockPlugin < Plugin
3
+
4
+ # Do something after parsed the item content.
5
+ def after_parse
6
+ add_label
7
+ end
8
+
9
+ private
10
+
11
+ # Adds label and id to code block.
12
+ #
13
+ def add_label
14
+ item = self.book.current_item
15
+ return unless self.book.config['formats']["#{self.book.format}"]['label']['elements'].include?('codeblock')
16
+
17
+ counter = 1
18
+ number = if item['number']
19
+ item['number']
20
+ else
21
+ item['element']
22
+ end
23
+
24
+ dom = ::Nokogiri::HTML::DocumentFragment.parse(item['content'])
25
+ dom.css('.codeblock.has-caption').each do |codeblock|
26
+ # add id
27
+ codeblock.set_attribute('id', "codeblock-#{number}-#{counter}")
28
+
29
+ # add label
30
+ caption = codeblock.css('.caption').first
31
+ label = self.book.render_label('codeblock', { 'item' => { 'number' => number, 'counter' => counter } })
32
+ caption_html = "<span>#{label}</span>#{caption.inner_html}"
33
+ caption.children = ::Nokogiri::HTML.fragment(caption_html, 'utf-8')
34
+
35
+ counter += 1
36
+ end
37
+
38
+ item['content'] = dom.to_xhtml
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,62 @@
1
+ module Burr
2
+ class FigurePlugin < Plugin
3
+
4
+ # Do something after parsed the item content.
5
+ def after_parse
6
+ fix_src_for_pdf if self.book.format == 'pdf'
7
+
8
+ add_label
9
+ end
10
+
11
+ private
12
+
13
+ # Replaces figure src for pdf.
14
+ #
15
+ # 'figures/sample.png' => '<root_dir>/figures/sample.png'
16
+ #
17
+ def fix_src_for_pdf
18
+ item = self.book.current_item
19
+ prefix = File.join(self.book.outputs_dir, 'site')
20
+ dom = ::Nokogiri::HTML::DocumentFragment.parse(item['content'])
21
+ dom.search('img').each do |img|
22
+ ori_src = img.get_attribute('src')
23
+ new_src = File.join(prefix, ori_src)
24
+ img.set_attribute('src', new_src)
25
+ end
26
+ item['content'] = dom.to_xhtml
27
+
28
+ nil
29
+ end
30
+
31
+ # Adds label and id to figure block.
32
+ #
33
+ def add_label
34
+ item = self.book.current_item
35
+ return unless self.book.config['formats']["#{self.book.format}"]['label']['elements'].include?('figure')
36
+
37
+ counter = 1
38
+ number = if item['number']
39
+ item['number']
40
+ else
41
+ item['element']
42
+ end
43
+
44
+ dom = ::Nokogiri::HTML::DocumentFragment.parse(item['content'])
45
+ dom.css('div.figure').each do |figure|
46
+ # add id
47
+ figure.set_attribute('id', "figure-#{number}-#{counter}")
48
+
49
+ # add label
50
+ caption = figure.css('.caption').first
51
+ label = self.book.render_label('figure', { 'item' => { 'number' => number, 'counter' => counter } })
52
+ caption_html = "<span>#{label}</span>#{caption.inner_html}"
53
+ caption.children = ::Nokogiri::HTML.fragment(caption_html, 'utf-8')
54
+
55
+ counter += 1
56
+ end
57
+
58
+ item['content'] = dom.to_xhtml
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,47 @@
1
+ module Burr
2
+ class LinkPlugin < Plugin
3
+
4
+ # Do something after parsed the item content.
5
+ def after_parse
6
+ extend_url if %w(site epub).include?(self.book.format)
7
+ end
8
+
9
+ private
10
+
11
+ # Extends cross link url.
12
+ #
13
+ def extend_url
14
+ item = self.book.current_item
15
+ dom = ::Nokogiri::HTML::DocumentFragment.parse(item['content'])
16
+ dom.css('a[href]').each do |link|
17
+ href = link['href']
18
+ next unless cross_link?(href)
19
+
20
+ parts = href.split('-')
21
+
22
+ next if parts[0].start_with?('#fn')
23
+
24
+ if href.start_with?('#chapter')
25
+ new_href = "chapter#{parts[1]}.html"
26
+ elsif href.start_with?('#section', '#figure', '#codeblock', '#table', '#aside')
27
+ new_href = "chapter#{parts[1]}.html#{parts.join('-')}"
28
+ else
29
+ new_href = "#{href[1..-1]}.html"
30
+ end
31
+
32
+ link['href'] = new_href
33
+ end
34
+
35
+ item['content'] = dom.to_xhtml
36
+
37
+ nil
38
+ end
39
+
40
+ def cross_link?(url)
41
+ return true if url.start_with?('#')
42
+
43
+ false
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,18 @@
1
+ module Burr
2
+ class ParserPlugin < Plugin
3
+
4
+ # Do something after parsed the item content.
5
+ def after_parse
6
+ #replace_br
7
+ end
8
+
9
+ private
10
+
11
+ # Replace <br> by <br/> (it causes problems for epub books)
12
+ def replace_br
13
+ item = self.book.current_item
14
+ item['content'].gsub!('<br>', '<br />')
15
+ end
16
+
17
+ end
18
+ end