module Locomotive module Liquid module Tags # Display the children pages of the site, current page or the parent page. If not precised, nav is applied on the current page. # The html output is based on the ul/li tags. # # Passing through depth will control how many nested children are output # # Usage: # # {% nav site %} => # # {% nav site, no_wrapper: true, exclude: 'contact|about', id: 'main-nav', class: 'nav', active_class: 'on' } # class Nav < ::Liquid::Tag Syntax = /(#{::Liquid::Expression}+)?/ def initialize(tag_name, markup, tokens, context) if markup =~ Syntax @source = ($1 || 'page').gsub(/"|'/, '') @options = { :id => 'nav', :depth => 1, :class => '', :active_class => 'on' } markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } @options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude] @options[:add_attributes] = [] if @options[:snippet] template = @options[:snippet].include?('{') ? @options[:snippet] : context[:site].snippets.where(:slug => @options[:snippet] ).try(:first).try(:template) unless template.blank? @options[:liquid_render] = ::Liquid::Template.parse( template ) @options[:add_attributes] = ['editable_elements'] end end else raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav ") end super end def render(context) children_output = [] entries = fetch_entries(context) entries.each_with_index do |p, index| css = [] css << 'first' if index == 0 css << 'last' if index == entries.size - 1 children_output << render_entry_link(p, css.join(' '), 1) end output = children_output.join("\n") if @options[:no_wrapper] != 'true' list_class = !@options[:class].blank? ? %( class="#{@options[:class]}") : '' output = %{} end output end private # Determines root node for the list def fetch_entries(context) @site, @page = context.registers[:site], context.registers[:page] children = (case @source when 'site' then @site.pages.root.minimal_attributes( @options[:add_attributes] ).first # start from home page when 'parent' then @page.parent || @page when 'page' then @page else @site.pages.fullpath(@source).minimal_attributes( @options[:add_attributes] ).first end).children_with_minimal_attributes( @options[:add_attributes] ).to_a children.delete_if { |p| !include_page?(p) } end # Returns a list element, a link to the page and its children def render_entry_link(page, css, depth) selected = @page.fullpath =~ /^#{page.fullpath}/ ? " #{@options[:active_class]}" : '' icon = @options[:icon] ? '' : '' title = @options[:liquid_render] ? @options[:liquid_render].render( 'page' => page ) : page.title label = %{#{icon if @options[:icon] != 'after' }#{title}#{icon if @options[:icon] == 'after' }} output = %{} output.strip end # Recursively creates a nested unordered list for the depth specified def render_entry_children(page, depth) output = %{} children = page.children_with_minimal_attributes( @options[:add_attributes] ).reject { |c| !include_page?(c) } if children.present? output = %{} end output end # Determines whether or not a page should be a part of the menu def include_page?(page) if !page.listed? || page.templatized? || !page.published? false elsif @options[:exclude] (page.fullpath =~ @options[:exclude]).nil? else true end end ::Liquid::Template.register_tag('nav', Nav) end end end end