docubot 0.3.3 → 0.4

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 (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 )