docubot 0.3.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/bin/docubot +5 -1
  2. data/lib/docubot.rb +4 -2
  3. data/lib/docubot/bundle.rb +109 -61
  4. data/lib/docubot/converter.rb +4 -1
  5. data/lib/docubot/converters/haml.rb +1 -1
  6. data/lib/docubot/glossary.rb +3 -0
  7. data/lib/docubot/link_tree.rb +109 -0
  8. data/lib/docubot/metasection.rb +61 -0
  9. data/lib/docubot/page.rb +40 -118
  10. data/lib/docubot/shells/docubot-help/3_Advanced_Topics/Controlling the Table of Contents.md +2 -4
  11. data/lib/docubot/shells/nvphysx/_templates/_root/common.css +10 -5
  12. data/lib/docubot/shells/nvphysx/_templates/_root/glossary.css +1 -0
  13. data/lib/docubot/shells/nvphysx/_templates/_root/glossary.js +11 -1
  14. data/lib/docubot/shells/nvphysx/_templates/section.haml +9 -0
  15. data/lib/docubot/templates/index.haml +13 -20
  16. data/lib/docubot/templates/section.haml +1 -4
  17. data/lib/docubot/templates/toc.haml +2 -11
  18. data/lib/docubot/templates/top.haml +7 -4
  19. data/lib/docubot/writers/chm.rb +6 -5
  20. data/lib/docubot/writers/chm/hhc.erb +35 -12
  21. data/lib/docubot/writers/chm/hhk.erb +1 -1
  22. data/lib/docubot/writers/chm/hhp.erb +2 -2
  23. data/lib/docubot/writers/html.rb +33 -23
  24. data/spec/_all.rb +1 -0
  25. data/spec/_helper.rb +10 -0
  26. data/spec/bundle.rb +193 -2
  27. data/spec/global.rb +28 -0
  28. data/spec/glossary.rb +13 -11
  29. data/spec/page.rb +35 -9
  30. data/spec/samples/attributes/defaults.haml +34 -0
  31. data/spec/samples/attributes/explicit1.haml +43 -0
  32. data/spec/samples/attributes/explicit2.haml +42 -0
  33. data/spec/samples/attributes/hidden.haml +40 -0
  34. data/spec/samples/attributes/index.md +8 -0
  35. data/spec/samples/collisions/page1.md +3 -0
  36. data/spec/samples/collisions/page1.textile +3 -0
  37. data/spec/samples/collisions/page2.haml +4 -0
  38. data/spec/samples/collisions/page2.html +3 -0
  39. data/spec/samples/collisions/page2.txt +3 -0
  40. data/spec/samples/collisions/page3.bin +1 -0
  41. data/spec/samples/collisions/page3.md +3 -0
  42. data/spec/samples/{link_test/sub2/bozo.bin → files/BUILDING.txt} +0 -0
  43. data/spec/samples/{titletest/1 First One.txt → files/_static/Thumbs.db} +0 -0
  44. data/spec/samples/{titletest/2_Second_One.txt → files/_static/foo.ai} +0 -0
  45. data/spec/samples/{titletest/4 Fourth_One.txt → files/_static/foo.png} +0 -0
  46. data/spec/samples/{titletest/5_Fifth One.txt → files/_static/foo.psd} +0 -0
  47. data/spec/samples/files/another.md +0 -0
  48. data/spec/samples/files/common.css +0 -0
  49. data/spec/samples/files/first.textile +0 -0
  50. data/spec/samples/files/index.md +2 -0
  51. data/spec/samples/files/section/foo.jpg +0 -0
  52. data/spec/samples/files/section/page.haml +0 -0
  53. data/spec/samples/files/section/sub section/Thumbs.db b/data/spec/samples/files/section/sub → section/Thumbs.db +0 -0
  54. data/spec/samples/files/section/sub section/foo.gif b/data/spec/samples/files/section/sub → section/foo.gif +0 -0
  55. data/spec/samples/files/section/sub section/page.txt b/data/spec/samples/files/section/sub → section/page.txt +0 -0
  56. data/spec/samples/{link_test → links}/index.txt +0 -0
  57. data/spec/samples/{link_test → links}/root.md +0 -0
  58. data/spec/samples/{link_test → links}/sub1/inner1.md +0 -0
  59. data/spec/samples/{link_test → links}/sub2.md +0 -0
  60. data/spec/samples/links/sub2/bozo.bin +0 -0
  61. data/spec/samples/{link_test → links}/sub2/inner2.md +0 -0
  62. data/spec/samples/templates/_templates/404.haml +1 -0
  63. data/spec/samples/templates/_templates/doubler.haml +7 -0
  64. data/spec/samples/templates/_templates/page.haml +2 -0
  65. data/spec/samples/templates/goaway.txt +3 -0
  66. data/spec/samples/templates/onepara_html.html +3 -0
  67. data/spec/samples/templates/onepara_md.md +5 -0
  68. data/spec/samples/templates/twopara_haml.haml +7 -0
  69. data/spec/samples/templates/twopara_textile.textile +6 -0
  70. data/spec/samples/titles/1 First One.txt b/data/spec/samples/titles/1 First → One.txt +0 -0
  71. data/spec/samples/titles/2_Second_One.txt +0 -0
  72. data/spec/samples/{titletest → titles}/3_renamed.txt +0 -0
  73. data/spec/samples/titles/4 Fourth_One.txt b/data/spec/samples/titles/4 → Fourth_One.txt +0 -0
  74. data/spec/samples/titles/5_Fifth One.txt b/data/spec/samples/titles/5_Fifth → One.txt +0 -0
  75. data/spec/samples/titles/911.txt +0 -0
  76. data/spec/samples/titles/index.txt +2 -0
  77. data/spec/templates.rb +42 -0
  78. data/spec/toc.rb +64 -30
  79. metadata +53 -14
  80. data/spec/samples/titletest/index.txt +0 -2
