kitabu 1.0.0.rc1 → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +14 -20
- data/README.rdoc +54 -23
- data/kitabu.gemspec +1 -1
- data/lib/kitabu.rb +11 -4
- data/lib/kitabu/cli.rb +49 -6
- data/lib/kitabu/dependency.rb +19 -0
- data/lib/kitabu/exporter.rb +4 -5
- data/lib/kitabu/generator.rb +12 -15
- data/lib/kitabu/parser.rb +27 -2
- data/lib/kitabu/parser/epub.rb +96 -83
- data/lib/kitabu/parser/html.rb +51 -16
- data/lib/kitabu/parser/mobi.rb +1 -4
- data/lib/kitabu/parser/pdf.rb +23 -20
- data/lib/kitabu/parser/txt.rb +1 -5
- data/lib/kitabu/syntax.rb +5 -3
- data/lib/kitabu/syntax/highlight.rb +22 -0
- data/lib/kitabu/toc.rb +4 -78
- data/lib/kitabu/toc/epub.rb +41 -0
- data/lib/kitabu/toc/html.rb +78 -0
- data/lib/kitabu/version.rb +1 -1
- data/spec/kitabu/cli/export_spec.rb +2 -2
- data/spec/kitabu/cli/new_spec.rb +1 -1
- data/spec/kitabu/cli/permalinks_spec.rb +1 -1
- data/spec/kitabu/cli/version_spec.rb +1 -1
- data/spec/kitabu/extensions/redcloth_spec.rb +6 -6
- data/spec/kitabu/extensions/string_spec.rb +1 -1
- data/spec/kitabu/parser/epub_spec.rb +5 -1
- data/spec/kitabu/parser/html_spec.rb +15 -15
- data/spec/kitabu/parser/pdf_spec.rb +4 -4
- data/spec/kitabu/syntax_spec.rb +78 -74
- data/spec/kitabu/{toc_spec.rb → toc/html_spec.rb} +5 -5
- data/spec/spec_helper.rb +3 -1
- data/spec/support/mybook/output/mybook.pdf.html +83 -0
- data/spec/support/mybook/templates/{cover.erb → epub/cover.erb} +1 -1
- data/spec/support/mybook/templates/{epub.erb → epub/page.erb} +1 -1
- data/spec/support/mybook/templates/epub/style.css +0 -0
- data/spec/support/mybook/templates/html/layout.css +353 -0
- data/spec/support/mybook/templates/html/layout.erb +50 -0
- data/spec/support/mybook/templates/html/syntax.css +58 -0
- data/spec/support/mybook/templates/html/user.css +1 -0
- data/spec/support/shared.rb +48 -15
- data/templates/cover.erb +1 -1
- data/templates/cover.png +0 -0
- data/templates/epub.css +1 -0
- data/templates/epub.erb +1 -1
- data/templates/sample.md +6 -0
- data/templates/syntax.css +58 -0
- metadata +32 -44
- data/spec/support/mybook/templates/epub.css +0 -1
- data/spec/support/mybook/templates/layout.css +0 -137
- data/spec/support/mybook/templates/layout.erb +0 -46
- data/spec/support/mybook/templates/syntax.css +0 -186
- data/spec/support/mybook/templates/user.css +0 -1
- data/templates/styles/active4d.css +0 -114
- data/templates/styles/all_hallows_eve.css +0 -72
- data/templates/styles/amy.css +0 -147
- data/templates/styles/blackboard.css +0 -88
- data/templates/styles/brilliance_black.css +0 -605
- data/templates/styles/brilliance_dull.css +0 -599
- data/templates/styles/cobalt.css +0 -149
- data/templates/styles/dawn.css +0 -121
- data/templates/styles/eiffel.css +0 -121
- data/templates/styles/espresso_libre.css +0 -109
- data/templates/styles/idle.css +0 -62
- data/templates/styles/iplastic.css +0 -80
- data/templates/styles/lazy.css +0 -73
- data/templates/styles/mac_classic.css +0 -123
- data/templates/styles/magicwb_amiga.css +0 -104
- data/templates/styles/pastels_on_dark.css +0 -188
- data/templates/styles/slush_poppies.css +0 -85
- data/templates/styles/spacecadet.css +0 -51
- data/templates/styles/sunburst.css +0 -180
- data/templates/styles/twilight.css +0 -137
- data/templates/styles/zenburnesque.css +0 -91
@@ -0,0 +1,22 @@
|
|
1
|
+
module Kitabu
|
2
|
+
class Syntax
|
3
|
+
class Highlight
|
4
|
+
def self.apply(code, language)
|
5
|
+
if Dependency.pygments_rb?
|
6
|
+
pygments(code, language)
|
7
|
+
else
|
8
|
+
coderay(code, language)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
def self.pygments(code, language)
|
14
|
+
Pygments.highlight(code, :lexer => language, :options => {:encoding => "utf-8"})
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.coderay(code, language)
|
18
|
+
CodeRay.scan(code, language).div(:css => :class)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/kitabu/toc.rb
CHANGED
@@ -1,80 +1,6 @@
|
|
1
1
|
module Kitabu
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
attr_reader :toc
|
6
|
-
|
7
|
-
private_class_method :new
|
8
|
-
attr_reader :buffer # :nodoc:
|
9
|
-
attr_reader :attrs # :nodoc:
|
10
|
-
attr_accessor :content # :nodoc:
|
11
|
-
|
12
|
-
# Traverse every title and add a +id+ attribute.
|
13
|
-
# Return the modified content.
|
14
|
-
#
|
15
|
-
def self.normalize(content)
|
16
|
-
counter = {}
|
17
|
-
html = Nokogiri::HTML.parse(content)
|
18
|
-
html.search("h2, h3, h4, h5, h6").each do |tag|
|
19
|
-
title = tag.inner_text
|
20
|
-
permalink = title.to_permalink
|
21
|
-
|
22
|
-
counter[permalink] ||= 0
|
23
|
-
counter[permalink] += 1
|
24
|
-
|
25
|
-
permalink = "#{permalink}-#{counter[permalink]}" if counter[permalink] > 1
|
26
|
-
|
27
|
-
tag.set_attribute("id", permalink)
|
28
|
-
end
|
29
|
-
|
30
|
-
html = html.to_xhtml
|
31
|
-
html.force_encoding("UTF-8") if html.encoding_aware?
|
32
|
-
|
33
|
-
_, content = *html.match(/<body>(.*?)<\/body>/m)
|
34
|
-
content
|
35
|
-
end
|
36
|
-
|
37
|
-
# Traverse every title normalizing its content as a permalink.
|
38
|
-
#
|
39
|
-
def self.generate(content)
|
40
|
-
content = normalize(content)
|
41
|
-
listener = new
|
42
|
-
listener.content = content
|
43
|
-
Stream.new(content, listener).parse
|
44
|
-
listener
|
45
|
-
end
|
46
|
-
|
47
|
-
def initialize # :nodoc:
|
48
|
-
@toc = []
|
49
|
-
@counters = {}
|
50
|
-
end
|
51
|
-
|
52
|
-
def tag(node) # :nodoc:
|
53
|
-
toc << {
|
54
|
-
:level => node.name.gsub(/[^\d]/, "").to_i,
|
55
|
-
:text => node.text,
|
56
|
-
:permalink => node["id"]
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
# Return a hash with all normalized attributes.
|
61
|
-
#
|
62
|
-
def to_hash
|
63
|
-
{
|
64
|
-
:content => content,
|
65
|
-
:html => to_html,
|
66
|
-
:toc => toc
|
67
|
-
}
|
68
|
-
end
|
69
|
-
|
70
|
-
# Return the table of contents in HTML format.
|
71
|
-
#
|
72
|
-
def to_html
|
73
|
-
String.new.tap do |html|
|
74
|
-
toc.each do |options|
|
75
|
-
html << %[<div class="level#{options[:level]} #{options[:permalink]}"><a href="##{options[:permalink]}"><span>#{CGI.escape_html(options[:text])}</span></a></div>]
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
2
|
+
module TOC
|
3
|
+
autoload :HTML, "kitabu/toc/html"
|
4
|
+
autoload :Epub, "kitabu/toc/epub"
|
79
5
|
end
|
80
|
-
end
|
6
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Kitabu
|
2
|
+
module TOC
|
3
|
+
class Epub
|
4
|
+
attr_accessor :navigation
|
5
|
+
|
6
|
+
def initialize(navigation)
|
7
|
+
@navigation = navigation
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_html
|
11
|
+
ERB.new(template).result OpenStruct.new(:navigation => navigation).instance_eval{ binding }
|
12
|
+
end
|
13
|
+
|
14
|
+
def template
|
15
|
+
<<-HTML.strip_heredoc.force_encoding("utf-8")
|
16
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
17
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
18
|
+
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
19
|
+
<head>
|
20
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
21
|
+
<link rel="stylesheet" type="text/css" href="epub.css"/>
|
22
|
+
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
23
|
+
<title>Table of Contents</title>
|
24
|
+
</head>
|
25
|
+
<body>
|
26
|
+
<div id="toc">
|
27
|
+
<ul>
|
28
|
+
<% navigation.each do |nav| %>
|
29
|
+
<li>
|
30
|
+
<a href="<%= nav[:content] %>"><%= nav[:label] %></a>
|
31
|
+
</li>
|
32
|
+
<% end %>
|
33
|
+
</ul>
|
34
|
+
</div>
|
35
|
+
</body>
|
36
|
+
</html>
|
37
|
+
HTML
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Kitabu
|
2
|
+
module TOC
|
3
|
+
class HTML
|
4
|
+
# Return the table of contents in hash format.
|
5
|
+
#
|
6
|
+
attr_reader :toc
|
7
|
+
|
8
|
+
private_class_method :new
|
9
|
+
attr_reader :buffer # :nodoc:
|
10
|
+
attr_reader :attrs # :nodoc:
|
11
|
+
attr_accessor :content # :nodoc:
|
12
|
+
|
13
|
+
# Traverse every title and add a +id+ attribute.
|
14
|
+
# Return the modified content.
|
15
|
+
#
|
16
|
+
def self.normalize(content)
|
17
|
+
counter = {}
|
18
|
+
html = Nokogiri::HTML.parse(content)
|
19
|
+
html.search("h2, h3, h4, h5, h6").each do |tag|
|
20
|
+
title = tag.inner_text
|
21
|
+
permalink = title.to_permalink
|
22
|
+
|
23
|
+
counter[permalink] ||= 0
|
24
|
+
counter[permalink] += 1
|
25
|
+
|
26
|
+
permalink = "#{permalink}-#{counter[permalink]}" if counter[permalink] > 1
|
27
|
+
|
28
|
+
tag.set_attribute("id", permalink)
|
29
|
+
end
|
30
|
+
|
31
|
+
html.css("body").inner_html
|
32
|
+
end
|
33
|
+
|
34
|
+
# Traverse every title normalizing its content as a permalink.
|
35
|
+
#
|
36
|
+
def self.generate(content)
|
37
|
+
content = normalize(content)
|
38
|
+
listener = new
|
39
|
+
listener.content = content
|
40
|
+
Stream.new(content, listener).parse
|
41
|
+
listener
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize # :nodoc:
|
45
|
+
@toc = []
|
46
|
+
@counters = {}
|
47
|
+
end
|
48
|
+
|
49
|
+
def tag(node) # :nodoc:
|
50
|
+
toc << {
|
51
|
+
:level => node.name.gsub(/[^\d]/, "").to_i,
|
52
|
+
:text => node.text,
|
53
|
+
:permalink => node["id"]
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return a hash with all normalized attributes.
|
58
|
+
#
|
59
|
+
def to_hash
|
60
|
+
{
|
61
|
+
:content => content,
|
62
|
+
:html => to_html,
|
63
|
+
:toc => toc
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return the table of contents in HTML format.
|
68
|
+
#
|
69
|
+
def to_html
|
70
|
+
String.new.tap do |html|
|
71
|
+
toc.each do |options|
|
72
|
+
html << %[<div class="level#{options[:level]} #{options[:permalink]}"><a href="##{options[:permalink]}"><span>#{CGI.escape_html(options[:text])}</span></a></div>]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/kitabu/version.rb
CHANGED
@@ -2,13 +2,13 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Kitabu::Cli do
|
4
4
|
context "while running export" do
|
5
|
-
it "
|
5
|
+
it "exits with status 1 when an invalid --only option is provided" do
|
6
6
|
expect {
|
7
7
|
capture(:stderr){ Kitabu::Cli.start(["export", "--only=invalid"]) }
|
8
8
|
}.to exit_with_code(1)
|
9
9
|
end
|
10
10
|
|
11
|
-
it "
|
11
|
+
it "exits with status 1 when no config file is found" do
|
12
12
|
expect {
|
13
13
|
capture(:stderr){ Kitabu::Cli.start(["export"]) }
|
14
14
|
}.to exit_with_code(1)
|
data/spec/kitabu/cli/new_spec.rb
CHANGED
@@ -10,7 +10,7 @@ describe Kitabu::Cli do
|
|
10
10
|
it_behaves_like "e-book"
|
11
11
|
end
|
12
12
|
|
13
|
-
it "
|
13
|
+
it "exits with status 1 when no path is provided" do
|
14
14
|
expect {
|
15
15
|
capture(:stderr){ Kitabu::Cli.start(["new"]) }
|
16
16
|
}.to exit_with_code(1)
|
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Kitabu::Cli do
|
4
4
|
context "while running version" do
|
5
|
-
it "
|
5
|
+
it "outputs version" do
|
6
6
|
%w[version -v --version].each do |arg|
|
7
7
|
output = capture(:stdout){ Kitabu::Cli.start([arg]) }.chomp
|
8
8
|
output.should == "Kitabu version #{Kitabu::Version::STRING}"
|
@@ -2,7 +2,7 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe RedCloth do
|
4
4
|
describe "#figure" do
|
5
|
-
it "
|
5
|
+
it "renders html" do
|
6
6
|
html = RedCloth.convert("figure(The Rails logo). rails.png")
|
7
7
|
html.should have_tag("p.figure") do |p|
|
8
8
|
p.should have_tag("img[@src='../images/rails.png'][@alt='The Rails logo']")
|
@@ -12,21 +12,21 @@ describe RedCloth do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
describe "#note" do
|
15
|
-
it "
|
15
|
+
it "renders html" do
|
16
16
|
html = RedCloth.convert("note. Some important note!")
|
17
17
|
html.should have_tag("p.note", "Some important note!")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
21
|
describe "#attention" do
|
22
|
-
it "
|
22
|
+
it "renders html" do
|
23
23
|
html = RedCloth.convert("attention. Some warning note!")
|
24
24
|
html.should have_tag("p.attention", "Some warning note!")
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
28
|
describe "#file" do
|
29
|
-
it "
|
29
|
+
it "renders html" do
|
30
30
|
Kitabu.stub :config => { :base_url => "http://example.com" }
|
31
31
|
html = RedCloth.convert("file. app/models/users.rb")
|
32
32
|
|
@@ -37,14 +37,14 @@ describe RedCloth do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context "custom footnote helper" do
|
40
|
-
it "
|
40
|
+
it "renders html" do
|
41
41
|
html = RedCloth.convert("Writing some text with a footnote %{this is a footnote}")
|
42
42
|
html.should == %[<p>Writing some text with a footnote<span class="footnote">this is a footnote</span></p>]
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
context "custom url helper" do
|
47
|
-
it "
|
47
|
+
it "renders html" do
|
48
48
|
html = RedCloth.convert("<http://example.com>")
|
49
49
|
html.should == %[<p><a href="http://example.com">http://example.com</a></p>]
|
50
50
|
end
|
@@ -3,7 +3,7 @@ require "spec_helper"
|
|
3
3
|
|
4
4
|
describe String do
|
5
5
|
describe "#to_permalink" do
|
6
|
-
it "
|
6
|
+
it "normalizes strings" do
|
7
7
|
{
|
8
8
|
'This IS a Tripped out title!!.!1 (well/ not really)' => 'this-is-a-tripped-out-title-1-well-not-really',
|
9
9
|
'////// meph1sto r0x ! \\\\\\' => 'meph1sto-r0x',
|
@@ -3,8 +3,12 @@ require "spec_helper"
|
|
3
3
|
describe Kitabu::Parser::Epub do
|
4
4
|
let(:root) { SPECDIR.join("support/mybook") }
|
5
5
|
|
6
|
-
|
6
|
+
before do
|
7
|
+
Kitabu::Parser::HTML.parse(root)
|
7
8
|
Kitabu::Parser::Epub.parse(root)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "generates e-pub" do
|
8
12
|
root.join("output/mybook.epub").should be_file
|
9
13
|
end
|
10
14
|
end
|
@@ -1,36 +1,36 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Kitabu::Parser::
|
3
|
+
describe Kitabu::Parser::HTML do
|
4
4
|
let(:root) { SPECDIR.join("support/mybook") }
|
5
5
|
let(:source) { root.join("text") }
|
6
|
-
let(:parser) {
|
6
|
+
let(:parser) { described_class.new(root) }
|
7
7
|
let(:entries) { parser.entries }
|
8
8
|
let(:relative) { entries.collect {|e| e.to_s.gsub(/^#{Regexp.escape(source.to_s)}\//, "")} }
|
9
9
|
|
10
10
|
context "when filtering entries" do
|
11
|
-
it "
|
11
|
+
it "skips dot directories" do
|
12
12
|
relative.should_not include(".")
|
13
13
|
relative.should_not include("..")
|
14
14
|
end
|
15
15
|
|
16
|
-
it "
|
16
|
+
it "skips dot files" do
|
17
17
|
relative.should_not include(".gitkeep")
|
18
18
|
end
|
19
19
|
|
20
|
-
it "
|
20
|
+
it "skips files that start with underscore" do
|
21
21
|
relative.should_not include("_00_Introduction.markdown")
|
22
22
|
end
|
23
23
|
|
24
|
-
it "
|
24
|
+
it "skips other files" do
|
25
25
|
relative.should_not include("CHANGELOG.textile")
|
26
26
|
relative.should_not include("TOC.textile")
|
27
27
|
end
|
28
28
|
|
29
|
-
it "
|
29
|
+
it "returns only first-level entries" do
|
30
30
|
relative.should_not include("04_With_Directory/Some_Chapter.mkdn")
|
31
31
|
end
|
32
32
|
|
33
|
-
it "
|
33
|
+
it "returns entries" do
|
34
34
|
relative.first.should == "01_Markdown_Chapter.markdown"
|
35
35
|
relative.second.should == "02_Textile_Chapter.textile"
|
36
36
|
relative.third.should == "03_HTML_Chapter.html"
|
@@ -44,31 +44,31 @@ describe Kitabu::Parser::Html do
|
|
44
44
|
let(:html) { File.read(file) }
|
45
45
|
before { parser.parse }
|
46
46
|
|
47
|
-
it "
|
47
|
+
it "has several chapters" do
|
48
48
|
html.should have_tag("div.chapter", 4)
|
49
49
|
end
|
50
50
|
|
51
|
-
it "
|
51
|
+
it "renders .markdown" do
|
52
52
|
html.should have_tag("div.chapter > h2#markdown", "Markdown")
|
53
53
|
end
|
54
54
|
|
55
|
-
it "
|
55
|
+
it "renders .mkdn" do
|
56
56
|
html.should have_tag("div.chapter > h2#some-chapter", "Some Chapter")
|
57
57
|
end
|
58
58
|
|
59
|
-
it "
|
59
|
+
it "renders .textile" do
|
60
60
|
html.should have_tag("div.chapter > h2#textile", "Textile")
|
61
61
|
end
|
62
62
|
|
63
|
-
it "
|
63
|
+
it "renders .html" do
|
64
64
|
html.should have_tag("div.chapter > h2#html", "HTML")
|
65
65
|
end
|
66
66
|
|
67
|
-
it "
|
67
|
+
it "uses config file" do
|
68
68
|
html.should have_tag("div.imprint p", "Copyright (C) 2010 John Doe.")
|
69
69
|
end
|
70
70
|
|
71
|
-
it "
|
71
|
+
it "renders changelog" do
|
72
72
|
html.should have_tag("div.changelog h2", "Revisions")
|
73
73
|
end
|
74
74
|
end
|
@@ -1,14 +1,14 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Kitabu::Parser::
|
3
|
+
describe Kitabu::Parser::PDF do
|
4
4
|
let(:root) { SPECDIR.join("support/mybook") }
|
5
5
|
|
6
6
|
before do
|
7
|
-
Kitabu::Parser::
|
8
|
-
Kitabu::Parser::
|
7
|
+
Kitabu::Parser::HTML.new(root).parse
|
8
|
+
Kitabu::Parser::PDF.new(root).parse
|
9
9
|
end
|
10
10
|
|
11
|
-
it "
|
11
|
+
it "generates pdf file" do
|
12
12
|
root.join("output/mybook.pdf").should be_file
|
13
13
|
end
|
14
14
|
end
|