Wednesday 30 September 2015

Automatically Generated Nav Bar for Jekyll and Github pages

Github pages, which I use for the Simul documentation, uses Jekyll, which is great for building static websites and blogs.

I wanted to create an automatic two-level nav bar for my site, and after much searching, I found this approach, using the url's of the pages. The implementation didn't quite work for me, and I wanted multi-level navigation, so this is what I came up with:
      
      {% assign url_parts = page.url | split: '/' %}
      {% assign url_parts_size = url_parts | size %}
      {% if url_parts_size != 0 %}
        {% assign rm = url_parts | last %}
        {% assign base_url = page.url | replace: rm %}
      {% else %}
        {% assign base_url = page.url %}
      {% endif %}
      <ul>
      {% for node in site.pages %}
        {% assign node_url_parts = node.url | split: '/' %}
        {% assign node_url_parts_size = node_url_parts | size %}
        {% assign filename = node_url_parts | last %}
       
        {% if node_url_parts_size == 2  %}
            <li>
            <a class="sidebar-nav-item{% if page.url == node.url %} active{% endif %}" href="{{ node.url }}">{{ node.title }}</a></li>
            {% if page.url contains node.url %}
              <ul>
              {% for node2 in site.pages %}
                {% if node2.url contains node.url %}
                  <li><a href='{{node2.url}}'>{{node2.title}}</a></li>
                {% endif %}
              {% endfor %}
              </ul>
            {% endif %}
        {% endif %}
      {% endfor %}
      {% for num in (1...menu_level) %}
        </ul>
      {% endfor %}
      </ul>
      
I think this works. It just looks at all the site's pages, and prints up links for those with two parts to the URL: these are the first sub-level. If that URL is part of the URL of the current page, it does another loop, to find the current page's siblings.

UPDATE:

You could add further nav levels by putting extra inner for loops. Here's a more complete example from my site, that goes to three levels. (See also http://dbgdiary.blogspot.com/2015/11/piping-doxygen-output-to-github-pages.html Done)

{% assign sorted_pages = site.pages | sort: "url" %}

    {% assign this_path = "" %}

    {% assign pages = site.array %}
    {% assign paths = site.array %}
    {% assign titles = site.array %}
    {% assign path_sizes = site.array %}
    {% assign this_index = 100000000 %}


We've initialized some empty arrays here using site.array, which is defined in _config.yml at the site root as:

array: []


Next we build the arrays from the page list, tweaking Doxygen's filenames to produce a tree-like structure (doxygen's file structure is flat, but we want a tree of URI's):


    {% for node in sorted_pages %}
        {% assign path = node.url | remove: "/index.html" %}
        {% if path contains "/dir_" or path contains "/struct" or path contains "/functions" or path contains "/namespacemembers" or path contains "graph_legend" or path contains "google" or path contains "-members.html" or path contains "/classes.html" %}
          {% continue %}
        {% endif %}
        {% if path contains "_source.html" %}
          {% assign path = path | replace: "reference/","reference/source/" %}
        {% endif %}
        {% assign path = path | replace: "reference/class","reference/classes/" | replace: "reference/classes/es","reference/classes" | replace: "reference/namespaces","reference/classes" | remove: ".html" | replace: "_1_1","/" | replace: "_01"," " | replace: "_8","." | remove_first: "/" | replace: "_ooo","/" %}
        {% assign psz = path | size %}

        {% assign title = node.title | replace "Namespace List","Reference" | remove: "Files (x86)/Jenkins/jobs/Simul Installers/workspace/Simul/PRODUCTS/TrueSky/Help/" | remove: " Source File" | remove: " Class Reference" %}
        {% if title == "404" or path contains "search-results" %}
          {% continue %}
        {% endif %}
        {% assign path_parts = path | split: '/' %}
        {% assign path_parts_size = path_parts | size %}

        {% assign paths = paths | push: path %}
        {% assign pages = pages | push: node %}
        {% assign path_sizes = path_sizes | push: path_parts_size %}

        {% assign titles = titles | push: title %}
        {% if node.url == page.url %}
          {% assign this_path = path %}
          {% assign this_node = node %}
          {% assign this_index = forloop.index0 %}
        {% endif %}
      {% endfor %}

Having build the arrays, we now iterate through the page URI's from the outermost level to as many levels as we want. The first loop:
      {% assign menu_level = 0 %}
      {% for node in pages %}
        {% assign node_path = paths[forloop.index0] %}
        {% assign node_title = titles[forloop.index0] %}
        {% assign node_path_parts = node_path | split: '/' %}
        {% assign node_path_parts_size = path_sizes[forloop.index0] %}
        {% unless node_path_parts_size == 1 %}
          {% continue %}
        {% endunless %}
<li>
            <a class="sidebar-nav-item{% if page.url == node.url %} active{% endif %} sidebar-1" href="https://www.blogger.com/%7B%7B%20node.url%20%7D%7D">{{ node_title }}</a></li>
{% if this_path contains node_path %}
              {% assign paths2 = site.array %}
              {% assign pages2 = site.array %}
              {% assign titles2 = site.array %}

For the second loop we will iterate through all the pages, but only pick out the ones with two elements in the path, and only the ones that contain the path to the current parent in loop 1. First we build the array, pages2:
{% for node2 in pages %} {% unless path_sizes[forloop.index0] == 2 %} {% continue %} {% endunless %} {% assign path2 = paths[forloop.index0] %} {% if path2 contains node_path and node2.url != node.url %} {% assign paths2 = paths2 | push: path2 %} {% assign pages2 = pages2 | push: node2 %} {% assign titles2 = titles2 | push: titles[forloop.index0] %} {% endif %} {% endfor %}

Now iterate through pages2:

              {% if pages2.size > 0 %}
                {% assign menu_level = 1 %}
                <ul>
                {% for node2 in pages2 %}
<li><a class="sidebar-nav-item{% if page.url == node2.url %} active{% endif %} sidebar-2" href="https://www.blogger.com/%7B%7Bnode2.url%7D%7D">{{ titles2[forloop.index0] }}</a></li>
{% assign path2 = paths2[forloop.index0] %}

                  {% if this_path contains path2 %}
Now the same process for pages3, build the array:
                    {% assign pages3 = site.array %}
                    {% assign titles3 = site.array %}
                    {% for node3 in pages %}
                      {% unless path_sizes[forloop.index0] == 3 %}
                        {% continue %}
                      {% endunless %}
                      {% assign path3 = paths[forloop.index0] %}
                      {% if path3 contains path2 and node3.url != node.url  %}
                        {% assign paths3 = paths3 | push: path3 %}
                        {% assign pages3 = pages3 | push: node3 %}
                        {% assign titles3 = titles3 | push: titles[forloop.index0] %}
                      {% endif %}
                    {% endfor %}

And iterate through the array for the third level:
{% if pages3.size > 0 %} {% assign menu_level = 1 %} <ul> {% for node3 in pages3 %} <li><a class="sidebar-nav-item{% if page.url == node3.url %} active{% endif %} sidebar-3" href="https://www.blogger.com/%7B%7Bnode3.url%7D%7D">{{ titles3[forloop.index0] }}</a></li> {% endfor %} </ul> {% endif%} {% endif %} {% endfor %} </ul> {% endif%} {% endif %} {% endfor %}
That's it for a set of Doxygen html pages - if your output is not from Doxygen the process may be simpler.

No comments:

Post a Comment