@@ -3,18 +3,14 @@ require 'yaml'
3
3
  require 'nokogiri'
4
4
 
5
5
  class DocuBot::Page
6
- META_SEPARATOR = /^\+\+\+\s*$/ # Sort of like +++ATH0
7
6
  AUTO_ID_ELEMENTS = %w[ h1 h2 h3 h4 h5 h6 legend caption dt ].join(',')
8
7
 
9
- attr_reader :pages, :type, :folder, :file, :meta, :nokodoc, :bundle
10
- attr_accessor :parent
8
+ attr_reader :type, :folder, :file, :meta, :nokodoc, :bundle
11
9
 
12
10
  def initialize( bundle, source_path, title=nil )
13
11
  puts "#{self.class}.new( #{source_path.inspect}, #{title.inspect}, #{type.inspect} )" if $DEBUG
14
12
  title ||= File.basename( source_path ).sub( /\.[^.]+$/, '' ).gsub( '_', ' ' ).sub( /^\d+\s/, '' )
15
13
  @bundle = bundle
16
- @meta = { 'title'=>title }
17
- @pages = []
18
14
  @file = source_path
19
15
  if File.directory?( @file )
20
16
  @folder = @file
@@ -23,26 +19,14 @@ class DocuBot::Page
23
19
  else
24
20
  @folder = File.dirname( @file )
25
21
  end
26
- slurp_file_contents if @file
22
+ @type = File.extname( @file )[ 1..-1 ] if @file
23
+ @meta = DocuBot::MetaSection.new( {'title'=>title}, @file )
24
+ @raw = @meta.__contents__
25
+ @raw = nil if @raw && @raw.empty?
26
+
27
27
  create_nokodoc
28
28
  end
29
29
 
30
- def slurp_file_contents
31
- @type = File.extname( @file )[ 1..-1 ]
32
- parts = IO.read( @file ).split( META_SEPARATOR, 2 )
33
-
34
- if parts.length > 1
35
- # Make YAML friendler to n00bs
36
- yaml = YAML.load( parts.first.gsub( /^\t/, ' ' ) )
37
- @meta.merge!( yaml )
38
- end
39
-
40
- # Raw markup, untransformed, needs content
41
- if @raw = parts.last && parts.last.strip
42
- @raw = nil if @raw.empty?
43
- end
44
- end
45
-
46
30
  def create_nokodoc
47
31
  # Directories with no index.* file will not have any @raw
48
32
  # Pages with metasection only will also not have any @raw
@@ -50,7 +34,7 @@ class DocuBot::Page
50
34
  html = DocuBot::process_snippets( self, @raw )
