require 'erb' require 'fileutils' require 'tmpdir' require 'pp' module Manual DIRECTORY_ENTRIES = {} def self.traverse_data(entries, directory_sort = false, paths = [], key_paths = [], &block) entries.map do |entry| entry = entry.dup if entry[:type] == 'directory' entry[:children] = traverse_data(entry[:children], directory_sort, paths + [entry[:name]], key_paths + [entry[:key]], &block) end block_given? ? block.call(entry) : entry end end def self.traverse(path, directory_sort = false, paths = [], key_paths = [], &block) entries = Dir.glob(File.join(path,'*')).sort entries.sort_by! { |e| File.directory?(e) ? 1 : 0 } if directory_sort entries.map do |entry| is_dir = File.directory?(entry) data = extract_data(is_dir ? "#{entry}.html" : entry) short_title = data['menu_title'] || data['title'] name = entry[/[^\/]+$/] # filename key = name.sub(/^[0-9]+(\-|_)/,'').sub(/\.[^\.]+$/,'') # slug my_paths = paths + [name] my_key_paths = key_paths + [key] url = '/' + my_key_paths.join('/') + '/' without_extension = entry.sub(/\.[^\/\/]+$/,'') h = { name: name, title: data['title'] || key, menu_title: short_title || key, key: key, filename: entry, type: is_dir ? 'directory' : 'file', url: url } if is_dir h.update \ children: traverse(entry, directory_sort, my_paths, my_key_paths, &block) else h.update extension: File.extname(name), has_dir: File.directory?(without_extension) end if is_dir DIRECTORY_ENTRIES[url] = h end block_given? ? block.call(h) : h end.compact end def self.extract_data(filename) if File.exists?(filename) and !File.directory?(filename) and first3 = File.open(filename) { |fd| fd.read(3) } and first3 == '---' blah = filename.sub(/^_manual\//,'') page = Jekyll::Page.new(@site, '_manual', File.dirname(blah), File.basename(blah)) page.data else {} end end class ManualPage < Jekyll::Page def initialize(*args) super end end class ManualGenerator < Jekyll::Generator safe true def generate(site) source = site.config['source'] destination = site.config['destination'] manual_dir = '_manual' # now we need to turn our raw input files into something for jekyll to process # everything is in a directory with it's name and all content is in index.html files # the tmpdir gets removed at the end of this block automatically Dir.mktmpdir do |tmpdir| Manual.traverse manual_dir, true do |entry| output_filename = File.join(tmpdir, entry[:url], "index#{entry[:extension]}") FileUtils.mkdir_p File.dirname(output_filename) next unless entry[:type] == 'file' File.open(output_filename, 'w+') do |f| f << File.read(entry[:filename]) end relative_filename = File.join(entry[:url], "index#{entry[:extension]}") site.pages << Jekyll::Page.new(site, tmpdir, File.dirname(relative_filename), File.basename(relative_filename)) end end end end class ManualChildPageTag < Liquid::Tag def render(context) current = context['page.url'].sub(/[^\/]+$/,'') if entry = DIRECTORY_ENTRIES[current] path = File.join(entry[:filename], '*') entries = entry[:children].map do |child| "
  • #{child[:title]}
  • " end.uniq "" end end end # generates a big
    list of the manual page stucture class ManualTOCTag < Liquid::Tag def process_hierarchy(items_a, items_b) current = true position = nil level = -1 [items_a.length,items_b.length].max.times do |i| a = items_a[i] b = items_b[i] current = false if a != b # start incrementing this when we don't have either a or b level += 1 if !a || !b if a && b return [false] if a != b elsif a position = :parent elsif b position = :child end end position ? [current, position, level + 1] : [current] end def sanitize_NMTOKEN(s) 'ID'+s.gsub(/[^0-9A-z:_.-]/, '_') end def render(context) @source = '_manual' #context.registers[:site].source @@data_tree ||= Manual.traverse(@source) @site = context.registers[:site] current = context['page.url'].sub(/[^\/]+$/,'') current_a = current.split('/').reject(&:empty?) tree = Manual.traverse_data(@@data_tree) do |entry| url = entry[:url] url_a = url.split('/').reject(&:empty?) depth = url_a.length is_current, position, level = *process_hierarchy(current_a, url_a) # this massively speeds up build time by not including the whole menu tree for each page next if depth > 1 && current_a[0] != url_a[0] css_classes = [] css_classes << 'active' if is_current css_classes << position.to_s if position css_classes << "#{position}-#{level}" if position && level css_classes << 'other' unless is_current || position || level css_classes << "level-#{depth}" css_classes = css_classes.join(' ') if entry[:type] == 'directory' erb = ::ERB.new <<-HTML
    <%= entry[:menu_title] %>
    <% if entry[:children].any? %>
    <%= entry[:children].join %>
    <% end %>
    HTML erb.result(binding) else directory_filename = entry[:filename].sub(/\.[^\/\.]+$/,'') unless entry[:has_dir] erb = ::ERB.new <<-HTML
    <%= entry[:menu_title] %>
    HTML erb.result(binding) end end end "
    #{tree.join}
    " end end end Liquid::Template.register_tag('tree', Manual::ManualTOCTag) Liquid::Template.register_tag('children', Manual::ManualChildPageTag)