docubot 0.6.1 → 0.6.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.
- data/bin/docubot +125 -125
- data/lib/docubot.rb +43 -43
- data/lib/docubot/bundle.rb +182 -182
- data/lib/docubot/converter.rb +28 -28
- data/lib/docubot/converters/haml.rb +9 -9
- data/lib/docubot/converters/markdown.rb +14 -14
- data/lib/docubot/index.rb +67 -67
- data/lib/docubot/link_tree.rb +111 -111
- data/lib/docubot/metasection.rb +61 -61
- data/lib/docubot/page.rb +167 -167
- data/lib/docubot/shells/default/0_License.md +59 -59
- data/lib/docubot/shells/default/Appendix/Glossary.md +4 -4
- data/lib/docubot/shells/default/Appendix/Index_Page.md +8 -8
- data/lib/docubot/shells/default/Appendix/Table of Contents.md +4 -4
- data/lib/docubot/shells/docubot-help/0_License.md +20 -20
- data/lib/docubot/shells/docubot-help/1_Getting_Started.md +47 -47
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/4 Adding Images.md +1 -1
- data/lib/docubot/shells/docubot-help/2_Basic_Concepts/index.md +5 -5
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Glossary.md +2 -2
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling Indexing.md +9 -9
- data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling the Table of Contents.md +5 -5
- data/lib/docubot/shells/docubot-help/4_Appendix/Glossary.md +4 -4
- data/lib/docubot/shells/docubot-help/4_Appendix/Index_Page.md +5 -5
- data/lib/docubot/shells/docubot-help/4_Appendix/Table of Contents.md +7 -7
- data/lib/docubot/shells/docubot-help/index.txt +7 -7
- data/lib/docubot/snippet.rb +19 -19
- data/lib/docubot/snippets/glossary.rb +7 -7
- data/lib/docubot/snippets/index_entries.rb +6 -6
- data/lib/docubot/templates/_root/glossary.css +3 -3
- data/lib/docubot/templates/_root/glossary.js +57 -57
- data/lib/docubot/templates/index.haml +14 -14
- data/lib/docubot/templates/toc.haml +2 -2
- data/lib/docubot/templates/top.haml +32 -32
- data/lib/docubot/writers/chm/hhc.erb +1 -1
- data/lib/docubot/writers/chm/hhk.erb +2 -2
- data/lib/docubot/writers/html.rb +87 -87
- data/spec/_all.rb +12 -12
- data/spec/_helper.rb +16 -16
- data/spec/bundle.rb +339 -339
- data/spec/command.rb +3 -3
- data/spec/converters.rb +2 -2
- data/spec/global.rb +27 -27
- data/spec/glossary.rb +94 -94
- data/spec/index.rb +2 -2
- data/spec/page.rb +80 -80
- data/spec/samples/attributes/defaults.haml +34 -34
- data/spec/samples/attributes/explicit1.haml +42 -42
- data/spec/samples/attributes/explicit2.haml +41 -41
- data/spec/samples/attributes/hidden.haml +39 -39
- data/spec/samples/attributes/index.md +8 -8
- data/spec/samples/collisions/page1.md +2 -2
- data/spec/samples/collisions/page1.textile +2 -2
- data/spec/samples/collisions/page2.haml +3 -3
- data/spec/samples/collisions/page2.html +2 -2
- data/spec/samples/collisions/page2.txt +2 -2
- data/spec/samples/collisions/page3.md +2 -2
- data/spec/samples/files/index.md +1 -1
- data/spec/samples/glossary/Glossary.txt +4 -4
- data/spec/samples/glossary/Some Page.md +2 -2
- data/spec/samples/glossary/_glossary/Simple Term.md +2 -2
- data/spec/samples/glossary/_glossary/complex.haml +8 -8
- data/spec/samples/glossary/_glossary/project_x.md +3 -3
- data/spec/samples/hierarchy/1/1.1/1.1.1/index.haml +1 -1
- data/spec/samples/hierarchy/1/1.1/1.1.1/page.haml +3 -3
- data/spec/samples/hierarchy/1/1.1/page.haml +3 -3
- data/spec/samples/hierarchy/1/page.haml +3 -3
- data/spec/samples/hierarchy/2/2.1/2.1.1/page.haml +3 -3
- data/spec/samples/hierarchy/2/2.1/index.haml +1 -1
- data/spec/samples/hierarchy/2/2.1/page.haml +3 -3
- data/spec/samples/hierarchy/2/page.haml +3 -3
- data/spec/samples/hierarchy/main.haml +1 -1
- data/spec/samples/hierarchy/toc.md +1 -1
- data/spec/samples/links/index.txt +11 -11
- data/spec/samples/links/root.md +13 -13
- data/spec/samples/links/sub1/inner1.md +11 -11
- data/spec/samples/links/sub2.md +4 -4
- data/spec/samples/links/sub2/inner2.md +7 -7
- data/spec/samples/simplest/HTML.html +9 -9
- data/spec/samples/simplest/Haml.haml +12 -12
- data/spec/samples/simplest/Markdown.md +9 -9
- data/spec/samples/simplest/Text.txt +9 -9
- data/spec/samples/simplest/Textile.textile +9 -9
- data/spec/samples/templates/_templates/doubler.haml +6 -6
- data/spec/samples/templates/_templates/page.haml +1 -1
- data/spec/samples/templates/goaway.txt +2 -2
- data/spec/samples/templates/onepara_html.html +2 -2
- data/spec/samples/templates/onepara_md.md +4 -4
- data/spec/samples/templates/twopara_haml.haml +6 -6
- data/spec/samples/templates/twopara_textile.textile +5 -5
- data/spec/samples/titles/3_renamed.txt +1 -1
- data/spec/samples/titles/index.txt +1 -1
- data/spec/templates.rb +43 -43
- data/spec/toc.rb +128 -128
- data/spec/writer/chm.rb +2 -2
- data/spec/writer/html.rb +2 -2
- metadata +56 -25
- data/bin/docubot.bat +0 -1
data/lib/docubot/metasection.rb
CHANGED
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
class DocuBot::MetaSection; end
|
|
3
|
-
module DocuBot::MetaSection::Castable
|
|
4
|
-
def as_boolean
|
|
5
|
-
self == 'true'
|
|
6
|
-
end
|
|
7
|
-
def as_list
|
|
8
|
-
return [] if self.nil?
|
|
9
|
-
# Replace commas inside quoted strings with unlikely-to-be-used Unicode
|
|
10
|
-
# FIXME: This doesnt work for the sadistic case of "hello """, "world"
|
|
11
|
-
csv = self.gsub( /("(?:[^",]|"")+),/, '\\1⥸' )
|
|
12
|
-
csv.split(/\s*,\s*/).map do |str|
|
|
13
|
-
# Put real commas back, unquote, undouble internal quotes
|
|
14
|
-
str[/^".*"$/] ? str[1..-2].gsub('⥸',',').gsub('""','"') : str
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class DocuBot::MetaSection
|
|
20
|
-
META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
|
|
21
|
-
NIL_CASTABLE = nil.extend( Castable )
|
|
22
|
-
attr_reader :__contents__
|
|
23
|
-
|
|
24
|
-
def initialize( attrs={}, file_path=nil )
|
|
25
|
-
@attrs = {}
|
|
26
|
-
attrs.each{ |key,value| self[key]=value }
|
|
27
|
-
if file_path && File.exists?( file_path )
|
|
28
|
-
parts = IO.read( file_path ).split( META_SEPARATOR, 2 )
|
|
29
|
-
if parts.length > 1
|
|
30
|
-
parts.first.scan(/.+/) do |line|
|
|
31
|
-
next if line =~ /^\s*#/
|
|
32
|
-
next unless line.include?(':')
|
|
33
|
-
attribute, value = line.split(':',2).map{ |str| str.strip }
|
|
34
|
-
self[attribute] = value
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
@__contents__ = parts.last && parts.last.strip
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def has_key?( key )
|
|
42
|
-
@attrs.has_key?( key )
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def []( attribute )
|
|
46
|
-
@attrs.has_key?( attribute ) ? @attrs[attribute] : NIL_CASTABLE
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def []=( attribute, value )
|
|
50
|
-
@attrs[attribute.to_s] = value.extend(Castable)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def method_missing( method, *args )
|
|
54
|
-
key=method.to_s
|
|
55
|
-
case key[-1..-1] # the last character of the method name
|
|
56
|
-
when '=' then self[key[0..-2]] = args.first
|
|
57
|
-
else self[key]
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
end
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
class DocuBot::MetaSection; end
|
|
3
|
+
module DocuBot::MetaSection::Castable
|
|
4
|
+
def as_boolean
|
|
5
|
+
self == 'true'
|
|
6
|
+
end
|
|
7
|
+
def as_list
|
|
8
|
+
return [] if self.nil?
|
|
9
|
+
# Replace commas inside quoted strings with unlikely-to-be-used Unicode
|
|
10
|
+
# FIXME: This doesnt work for the sadistic case of "hello """, "world"
|
|
11
|
+
csv = self.gsub( /("(?:[^",]|"")+),/, '\\1⥸' )
|
|
12
|
+
csv.split(/\s*,\s*/).map do |str|
|
|
13
|
+
# Put real commas back, unquote, undouble internal quotes
|
|
14
|
+
str[/^".*"$/] ? str[1..-2].gsub('⥸',',').gsub('""','"') : str
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class DocuBot::MetaSection
|
|
20
|
+
META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
|
|
21
|
+
NIL_CASTABLE = nil.extend( Castable )
|
|
22
|
+
attr_reader :__contents__
|
|
23
|
+
|
|
24
|
+
def initialize( attrs={}, file_path=nil )
|
|
25
|
+
@attrs = {}
|
|
26
|
+
attrs.each{ |key,value| self[key]=value }
|
|
27
|
+
if file_path && File.exists?( file_path )
|
|
28
|
+
parts = IO.read( file_path ).split( META_SEPARATOR, 2 )
|
|
29
|
+
if parts.length > 1
|
|
30
|
+
parts.first.scan(/.+/) do |line|
|
|
31
|
+
next if line =~ /^\s*#/
|
|
32
|
+
next unless line.include?(':')
|
|
33
|
+
attribute, value = line.split(':',2).map{ |str| str.strip }
|
|
34
|
+
self[attribute] = value
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
@__contents__ = parts.last && parts.last.strip
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def has_key?( key )
|
|
42
|
+
@attrs.has_key?( key )
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def []( attribute )
|
|
46
|
+
@attrs.has_key?( attribute ) ? @attrs[attribute] : NIL_CASTABLE
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def []=( attribute, value )
|
|
50
|
+
@attrs[attribute.to_s] = value.extend(Castable)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def method_missing( method, *args )
|
|
54
|
+
key=method.to_s
|
|
55
|
+
case key[-1..-1] # the last character of the method name
|
|
56
|
+
when '=' then self[key[0..-2]] = args.first
|
|
57
|
+
else self[key]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
data/lib/docubot/page.rb
CHANGED
|
@@ -1,167 +1,167 @@
|
|
|
1
|
-
# encoding: UTF-8
|
|
2
|
-
require 'yaml'
|
|
3
|
-
require 'nokogiri'
|
|
4
|
-
|
|
5
|
-
class DocuBot::Page
|
|
6
|
-
AUTO_ID_ELEMENTS = %w[ h1 h2 h3 h4 h5 h6 legend caption dt ].join(',')
|
|
7
|
-
|
|
8
|
-
attr_reader :type, :folder, :file, :meta, :nokodoc, :bundle
|
|
9
|
-
|
|
10
|
-
def self.title( source_path )
|
|
11
|
-
# File#basename might return the same string
|
|
12
|
-
title = File.basename( source_path ).dup
|
|
13
|
-
title.sub!(/\.[^.\s]+$/,'') unless File.directory?( source_path )
|
|
14
|
-
title.gsub!( '_', ' ' )
|
|
15
|
-
title.sub!( /^\d+\s/, '' )
|
|
16
|
-
title
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def initialize( bundle, source_path, title=nil )
|
|
20
|
-
puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
|
|
21
|
-
title ||= self.class.title( source_path )
|
|
22
|
-
@bundle = bundle
|
|
23
|
-
@file = source_path
|
|
24
|
-
if File.directory?( @file )
|
|
25
|
-
@folder = @file
|
|
26
|
-
@file = Dir[ source_path/'index.*' ][0]
|
|
27
|
-
# Directories without an index.* file now have nil @file
|
|
28
|
-
else
|
|
29
|
-
@folder = File.dirname( @file )
|
|
30
|
-
end
|
|
31
|
-
@type = File.extname( @file )[ 1..-1 ] if @file
|
|
32
|
-
@meta = DocuBot::MetaSection.new( {'title'=>title}, @file )
|
|
33
|
-
@raw = @meta.__contents__
|
|
34
|
-
@raw = nil if @raw && @raw.empty?
|
|
35
|
-
|
|
36
|
-
create_nokodoc
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def create_nokodoc
|
|
40
|
-
# Directories with no index.* file will not have any @raw
|
|
41
|
-
# Pages with metasection only will also not have any @raw
|
|
42
|
-
html = if @raw && !@raw.empty?
|
|
43
|
-
html = DocuBot::process_snippets( self, @raw )
|
|
44
|
-
html = DocuBot::convert_to_html( self, html, @type )
|
|
45
|
-
end
|
|
46
|
-
@nokodoc = Nokogiri::HTML::DocumentFragment.parse(html || "")
|
|
47
|
-
auto_id
|
|
48
|
-
auto_section
|
|
49
|
-
@nokodoc
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def nokodoc
|
|
53
|
-
@nokodoc ||= create_nokodoc
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Add IDs to elements that don't have them
|
|
57
|
-
def auto_id
|
|
58
|
-
# ...but only if a toc entry might reference one, or requested.
|
|
59
|
-
if @meta['auto-id'].as_boolean || @meta.toc.as_list.any?{ |toc| !toc['#'] }
|
|
60
|
-
@nokodoc.css( AUTO_ID_ELEMENTS ).each do |node|
|
|
61
|
-
next if node.has_attribute?('id')
|
|
62
|
-
# Strip off the unwanted leading '#'
|
|
63
|
-
node['id'] = DocuBot.id_from_text(node.inner_text)[1..-1]
|
|
64
|
-
end
|
|
65
|
-
dirty_doc
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Wrap siblings of headers in <div class='section'>
|
|
70
|
-
def auto_section
|
|
71
|
-
return if @meta['auto-section']==false
|
|
72
|
-
|
|
73
|
-
#TODO: Make this a generic nokogiri call on any node (like body) where you can pass in a hierarchy of elements and a wrapper
|
|
74
|
-
stack = []
|
|
75
|
-
@nokodoc.children.each do |node|
|
|
76
|
-
# non-matching nodes will get level of 0
|
|
77
|
-
level = node.name[ /h([1-6])/i, 1 ].to_i
|
|
78
|
-
level = 99 if level == 0
|
|
79
|
-
|
|
80
|
-
stack.pop while (top=stack.last) && top[:level]>=level
|
|
81
|
-
stack.last[:div].add_child( node ) if stack.last
|
|
82
|
-
if level<99
|
|
83
|
-
div = Nokogiri::XML::Node.new('div',@nokodoc)
|
|
84
|
-
div.set_attribute( 'class', 'section' )
|
|
85
|
-
node.add_next_sibling(div)
|
|
86
|
-
stack << { :div=>div, :level=>level }
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
dirty_doc
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def []( key )
|
|
93
|
-
@meta[key]
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def method_missing( method, *args )
|
|
97
|
-
key=method.to_s
|
|
98
|
-
case key[-1..-1] # the last character of the method name
|
|
99
|
-
when '?' then @meta.has_key?( key[0..-2] )
|
|
100
|
-
when '!','=' then super
|
|
101
|
-
else
|
|
102
|
-
# warn "Unknown attribute #{key.inspect} asked for on #{@file || @folder}" unless @meta.has_key?( key )
|
|
103
|
-
@meta[ key ]
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
def html_path
|
|
108
|
-
@html_path ||= @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Call this if the source generated by the converter would change
|
|
112
|
-
# THIS DESTROYS ANY CUSTOM CHANGES YOU HAVE MADE TO THE NOKODOC
|
|
113
|
-
def dirty_source
|
|
114
|
-
@nokodoc = nil
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
# Call this after modifying the structure of the nokodoc for the page
|
|
118
|
-
def dirty_doc
|
|
119
|
-
@content_html = nil
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Call this if the HTML generated by the page template needs to change
|
|
123
|
-
# e.g. for a glossary page.
|
|
124
|
-
def dirty_template
|
|
125
|
-
# to_html doesn't cache anything at this point
|
|
126
|
-
# so nothing needs to be done here
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
def content_html
|
|
130
|
-
@content_html ||= nokodoc.to_html
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def children
|
|
134
|
-
@bundle.toc.children(html_path).map{ |node| node.page }.uniq.compact
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def leaf?
|
|
138
|
-
@leaf ||= !children.any?{ |page| page != self }
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def depth
|
|
142
|
-
@depth ||= html_path.scan('/').length
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def root
|
|
146
|
-
@root ||= "../" * depth
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
#TODO: cache this is people keep calling to_html and it's a problem
|
|
150
|
-
def to_html
|
|
151
|
-
@meta.template ||= leaf? ? 'page' : 'section'
|
|
152
|
-
|
|
153
|
-
master_templates = DocuBot::TEMPLATE_DIR
|
|
154
|
-
source_templates = @bundle.source / '_templates'
|
|
155
|
-
tmpl = source_templates / "#{template}.haml"
|
|
156
|
-
tmpl = master_templates / "#{template}.haml" unless File.exists?( tmpl )
|
|
157
|
-
tmpl = master_templates / "page.haml" unless File.exists?( tmpl )
|
|
158
|
-
tmpl = IO.read( tmpl )
|
|
159
|
-
haml = Haml::Engine.new( tmpl, DocuBot::Writer::HAML_OPTIONS )
|
|
160
|
-
haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.global, :root=>root )
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
def inspect
|
|
164
|
-
"<#{self.class} '#{self.title}' #{@file ? "@file=#{@file.inspect}" : "@folder=#{@folder.inspect}"}>"
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
end
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'nokogiri'
|
|
4
|
+
|
|
5
|
+
class DocuBot::Page
|
|
6
|
+
AUTO_ID_ELEMENTS = %w[ h1 h2 h3 h4 h5 h6 legend caption dt ].join(',')
|
|
7
|
+
|
|
8
|
+
attr_reader :type, :folder, :file, :meta, :nokodoc, :bundle
|
|
9
|
+
|
|
10
|
+
def self.title( source_path )
|
|
11
|
+
# File#basename might return the same string
|
|
12
|
+
title = File.basename( source_path ).dup
|
|
13
|
+
title.sub!(/\.[^.\s]+$/,'') unless File.directory?( source_path )
|
|
14
|
+
title.gsub!( '_', ' ' )
|
|
15
|
+
title.sub!( /^\d+\s/, '' )
|
|
16
|
+
title
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize( bundle, source_path, title=nil )
|
|
20
|
+
puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
|
|
21
|
+
title ||= self.class.title( source_path )
|
|
22
|
+
@bundle = bundle
|
|
23
|
+
@file = source_path
|
|
24
|
+
if File.directory?( @file )
|
|
25
|
+
@folder = @file
|
|
26
|
+
@file = Dir[ source_path/'index.*' ][0]
|
|
27
|
+
# Directories without an index.* file now have nil @file
|
|
28
|
+
else
|
|
29
|
+
@folder = File.dirname( @file )
|
|
30
|
+
end
|
|
31
|
+
@type = File.extname( @file )[ 1..-1 ] if @file
|
|
32
|
+
@meta = DocuBot::MetaSection.new( {'title'=>title}, @file )
|
|
33
|
+
@raw = @meta.__contents__
|
|
34
|
+
@raw = nil if @raw && @raw.empty?
|
|
35
|
+
|
|
36
|
+
create_nokodoc
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def create_nokodoc
|
|
40
|
+
# Directories with no index.* file will not have any @raw
|
|
41
|
+
# Pages with metasection only will also not have any @raw
|
|
42
|
+
html = if @raw && !@raw.empty?
|
|
43
|
+
html = DocuBot::process_snippets( self, @raw )
|
|
44
|
+
html = DocuBot::convert_to_html( self, html, @type )
|
|
45
|
+
end
|
|
46
|
+
@nokodoc = Nokogiri::HTML::DocumentFragment.parse(html || "")
|
|
47
|
+
auto_id
|
|
48
|
+
auto_section
|
|
49
|
+
@nokodoc
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def nokodoc
|
|
53
|
+
@nokodoc ||= create_nokodoc
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Add IDs to elements that don't have them
|
|
57
|
+
def auto_id
|
|
58
|
+
# ...but only if a toc entry might reference one, or requested.
|
|
59
|
+
if @meta['auto-id'].as_boolean || @meta.toc.as_list.any?{ |toc| !toc['#'] }
|
|
60
|
+
@nokodoc.css( AUTO_ID_ELEMENTS ).each do |node|
|
|
61
|
+
next if node.has_attribute?('id')
|
|
62
|
+
# Strip off the unwanted leading '#'
|
|
63
|
+
node['id'] = DocuBot.id_from_text(node.inner_text)[1..-1]
|
|
64
|
+
end
|
|
65
|
+
dirty_doc
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Wrap siblings of headers in <div class='section'>
|
|
70
|
+
def auto_section
|
|
71
|
+
return if @meta['auto-section']==false
|
|
72
|
+
|
|
73
|
+
#TODO: Make this a generic nokogiri call on any node (like body) where you can pass in a hierarchy of elements and a wrapper
|
|
74
|
+
stack = []
|
|
75
|
+
@nokodoc.children.each do |node|
|
|
76
|
+
# non-matching nodes will get level of 0
|
|
77
|
+
level = node.name[ /h([1-6])/i, 1 ].to_i
|
|
78
|
+
level = 99 if level == 0
|
|
79
|
+
|
|
80
|
+
stack.pop while (top=stack.last) && top[:level]>=level
|
|
81
|
+
stack.last[:div].add_child( node ) if stack.last
|
|
82
|
+
if level<99
|
|
83
|
+
div = Nokogiri::XML::Node.new('div',@nokodoc)
|
|
84
|
+
div.set_attribute( 'class', 'section' )
|
|
85
|
+
node.add_next_sibling(div)
|
|
86
|
+
stack << { :div=>div, :level=>level }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
dirty_doc
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def []( key )
|
|
93
|
+
@meta[key]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def method_missing( method, *args )
|
|
97
|
+
key=method.to_s
|
|
98
|
+
case key[-1..-1] # the last character of the method name
|
|
99
|
+
when '?' then @meta.has_key?( key[0..-2] )
|
|
100
|
+
when '!','=' then super
|
|
101
|
+
else
|
|
102
|
+
# warn "Unknown attribute #{key.inspect} asked for on #{@file || @folder}" unless @meta.has_key?( key )
|
|
103
|
+
@meta[ key ]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def html_path
|
|
108
|
+
@html_path ||= @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Call this if the source generated by the converter would change
|
|
112
|
+
# THIS DESTROYS ANY CUSTOM CHANGES YOU HAVE MADE TO THE NOKODOC
|
|
113
|
+
def dirty_source
|
|
114
|
+
@nokodoc = nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Call this after modifying the structure of the nokodoc for the page
|
|
118
|
+
def dirty_doc
|
|
119
|
+
@content_html = nil
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Call this if the HTML generated by the page template needs to change
|
|
123
|
+
# e.g. for a glossary page.
|
|
124
|
+
def dirty_template
|
|
125
|
+
# to_html doesn't cache anything at this point
|
|
126
|
+
# so nothing needs to be done here
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def content_html
|
|
130
|
+
@content_html ||= nokodoc.to_html
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def children
|
|
134
|
+
@bundle.toc.children(html_path).map{ |node| node.page }.uniq.compact
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def leaf?
|
|
138
|
+
@leaf ||= !children.any?{ |page| page != self }
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def depth
|
|
142
|
+
@depth ||= html_path.scan('/').length
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def root
|
|
146
|
+
@root ||= "../" * depth
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
#TODO: cache this is people keep calling to_html and it's a problem
|
|
150
|
+
def to_html
|
|
151
|
+
@meta.template ||= leaf? ? 'page' : 'section'
|
|
152
|
+
|
|
153
|
+
master_templates = DocuBot::TEMPLATE_DIR
|
|
154
|
+
source_templates = @bundle.source / '_templates'
|
|
155
|
+
tmpl = source_templates / "#{template}.haml"
|
|
156
|
+
tmpl = master_templates / "#{template}.haml" unless File.exists?( tmpl )
|
|
157
|
+
tmpl = master_templates / "page.haml" unless File.exists?( tmpl )
|
|
158
|
+
tmpl = IO.read( tmpl )
|
|
159
|
+
haml = Haml::Engine.new( tmpl, DocuBot::Writer::HAML_OPTIONS )
|
|
160
|
+
haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.global, :root=>root )
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def inspect
|
|
164
|
+
"<#{self.class} '#{self.title}' #{@file ? "@file=#{@file.inspect}" : "@folder=#{@folder.inspect}"}>"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
end
|