51
35
  html = DocuBot::convert_to_html( self, html, @type )
52
36
  end
53
- @nokodoc = Nokogiri::HTML(html || "")
37
+ @nokodoc = Nokogiri::HTML::DocumentFragment.parse(html || "")
54
38
  auto_id
55
39
  auto_section
56
40
  @nokodoc
@@ -63,10 +47,11 @@ class DocuBot::Page
63
47
  # Add IDs to elements that don't have them
64
48
  def auto_id
65
49
  # ...but only if a toc entry might reference one, or requested.
66
- if (@meta['auto-id']==true) || (@meta['toc'] && @meta['toc'][','])
50
+ if @meta['auto-id'].as_boolean || @meta.toc.as_list.any?{ |toc| !toc['#'] }
67
51
  @nokodoc.css( AUTO_ID_ELEMENTS ).each do |node|
68
52
  next if node.has_attribute?('id')
69
- node['id'] = DocuBot.id_from_text(node.inner_text)
53
+ # Strip off the unwanted leading '#'
54
+ node['id'] = DocuBot.id_from_text(node.inner_text)[1..-1]
70
55
  end
71
56
  dirty_doc
72
57
  end
@@ -75,11 +60,10 @@ class DocuBot::Page
75
60
  # Wrap siblings of headers in <div class='section'>
76
61
  def auto_section
77
62
  return if @meta['auto-section']==false
78
- return unless body = @nokodoc.at_css('body')
79
63
 
80
64
  #TODO: Make this a generic nokogiri call on any node (like body) where you can pass in a hierarchy of elements and a wrapper
81
65
  stack = []
82
- body.children.each do |node|
66
+ @nokodoc.children.each do |node|
83
67
  # non-matching nodes will get level of 0
84
68
  level = node.name[ /h([1-6])/i, 1 ].to_i
85
69
  level = 99 if level == 0
@@ -105,53 +89,14 @@ class DocuBot::Page
105
89
  case key[-1..-1] # the last character of the method name
106
90
  when '?' then @meta.has_key?( key[0..-2] )
107
91
  when '!','=' then super
108
- else @meta[ key ]
92
+ else
93
+ # warn "Unknown attribute #{key.inspect} asked for on #{@file || @folder}" unless @meta.has_key?( key )
94
+ @meta[ key ]
109
95
  end
110
96
  end
111
- def ancestors
112
- page = self
113
- anc = []
114
- anc.unshift( page ) while page = page.parent
115
- anc
116
- end
117
- def sections
118
- @pages.reject{ |e| e.pages.empty? }
119
- end
120
- def leafs
121
- @pages.select{ |e| e.pages.empty? }
122
- end
123
- def every_leaf
124
- (leafs + sub_sections.map{ |sub| sub.every_leaf }).flatten
125
- end
126
-
127
- def every_section
128
- (sub_sections + sub_sections.map{ |sub| sub.every_section }).flatten
129
- end
130
-
131
- def descendants
132
- (@pages + @pages.map{ |page| page.descendants }).flatten
133
- end
134
- alias_method :every_page, :descendants
135
-
136
- def <<( entry )
137
- @pages << entry
138
- entry.parent = self
139
- end
140
-
141
- def leaf?
142
- @pages.empty? || @pages.all?{ |x| x.is_a?(DocuBot::SubLink) }
143
- end
144
-
145
- def depth
146
- @_depth ||= self==@bundle.toc ? 0 : @file ? @file.count('/') : @folder.count('/') + 1
147
- end
148
-
149
- def root
150
- @_root ||= "../" * depth
151
- end
152
97
 
153
98
  def html_path
154
- @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
99
+ @html_path ||= @file ? @file.sub( /[^.]+$/, 'html' ) : ( @folder / 'index.html' )
155
100
  end
156
101
 
157
102
  # Call this if the source generated by the converter would change
@@ -173,64 +118,41 @@ class DocuBot::Page
173
118
  end
174
119
 
175
120
  def content_html
