10 def self.traverse_data(entries, directory_sort = false, paths = [], key_paths = [], &block)
12 entries.map do |entry|
16 if entry[:type] == 'directory'
17 entry[:children] = traverse_data(entry[:children], directory_sort, paths + [entry[:name]], key_paths + [entry[:key]], &block)
19 block_given? ? block.call(entry) : entry
23 def self.traverse(path, directory_sort = false, paths = [], key_paths = [], &block)
25 entries = Dir.glob(File.join(path,'*')).sort
27 entries.sort_by! { |e| File.directory?(e) ? 1 : 0 } if directory_sort
29 entries.map do |entry|
30 is_dir = File.directory?(entry)
32 data = extract_data(is_dir ? "#{entry}.html" : entry)
34 short_title = data['menu_title'] || data['title']
36 name = entry[/[^\/]+$/] # filename
37 key = name.sub(/^[0-9]+(\-|_)/,'').sub(/\.[^\.]+$/,'') # slug
38 my_paths = paths + [name]
39 my_key_paths = key_paths + [key]
40 url = '/' + my_key_paths.join('/') + '/'
42 without_extension = entry.sub(/\.[^\/\/]+$/,'')
46 title: data['title'] || key,
47 menu_title: short_title || key,
50 type: is_dir ? 'directory' : 'file',
56 children: traverse(entry, directory_sort, my_paths, my_key_paths, &block)
58 h.update extension: File.extname(name), has_dir: File.directory?(without_extension)
62 DIRECTORY_ENTRIES[url] = h
65 block_given? ? block.call(h) : h
69 def self.extract_data(filename)
70 if File.exists?(filename) and !File.directory?(filename) and first3 = File.open(filename) { |fd| fd.read(3) } and first3 == '---'
71 blah = filename.sub(/^_manual\//,'')
72 page = Jekyll::Page.new(@site, '_manual', File.dirname(blah), File.basename(blah))
79 class ManualPage < Jekyll::Page
85 class ManualGenerator < Jekyll::Generator
90 source = site.config['source']
91 destination = site.config['destination']
93 manual_dir = '_manual'
95 # now we need to turn our raw input files into something for jekyll to process
96 # everything is in a directory with it's name and all content is in index.html files
97 # the tmpdir gets removed at the end of this block automatically
99 Dir.mktmpdir do |tmpdir|
101 Manual.traverse manual_dir, true do |entry|
102 output_filename = File.join(tmpdir, entry[:url], "index#{entry[:extension]}")
104 FileUtils.mkdir_p File.dirname(output_filename)
106 next unless entry[:type] == 'file'
108 File.open(output_filename, 'w+') do |f|
109 f << File.read(entry[:filename])
112 relative_filename = File.join(entry[:url], "index#{entry[:extension]}")
114 site.pages << Jekyll::Page.new(site, tmpdir, File.dirname(relative_filename), File.basename(relative_filename))
122 class ManualChildPageTag < Liquid::Tag
124 current = context['page.url'].sub(/[^\/]+$/,'')
126 if entry = DIRECTORY_ENTRIES[current]
128 path = File.join(entry[:filename], '*')
130 entries = entry[:children].map do |child|
131 "<li><a href='#{child[:url]}'>#{child[:title]}</a></li>"
134 "<ul>#{entries.join}</ul>"
139 # generates a big <dl> list of the manual page stucture
141 class ManualTOCTag < Liquid::Tag
143 def process_hierarchy(items_a, items_b)
148 [items_a.length,items_b.length].max.times do |i|
152 current = false if a != b
154 # start incrementing this when we don't have either a or b
155 level += 1 if !a || !b
158 return [false] if a != b
165 position ? [current, position, level + 1] : [current]
168 # def sanitize_NMTOKEN(s)
169 # 'ID'+s.gsub(/[^0-9A-z:_.-]/, '_')
174 @source = '_manual' #context.registers[:site].source
176 @@data_tree ||= Manual.traverse(@source)
178 @site = context.registers[:site]
179 current = context['page.url'].sub(/[^\/]+$/,'')
181 current_a = current.split('/').reject(&:empty?)
183 tree = Manual.traverse_data(@@data_tree) do |entry|
187 url_a = url.split('/').reject(&:empty?)
190 is_current, position, level = *process_hierarchy(current_a, url_a)
192 # this massively speeds up build time by not including the whole menu tree for each page
193 next if depth > 1 && current_a[0] != url_a[0]
196 css_classes << 'active' if is_current
197 css_classes << position.to_s if position
198 css_classes << "#{position}-#{level}" if position && level
199 css_classes << 'other' unless is_current || position || level
201 css_classes << "level-#{depth}"
202 css_classes = css_classes.join(' ')
204 if entry[:type] == 'directory'
206 erb = ::ERB.new <<-HTML
207 <dt class="<%= css_classes %>">
208 <a href="<%= entry[:url] %>"><%= entry[:menu_title] %></a>
210 <dd class="<%= css_classes %>">
211 <% if entry[:children].any? %>
213 <%= entry[:children].join %>
222 directory_filename = entry[:filename].sub(/\.[^\/\.]+$/,'')
224 unless entry[:has_dir]
226 erb = ::ERB.new <<-HTML
227 <dt class="<%= css_classes %>">
228 <a href="<%= entry[:url] %>"><%= entry[:menu_title] %></a>
230 <dd class="<%= css_classes %>">
241 "<dl>#{tree.join}</dl>
242 <script type='text/javascript'><!--
243 offset = document.getElementsByClassName('active')[0].offsetTop;
244 height = document.getElementById('tree').clientHeight;
245 if (offset > (height * .7)) {
246 tree.scrollTop = offset - height * .3;
257 Liquid::Template.register_tag('tree', Manual::ManualTOCTag)
258 Liquid::Template.register_tag('children', Manual::ManualChildPageTag)