fnando-bookmaker 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +41 -0
- data/License.txt +20 -0
- data/Manifest.txt +38 -0
- data/PostInstall.txt +0 -0
- data/README.markdown +168 -0
- data/Rakefile +4 -0
- data/TODO.txt +2 -0
- data/app_generators/bookmaker/bookmaker_generator.rb +68 -0
- data/app_generators/bookmaker/templates/Rakefile +3 -0
- data/app_generators/bookmaker/templates/config.yml +8 -0
- data/app_generators/bookmaker/templates/css/active4d.css +114 -0
- data/app_generators/bookmaker/templates/css/blackboard.css +88 -0
- data/app_generators/bookmaker/templates/css/dawn.css +121 -0
- data/app_generators/bookmaker/templates/css/eiffel.css +121 -0
- data/app_generators/bookmaker/templates/css/idle.css +62 -0
- data/app_generators/bookmaker/templates/css/iplastic.css +80 -0
- data/app_generators/bookmaker/templates/css/lazy.css +73 -0
- data/app_generators/bookmaker/templates/css/mac_classic.css +123 -0
- data/app_generators/bookmaker/templates/css/slush_poppies.css +85 -0
- data/app_generators/bookmaker/templates/css/sunburst.css +180 -0
- data/app_generators/bookmaker/templates/layouts/boom/layout.css +471 -0
- data/app_generators/bookmaker/templates/layouts/boom/layout.html +37 -0
- data/app_generators/bookmaker/templates/user.css +0 -0
- data/bin/bookmaker +17 -0
- data/config/hoe.rb +76 -0
- data/config/requirements.rb +15 -0
- data/lib/bookmaker.rb +18 -0
- data/lib/bookmaker/base.rb +319 -0
- data/lib/bookmaker/blackcloth.rb +165 -0
- data/lib/bookmaker/tasks.rb +112 -0
- data/lib/bookmaker/version.rb +9 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +82 -0
- data/setup.rb +1585 -0
- data/test/test_bookmaker.rb +11 -0
- data/test/test_bookmaker_generator.rb +43 -0
- data/test/test_generator_helper.rb +29 -0
- data/test/test_helper.rb +2 -0
- metadata +101 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
|
2
|
+
"http://www.w3.org/TR/html4/strict.dtd">
|
3
|
+
<html>
|
4
|
+
<head>
|
5
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
6
|
+
<link rel="stylesheet" type="text/css" href="../templates/layout.css"/>
|
7
|
+
<link rel="stylesheet" type="text/css" href="../templates/syntax.css"/>
|
8
|
+
<link rel="stylesheet" type="text/css" href="../templates/user.css"/>
|
9
|
+
|
10
|
+
<meta name="author" content="<%%= authors.join(', ') %>" />
|
11
|
+
<meta name="subject" content="<%%= subject %>" />
|
12
|
+
<meta name="keywords" content="<%%= keywords %>" />
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<div class="frontcover"></div>
|
16
|
+
<div class="halftitlepage">
|
17
|
+
<h1 class="no-toc"><%%= title %></h1>
|
18
|
+
</div>
|
19
|
+
|
20
|
+
<div class="titlepage">
|
21
|
+
<h1 class="no-toc"><%%= title %></h1>
|
22
|
+
<p class="no-toc"><%%= authors.join('<br/>') %></p>
|
23
|
+
</div>
|
24
|
+
|
25
|
+
<div class="imprint">
|
26
|
+
<p><%%= copyright %></p>
|
27
|
+
</div>
|
28
|
+
|
29
|
+
<div class="chapters">
|
30
|
+
<%%= contents %>
|
31
|
+
</div>
|
32
|
+
|
33
|
+
<div class="white_page"></div>
|
34
|
+
<div class="white_page"></div>
|
35
|
+
<div class="backcover"></div>
|
36
|
+
</body>
|
37
|
+
</html>
|
File without changes
|
data/bin/bookmaker
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubigen'
|
5
|
+
|
6
|
+
if %w(-v --version).include? ARGV.first
|
7
|
+
require 'bookmaker/version'
|
8
|
+
puts "#{File.basename($0)} #{Bookmaker::VERSION::STRING}"
|
9
|
+
exit(0)
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'rubigen/scripts/generate'
|
13
|
+
source = RubiGen::PathSource.new(:application,
|
14
|
+
File.join(File.dirname(__FILE__), "../app_generators"))
|
15
|
+
RubiGen::Base.reset_sources
|
16
|
+
RubiGen::Base.append_sources source
|
17
|
+
RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'bookmaker')
|
data/config/hoe.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'bookmaker/version'
|
2
|
+
|
3
|
+
AUTHOR = 'Nando Vieira' # can also be an array of Authors
|
4
|
+
EMAIL = "fnando.vieira@gmail.com"
|
5
|
+
DESCRIPTION = "A framework for creating e-books from Markdown/Textile text markup using Ruby. Using the Prince PDF generator, you'll be able to get high quality PDFs. Mac users that have Textmate installed can have source code highlighted with his favorite theme."
|
6
|
+
GEM_NAME = 'bookmaker' # what ppl will type to install your gem
|
7
|
+
RUBYFORGE_PROJECT = 'bookmaker' # The unix name for your project
|
8
|
+
HOMEPATH = "http://github.com/fnando/bookmaker"
|
9
|
+
DOWNLOAD_PATH = "http://github.com/fnando/bookmaker"
|
10
|
+
EXTRA_DEPENDENCIES = [
|
11
|
+
['unicode', '>= 0.1'],
|
12
|
+
['RedCloth', '>= 3.0.4'],
|
13
|
+
['rubigen', '>= 1.3.2'],
|
14
|
+
['discount', '>= 1.2.6.1']
|
15
|
+
] # An array of rubygem dependencies [name, version]
|
16
|
+
|
17
|
+
@config_file = "~/.rubyforge/user-config.yml"
|
18
|
+
@config = nil
|
19
|
+
RUBYFORGE_USERNAME = "fnando"
|
20
|
+
def rubyforge_username
|
21
|
+
unless @config
|
22
|
+
begin
|
23
|
+
@config = YAML.load(File.read(File.expand_path(@config_file)))
|
24
|
+
rescue
|
25
|
+
puts <<-EOS
|
26
|
+
ERROR: No rubyforge config file found: #{@config_file}
|
27
|
+
Run 'rubyforge setup' to prepare your env for access to Rubyforge
|
28
|
+
- See http://newgem.rubyforge.org/rubyforge.html for more details
|
29
|
+
EOS
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
RUBYFORGE_USERNAME.replace @config["username"]
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
REV = nil
|
38
|
+
# UNCOMMENT IF REQUIRED:
|
39
|
+
# REV = YAML.load(`svn info`)['Revision']
|
40
|
+
VERS = Bookmaker::VERSION::STRING + (REV ? ".#{REV}" : "")
|
41
|
+
RDOC_OPTS = ['--quiet', '--title', 'bookmaker documentation',
|
42
|
+
"--opname", "index.html",
|
43
|
+
"--line-numbers",
|
44
|
+
"--main", "README",
|
45
|
+
"--inline-source"]
|
46
|
+
|
47
|
+
class Hoe
|
48
|
+
def extra_deps
|
49
|
+
@extra_deps.reject! { |x| Array(x).first == 'hoe' }
|
50
|
+
@extra_deps
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generate all the Rake tasks
|
55
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
56
|
+
$hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
57
|
+
p.developer(AUTHOR, EMAIL)
|
58
|
+
p.description = DESCRIPTION
|
59
|
+
p.summary = DESCRIPTION
|
60
|
+
p.url = HOMEPATH
|
61
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
62
|
+
p.test_globs = ["test/**/test_*.rb"]
|
63
|
+
p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
|
64
|
+
|
65
|
+
# == Optional
|
66
|
+
p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
|
67
|
+
#p.extra_deps = EXTRA_DEPENDENCIES
|
68
|
+
|
69
|
+
#p.spec_extras = {} # A hash of extra values to set in the gemspec.
|
70
|
+
end
|
71
|
+
|
72
|
+
CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
|
73
|
+
PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
|
74
|
+
$hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
|
75
|
+
$hoe.rsync_args = '-av --delete --ignore-errors'
|
76
|
+
$hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
include FileUtils
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
%w[rake hoe newgem rubigen].each do |req_gem|
|
6
|
+
begin
|
7
|
+
require req_gem
|
8
|
+
rescue LoadError
|
9
|
+
puts "This Rakefile requires the '#{req_gem}' RubyGem."
|
10
|
+
puts "Installation: gem install #{req_gem} -y"
|
11
|
+
exit
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
$:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
|
data/lib/bookmaker.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "yaml"
|
6
|
+
require "erb"
|
7
|
+
require "ostruct"
|
8
|
+
require "rexml/streamlistener"
|
9
|
+
require "rexml/document"
|
10
|
+
require "hpricot"
|
11
|
+
|
12
|
+
require "bookmaker/base"
|
13
|
+
|
14
|
+
begin
|
15
|
+
require "ruby-debug"
|
16
|
+
rescue LoadError => e
|
17
|
+
nil
|
18
|
+
end
|
@@ -0,0 +1,319 @@
|
|
1
|
+
module Bookmaker
|
2
|
+
module Markup
|
3
|
+
def self.content_for(options)
|
4
|
+
source_file = File.join(BOOKMAKER_ROOT, 'code', options[:source_file].to_s)
|
5
|
+
code = options[:code]
|
6
|
+
|
7
|
+
if options[:source_file] && File.exists?(source_file)
|
8
|
+
file = File.new(source_file)
|
9
|
+
|
10
|
+
if options[:from_line] && options[:to_line]
|
11
|
+
from_line = options[:from_line].to_i - 1
|
12
|
+
to_line = options[:to_line].to_i
|
13
|
+
offset = to_line - from_line
|
14
|
+
code = file.readlines.slice(from_line, offset).join
|
15
|
+
elsif block_name = options[:block_name]
|
16
|
+
re = %r(# ?begin: ?#{block_name} ?(?:[^\r\n]+)?\r?\n(.*?)\r?\n([^\r\n]+)?# ?end: #{block_name})sim
|
17
|
+
file.read.gsub(re) { |block| code = $1 }
|
18
|
+
else
|
19
|
+
code = file.read
|
20
|
+
code = code.gsub(/</, '<').gsub(/>/, '>').gsub(/&/, '&')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# no code? set to default
|
25
|
+
code ||= options[:code]
|
26
|
+
|
27
|
+
# normalize indentation
|
28
|
+
line = StringIO.new(code).readlines[0]
|
29
|
+
|
30
|
+
if line =~ /^(\t+)/
|
31
|
+
char = "\t"
|
32
|
+
size = $1.length
|
33
|
+
elsif line =~ /^( +)/
|
34
|
+
char = " "
|
35
|
+
size = $1.length
|
36
|
+
end
|
37
|
+
|
38
|
+
code.gsub! %r(^#{char}{#{size}}), "" if size.to_i > 0
|
39
|
+
|
40
|
+
# remove all line stubs
|
41
|
+
code.gsub! %r(^[\t ]*__$), ""
|
42
|
+
|
43
|
+
# return
|
44
|
+
code
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.syntax(code, syntax='plain_text')
|
48
|
+
# get chosen theme
|
49
|
+
theme = Bookmaker::Base.config['theme']
|
50
|
+
theme = Bookmaker::Base.default_theme unless Bookmaker::Base.theme?(theme)
|
51
|
+
|
52
|
+
# get syntax
|
53
|
+
syntax = Bookmaker::Base.default_syntax unless Bookmaker::Base.syntax?(syntax)
|
54
|
+
|
55
|
+
Uv.parse(code, "xhtml", syntax, false, theme)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module Base
|
60
|
+
DEFAULT_LAYOUT = 'boom'
|
61
|
+
DEFAULT_THEME = 'eiffel'
|
62
|
+
DEFAULT_SYNTAX = 'plain_text'
|
63
|
+
GEM_ROOT = File.expand_path(File.dirname(__FILE__) + "/../../")
|
64
|
+
|
65
|
+
def self.html_path
|
66
|
+
BOOKMAKER_ROOT + "/output/#{app_name}.html"
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.pdf_path
|
70
|
+
BOOKMAKER_ROOT + "/output/#{app_name}.pdf"
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.template_path
|
74
|
+
BOOKMAKER_ROOT + "/templates/layout.html"
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.config_path
|
78
|
+
BOOKMAKER_ROOT + "/config.yml"
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.text_dir
|
82
|
+
BOOKMAKER_ROOT + "/text"
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.config
|
86
|
+
@config ||= YAML::load_file(config_path)
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.parse_layout(contents)
|
90
|
+
template = File.new(template_path).read
|
91
|
+
contents, toc = self.table_of_contents(contents)
|
92
|
+
cfg = config.merge(:contents => contents, :toc => toc)
|
93
|
+
env = OpenStruct.new(cfg)
|
94
|
+
|
95
|
+
ERB.new(template).result env.instance_eval{binding}
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.table_of_contents(contents)
|
99
|
+
return [contents, nil] unless Object.const_defined?('Hpricot') && Object.const_defined?('Unicode')
|
100
|
+
|
101
|
+
doc = Hpricot(contents)
|
102
|
+
|
103
|
+
(doc/"h2, h3, h4, h5, h6").each do |node|
|
104
|
+
title = node.inner_text
|
105
|
+
permalink = Bookmaker::Base.to_permalink(title)
|
106
|
+
node.set_attribute(:id, permalink)
|
107
|
+
end
|
108
|
+
|
109
|
+
contents = doc.to_html
|
110
|
+
io = StringIO.new(contents)
|
111
|
+
toc = Toc.new
|
112
|
+
REXML::Document.parse_stream(io, toc)
|
113
|
+
|
114
|
+
[contents, toc.to_s]
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.generate_pdf
|
118
|
+
IO.popen('prince %s -o %s' % [html_path, pdf_path])
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.generate_html
|
122
|
+
# all parsed markdown file holder
|
123
|
+
contents = ""
|
124
|
+
|
125
|
+
# first, get all chapters; then, get all parsed markdown
|
126
|
+
# files from this chapter and group them into a <div class="chapter"> tag
|
127
|
+
Dir.entries(text_dir).sort.each do |dirname|
|
128
|
+
next if %w(. ..).include?(dirname) || File.file?(text_dir + "/#{dirname}")
|
129
|
+
|
130
|
+
# gets all parsed markdown files to wrap in a
|
131
|
+
# chapter element
|
132
|
+
chapter = ""
|
133
|
+
|
134
|
+
# merge all markdown and textile files into a single list
|
135
|
+
markup_files = Dir["#{text_dir}/#{dirname}/*.markdown"] + Dir["#{text_dir}/#{dirname}/*.textile"]
|
136
|
+
|
137
|
+
# no files, so skip it!
|
138
|
+
next if markup_files.empty?
|
139
|
+
|
140
|
+
markup_files.sort.each do |markup_file|
|
141
|
+
# get the file contents
|
142
|
+
markup_contents = File.new(markup_file).read
|
143
|
+
|
144
|
+
# instantiate a markup object
|
145
|
+
begin
|
146
|
+
if markup_file =~ /\.textile$/
|
147
|
+
markup = BlackCloth.new(markup_contents)
|
148
|
+
else
|
149
|
+
markup = Discount.new(markup_contents)
|
150
|
+
end
|
151
|
+
rescue Exception => e
|
152
|
+
puts "Skipping #{markup_file} (#{e.message})"
|
153
|
+
next
|
154
|
+
end
|
155
|
+
|
156
|
+
# convert the markup into html
|
157
|
+
parsed_contents = markup.to_html
|
158
|
+
|
159
|
+
if Object.const_defined?('Uv')
|
160
|
+
if markup.respond_to?(:syntax_blocks)
|
161
|
+
# textile
|
162
|
+
parsed_contents.gsub!(/@syntax:([0-9]+)/m) do |m|
|
163
|
+
syntax, code = markup.syntax_blocks[$1.to_i]
|
164
|
+
Bookmaker::Markup.syntax(code, syntax)
|
165
|
+
end
|
166
|
+
else
|
167
|
+
# markdown
|
168
|
+
parsed_contents.gsub! /<pre><code>(.*?)<\/code><\/pre>/m do |block|
|
169
|
+
code = $1.gsub(/</, '<').gsub(/>/, '>').gsub(/&/, '&')
|
170
|
+
code_lines = StringIO.new(code).readlines
|
171
|
+
syntax_settings = code_lines.first
|
172
|
+
|
173
|
+
syntax = 'plain_text'
|
174
|
+
|
175
|
+
if syntax_settings =~ /syntax\(.*?\)\./
|
176
|
+
code = code_lines.slice(1, code_lines.size).join
|
177
|
+
|
178
|
+
# syntax
|
179
|
+
m, syntax = *syntax_settings.match(/syntax\(([^ #]+).*?\)./)
|
180
|
+
|
181
|
+
# file name
|
182
|
+
m, source_file = *syntax_settings.match(/syntax\(.*?\)\. +(.*?)$/)
|
183
|
+
|
184
|
+
# get line interval
|
185
|
+
m, from_line, to_line = *syntax_settings.match(/syntax\(.*? ([0-9]+),([0-9]+)\)/)
|
186
|
+
|
187
|
+
# get block name
|
188
|
+
m, block_name = *syntax_settings.match(/syntax\(.*?#([0-9a-z_]+)\)/)
|
189
|
+
|
190
|
+
code = Bookmaker::Markup.content_for({
|
191
|
+
:code => code,
|
192
|
+
:from_line => from_line,
|
193
|
+
:to_line => to_line,
|
194
|
+
:block_name => block_name,
|
195
|
+
:source_file => source_file
|
196
|
+
})
|
197
|
+
|
198
|
+
Bookmaker::Markup.syntax(code, syntax)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
chapter << (parsed_contents + "\n\n")
|
205
|
+
end
|
206
|
+
|
207
|
+
contents << '<div class="chapter">%s</div>' % chapter
|
208
|
+
end
|
209
|
+
|
210
|
+
# save html file
|
211
|
+
File.open(html_path, 'w+') do |f|
|
212
|
+
f << Bookmaker::Base.parse_layout(contents)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def self.app_name
|
217
|
+
ENV['BOOKMAKER_NAME'] || 'bookmaker'
|
218
|
+
end
|
219
|
+
|
220
|
+
def self.theme?(theme_name)
|
221
|
+
themes.include?(theme_name)
|
222
|
+
end
|
223
|
+
|
224
|
+
def self.syntax?(syntax_name)
|
225
|
+
syntaxes.include?(syntax_name)
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.default_theme
|
229
|
+
DEFAULT_THEME
|
230
|
+
end
|
231
|
+
|
232
|
+
def self.default_syntax
|
233
|
+
DEFAULT_SYNTAX
|
234
|
+
end
|
235
|
+
|
236
|
+
def self.default_layout
|
237
|
+
DEFAULT_LAYOUT
|
238
|
+
end
|
239
|
+
|
240
|
+
def self.syntaxes
|
241
|
+
Uv.syntaxes
|
242
|
+
end
|
243
|
+
|
244
|
+
def self.layouts
|
245
|
+
@layouts ||= begin
|
246
|
+
filter = File.join(GEM_ROOT, "app_generators/bookmaker/templates/layouts/*.css")
|
247
|
+
Dir[filter].collect{|path| File.basename(path).gsub(/\.css$/, '') }.sort
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def self.themes
|
252
|
+
@themes ||= begin
|
253
|
+
filter = File.join(GEM_ROOT, "app_generators/bookmaker/templates/css/*.css")
|
254
|
+
Dir[filter].collect{|path| File.basename(path).gsub(/\.css$/, '') }.sort
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.to_permalink(str)
|
259
|
+
str = Unicode.normalize_KD(str).gsub(/[^\x00-\x7F]/n,'')
|
260
|
+
str = str.gsub(/[^-_\s\w]/, ' ').downcase.squeeze(' ').tr(' ', '-')
|
261
|
+
str = str.gsub(/-+/, '-').gsub(/^-+/, '').gsub(/-+$/, '')
|
262
|
+
str
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
class Toc
|
267
|
+
include REXML::StreamListener
|
268
|
+
|
269
|
+
def initialize
|
270
|
+
@toc = ""
|
271
|
+
@previous_level = 0
|
272
|
+
@tag = nil
|
273
|
+
@stack = []
|
274
|
+
end
|
275
|
+
|
276
|
+
def header?(tag=nil)
|
277
|
+
tag ||= @tag_name
|
278
|
+
return false unless tag.to_s =~ /h[2-6]/
|
279
|
+
@tag_name = tag
|
280
|
+
return true
|
281
|
+
end
|
282
|
+
|
283
|
+
def in_header?
|
284
|
+
@in_header
|
285
|
+
end
|
286
|
+
|
287
|
+
def tag_start(name, attrs)
|
288
|
+
@tag_name = name
|
289
|
+
return unless header?(name)
|
290
|
+
@in_header = true
|
291
|
+
@current_level = name.gsub!(/[^2-6]/, '').to_i
|
292
|
+
@stack << @current_level
|
293
|
+
@id = attrs["id"]
|
294
|
+
|
295
|
+
@toc << %(<ul class="level#{@current_level}">) if @current_level > @previous_level
|
296
|
+
@toc << %(</li></ul>) * (@previous_level - @current_level) if @current_level < @previous_level
|
297
|
+
@toc << %(</li>) if @current_level <= @previous_level
|
298
|
+
@toc << %(<li>)
|
299
|
+
end
|
300
|
+
|
301
|
+
def tag_end(name)
|
302
|
+
return unless header?(name)
|
303
|
+
@in_header = false
|
304
|
+
@previous_level = @current_level
|
305
|
+
end
|
306
|
+
|
307
|
+
def text(str)
|
308
|
+
return unless in_header?
|
309
|
+
@toc << %(<a href="##{@id}"><span>#{str}</span></a>)
|
310
|
+
end
|
311
|
+
|
312
|
+
def method_missing(*args)
|
313
|
+
end
|
314
|
+
|
315
|
+
def to_s
|
316
|
+
@toc + (%(</li></ul>) * (@stack.last - 1))
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end
|