176
- # Nokogiri 'helpfully' wraps our content in a full HTML page
177
- # but apparently doesn't create a body for no content.
178
- @content_html ||= (body=nokodoc.at_css('body')) && body.children.to_html
121
+ @content_html ||= nokodoc.to_html
122
+ end
123
+
124
+ def children
125
+ @bundle.toc.children(html_path).map{ |node| node.page }.uniq.compact
126
+ end
127
+
128
+ def leaf?
129
+ @leaf ||= !children.any?{ |page| page != self }
130
+ end
131
+
132
+ def depth
133
+ @depth ||= html_path.scan('/').length
134
+ end
135
+
136
+ def root
137
+ @root ||= "../" * depth
179
138
  end
180
139
 
140
+ #TODO: cache this is people keep calling to_html and it's a problem
181
141
  def to_html
182
- #TODO: cache this is people keep calling to_html and it's a problem
183
- @meta['template'] ||= leaf? ? 'page' : 'section'
142
+ @meta.template ||= leaf? ? 'page' : 'section'
184
143
 
185
144
  master_templates = DocuBot::TEMPLATE_DIR
186
145
  source_templates = @bundle.source / '_templates'
187
-
188
146
  tmpl = source_templates / "#{template}.haml"
189
147
  tmpl = master_templates / "#{template}.haml" unless File.exists?( tmpl )
190
148
  tmpl = master_templates / "page.haml" unless File.exists?( tmpl )
191
- haml = Haml::Engine.new( IO.read( tmpl ), DocuBot::Writer::HAML_OPTIONS )
192
- haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.toc, :root=>root )
149
+ tmpl = IO.read( tmpl )
150
+ haml = Haml::Engine.new( tmpl, DocuBot::Writer::HAML_OPTIONS )
151
+ haml.render( Object.new, :contents=>content_html, :page=>self, :global=>@bundle.global, :root=>root )
193
152
  end
194
153
 
195
- end
196
-
197
- class DocuBot::SubLink
198
- attr_reader :page, :title, :id
199
- def initialize( page, title, id )
200
- @page, @title, @id = page, title, id
201
- end
202
- def html_path
203
- "#{@page.html_path}##{@id}"
204
- end
205
- def leaf?
206
- true
207
- end
208
- def pages
209
- []
210
- end
211
- alias_method :descendants, :pages
212
- def depth
213
- @page.depth
214
- end
215
- def parent
216
- @page
217
- end
218
- def parent=( page )
219
- @page = page
220
- end
221
- def to_html
222
- ""
223
- end
224
- def ancestors
225
- @page.ancestors
226
- end
227
- def method_missing(*args)
228
- nil
229
- end
230
- def hide
231
- false
232
- end
233
- def sublink?
234
- true
154
+ def inspect
155
+ "<#{self.class} '#{self.title}' #{@file ? "@file=#{@file.inspect}" : "@folder=#{@folder.inspect}"}>"
235
156
  end
157
+
236
158
  end
@@ -1,7 +1,5 @@
1
- toc: #bones-and-groups #rigid-body-type #physical-mesh
1
+ toc: #bones-and-groups, #rigid-body-type, #physical-mesh
2
2
  +++
3
- * Use `toc: hide` to remove a page from the table of contents.
4
- * Use `toc: #list #of-html #identifiers` to add specific elements on the page,
3
+ * Use `toc: #list, #of-html, #identifiers` to add specific elements on the page,
5
4
  referenced by HTML id, as sub-section links in the table of contents.
6
5
  The contents of the HTML tag with that id will be used as the index header.
7
- * TODO: Use `icon: <somename>` to specify a specific CHM icon in the TOC.
@@ -44,9 +44,10 @@ body.nosidebar{
44
44
 
45
45
  div#mainbody
46
46
  {
47
- font-size:90%;
47
+ font-size:8pt;
48
48
  margin: 0 350px 0 10px;
49
49
  padding: 0;
50
+ padding-bottom:30em;
50
51
  }
51
52
 
52
53
  body.nosidebar div#mainbody
@@ -61,7 +62,7 @@ div#mainbody
61
62
 
62
63
  div.section
63
64
  {
64
- margin-left:2em
65
+ margin-left:1em
65
66
  }
66
67
 
67
68
  a:link, a:visited { white-space:nowrap; color:#6e8e34 }
@@ -238,6 +239,7 @@ sup
238
239
  font-size: 70%;
239
240
  margin-bottom: 0;
240
241
  margin-top: 1em;
242
+ padding-bottom:10em;
241
243
  }
242
244
 
243
245
  #pagebody h1 {
@@ -247,11 +249,14 @@ sup
247
249
 
248
250
  fieldset { margin-bottom:1.5em; margin-left:1em; padding:0.5em; border-right:none; }
249
251
  legend { font-weight:bold; color:#666 }
250
- fieldset dl { margin:0 2em }
252
+ fieldset dl { margin:0 1em }
251
253
  fieldset p { margin:1em 2em }
252
- dl.section { margin-left:2em }
254
+ dl.section { margin-left:1em }
253
255
  dt { font-weight:bold; font-style:normal; margin-top:0.8em }
254
- dd { margin-bottom:0.8em }
256
+ dd { margin-bottom:0.8em; margin-left:0; }
257
+ dd p { margin-bottom:0.4em }
258
+ dd ul { margin-left:3em; margin-top:0.4em; }
259
+ dd ul li { margin-bottom:0 }
255
260
 
256
261
  div#pagefooter {
257
262
  margin-top:10em ! important; margin-bottom:1em ! important; width:25em; overflow:hidden; white-space:nowrap;
@@ -2,3 +2,4 @@
2
2
  #glossary-close { width:24px; height:24px; background:url(close.png) no-repeat; position:absolute; right:-12px; top:-12px; cursor:pointer; }
3
3
  #glossary-box * { margin:0 }
4
4
  span.glossary { color:#36401C; border-bottom:1px dashed #6e8e34; cursor:help; }
5
+ span.glossary-missing { border-bottom:1px dotted #900 }
@@ -14,7 +14,17 @@ function glossaryClick(evt){
14
14
 
15
15
  handleEvent(window,'load',function(){
16
16
  for ( var spans=document.getElementsByTagName('span'),i=spans.length-1; i>=0; --i ){
17
- if (cssClass.has(spans[i],'glossary')) handleEvent(spans[i],'click',glossaryClick);
17
+ var span = spans[i];
18
+ if (cssClass.has(span,'glossary')){
19
+ var term = span.getAttribute('term') || span.innerHTML;
20
+ if ($glossaryTerms[term.toLowerCase()]){
21
+ handleEvent(span,'click',glossaryClick);
22
+ }else{
23
+ cssClass.kill(span,'glossary');
24
+ cssClass.add(span,'glossary-missing');
25
+ }
26
+
27
+ }
18
28
  }
19
29
  var box = document.getElementById('glossary-box');
20
30
  var close = document.getElementById('glossary-close');
@@ -0,0 +1,9 @@
1
+ %p The following pages are part of <em>#{page.title}</em>:
2
+ %ul#children
3
+ - page.pages.each do |child_page|
4
+ %li
5
+ %a{ :href=>root/child_page.html_path }= child_page.title
6
+ - if child_page.summary?
7
+ = ": #{child_page.summary}"
8
+ - unless contents.nil? or contents.empty?
9
+ #good-info= contents
@@ -1,21 +1,14 @@
1
- !!! Strict
2
- %html
3
- %head
4
- %meta(http-equiv='Content-Type' content='text/html; charset=utf-8')
5
- %title= global.title + " Index"
6
- %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}common.css", :media=>'all'}
7
- %body
8
- -# TODO: Breakdown by letter.
9
- %ul#index
10
- - global.index.entries.sort_by{ |k,p| k.downcase }.each do |keyword,pages|
11
- - if pages.length == 1
12
- %li
13
- %a{ :href => pages.first.html_path }= keyword
14
- - else
15
- %li
16
- = keyword
17
- %ul
18
- - pages.each do |page|
19
- %li
20
- %a{ :href => page.html_path }= page.title
1
+ -# TODO: Breakdown by letter.
2
+ %ul#index
3
+ - global.index.entries.sort_by{ |k,p| k.downcase }.each do |keyword,pages|
4
+ - if pages.length == 1
5
+ %li
6
+ %a{ :href => pages.first.html_path }= keyword
7
+ - else
8
+ %li
9
+ = keyword
10
+ %ul
11
+ - pages.each do |page|
12
+ %li
13
+ %a{ :href => page.html_path }= page.title
21
14
 
@@ -1,10 +1,7 @@
1
1
  %p The following pages are part of <em>#{page.title}</em>:
2
2
  %ul#children
3
- - page.pages.each do |child_page|
3
+ - page.children.each do |child_page|
4
4
  %li #{child_page.title}#{": #{child_page.summary}" if child_page.summary?}
5
5
  - if contents
6
- %p
7
- %strong
8
- Before you go in, you should probably know the following:
9
6
  #good-info= contents
10
7
 
@@ -1,12 +1,3 @@
1
- !!! Strict
2
- %html
3
- %head
4
- %meta(http-equiv='Content-Type' content='text/html; charset=utf-8')
5
- %title= global.title
6
- %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}common.css", :media=>'all'}
7
- %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}toc.css", :media=>'all'}
8
- %script{:type=>'text/javascript', :src=>"#{root}toc.js"}
9
- %body
10
- %ul#toc
11
- - li_pages_for global
1
+ %ul#toc
2
+ - li_pages_for global.toc
12
3
 
@@ -4,18 +4,21 @@
4
4
  %meta(http-equiv='Content-Type' content='text/html; charset=utf-8')
5
5
  %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}common.css", :media=>'all'}
6
6
  %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}glossary.css", :media=>'all'}
7
+ - if custom_css
8
+ %link{:rel=>'stylesheet', :type=>'text/css', :href=>"#{root}#{custom_css}", :media=>'all'}
7
9
  %script{:type=>'text/javascript', :src=>"#{root}glossary-terms.js"}
8
10
  %script{:type=>'text/javascript', :src=>"#{root}glossary.js"}
11
+ - if custom_js
12
+ %script{:type=>'text/javascript', :src=>"#{root}#{custom_js}"}
9
13
  %title= page.title
10
14
  %body{ :class=>page.style }
11
15
  #content
12
16
  #pagetop
13
17
  #breadcrumb
14
- - toc = page.ancestors.first # The TOC has no page to link to
15
- = toc.short_title || toc.title
18
+ = global.short_title || global.title
16
19
  %span.sep &gt;
17
- - page.ancestors[1..-1].each do |dad|
18
- %a{ :href=>root/dad.html_path }= dad.short_title || dad.title
20
+ - breadcrumb.each do |node|
21
+ %a{ :href=>root/node.link }= node.page.short_title || node.title
19
22
  %span.sep &gt;
20
23
  = page.title
21
24
  %h1#title= page.title
@@ -11,6 +11,7 @@ class DocuBot::CHMWriter < DocuBot::HTMLWriter
11
11
  lap = Time.now
12
12
  @chm_path = destination || "#{@bundle.source}.chm"
13
13
  @toc = @bundle.toc
14
+ @global = @bundle.global
14
15
  write_hhc
15
16
  write_hhk
16
17
  write_hhp
@@ -63,20 +64,20 @@ class DocuBot::CHMWriter < DocuBot::HTMLWriter
63
64
  def write_hhp
64
65
  @hhp = @chm_path.sub( /[^.]+$/, 'hhp' )
65
66
 
66
- if @toc.default?
67
+ if @global.default
67
68
  # User tried to specify the default page
68
- @default_topic = @toc.descendants.find{ |page| page.title==@toc.default }
69
+ @default_topic = @bundle.pages_by_title[ @global.default ].first
69
70
  if @default_topic
70
71
  if @default_topic.file =~ /\s/
71
72
  warn "'#{@toc.default}' cannot be the default CHM page; it has a space in the file name."
72
73
  @default_topic = nil
73
74
  end
74
75
  else
75
- warn "The requested default page '#{@toc.default}' could not be found. (Did the title change?)"
76
+ warn "The requested default page '#{@global.default}' could not be found. (Did the title change?)"
76
77
  end
77
78
  end
78
- @default_topic ||= @toc.descendants.find{ |page| page.file =~ /^\S+$/ }
79
- warn "No default page is set, because no page has a file name without spaces." unless @default_topic
79
+ @default_topic ||= @toc.descendants.find{ |node| node.link =~ /^\S+$/ }
80
+ warn "No default page is set, because no page has a path without spaces." unless @default_topic
80
81
 
81
82
  File.open( @hhp, 'w' ) do |f|
82
83
  f << ERB.new( IO.read( SUPPORT / 'hhp.erb' ) ).result( binding )