Mike Slinn

jekyll_outline

Published 2020-10-03. Last modified 2023-05-23.
Time to read: 2 minutes.

This page is part of the jekyll_plugins collection.

This Jekyll tag plugin creates a clickable table of contents.

You can see it in action in several areas of this web site:

Django / Django-Oscar index

Shell
---
auto_redirect_id: 2978707e-d0e7-4a1f-945c-7ed83eac75e6
categories: [Django, Django-Oscar]
date: 2021-02-02
description: A directory listing of my Django and django-oscar posts.
javascriptEnd: /assets/js/clipboard.min.js
javascriptInline: new ClipboardJS('.copyBtn');
last_modified_at: 2021-03-22
layout: django
order: 0
title: About These Django / Oscar Posts
---
<p>
  I was searching for a customizable e-commerce framework for a business case proof of concept,
  and after a
  {% href /blog/2021/01/30/opencart-postgres.html false start %},
  I realized that
  {% href /blog/2021/02/02/ecommerce-requirements.html the Django framework%},
  written in Python, was likely the way to go.
  I felt I would likely be able to add the ERP functionality I needed to a
  custom implementation of the <code>django-oscar</code> e-commerce framework.
</p>
<p>
  Soon after, I discovered that the
  {% href https://django-oscar.readthedocs.io/en/latest <code>django-oscar</code> e-commerce framework %},
  although not without issues, was likely good enough for my needs.
</p>
<p>
  I am documenting my progress in this separate series of posts.
  This information is written as I learn it, but instead of a strictly chronological journal,
  I continuously reorder and revise the information so it is more easily understood by others.
  If you are new to Django and/or <code>django-oscar</code>,
  you should read these posts from the bottom up.
</p>

<div style="text-align: center; vertical-align: middle;">
  {% img
    size="quartersize"
    src="django.webp"
    url="https://www.djangoproject.com"
    wrapper_style="margin-right: 3em; margin-top: 12px;"
  %}
  {% img
    class=" "
    size="quartersize"
    src="/blog/images/django/django-oscar.webp"
    url="https://django-oscar.readthedocs.io/en/latest/"
    wrapper_style="margin-left: 3em;"
  %}
</div>


<h2 id="index">{{ total }} Django and Django-Oscar Posts</h2>
{% outline attribution django %}
0: Django / Oscar Evaluation
400: Notes
800: Digging Deeper
1900: Debugging
2700: Production
{% endoutline %}

Jekyll index

Shell
---
canonical_url: 'https://www.mslinn.com/jekyll/index.html'
date: 2020-12-29
description: "Generating static websites using a highly optimized Jekyll setup."
last_modified_at: 2022-05-01
layout: jekyll
order: 0
title: Jekyll-Generated Sites
---
<!-- #region intro -->
{% img
  align="center"
  class=" "
  size="halfsize"
  src="jekyll.webp"
  url="https://jekyllrb.com"
%}
<p class="alert center rounded shadow">
  {% href /jekyll_plugins/ <b>My Jekyll Plugins are here.</b> %}
</p>
<!-- endregion -->

<!-- #region about -->
<h2 id="about">About This Material</h2>
<p>
  In this corner of my website, I discuss the open-source code
  I wrote (and continue to write) about developing websites generated by Jekyll.
  For this purpose I usually write the following types of code:
  Ruby, bash, HTML 5, CSS, JavaScript; I sometimes use Twitter Bootstrap.
</p>
<p>
  To make these articles possible, I have written and published dozens of Jekyll plugins
  as Ruby gems, all hosted on GitHub as F/OSS software with a
  dual license for medium-sized and larger corporations.
  These articles discuss how to use those plugins, and how they work.
  If you are interested in exploring whether this publishing setup might be suitable for your technical publishing needs, please contact me.
  My co-ordinates are all over this website.
</p>
<!-- endregion -->

<h2 id="articles">Jekyll Articles</h2>
{% outline attribution jekyll %}
0000: General
1000: Setup
2000: Operation
4000: Scripts
6000: Plugins
10000: Creating and Debugging Custom Jekyll Plugins
12000: Jekyll Internals
{% endoutline %}

<!-- #region images -->
<div style="display: none">
  {% img
    align="right"
    class=""
    id="outline_6000"
    src="/blog/jekyll/rubygems_logo_red.webp"
    size="eighthsize"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
  {% img
    align="right"
    id="outline_12000"
    src="/blog/images/magnifying-glass.webp"
    size="eighthsize"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
  {% img
    align="right"
    class=""
    id="outline_10000"
    src="/blog/images/magic_wand.webp"
    size="eighthsize"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
</div>
<!-- endregion -->

{% outline_js wrap_in_script_tag %}

Jekyll Plugins index

Shell
---
categories: [Jekyll]
date: 2023-05-18
description: This is the index for my Jekyll plugins.
last_modified_at: 2023-05-18
layout: jekyll
order: 3000
redirect_from:
  - /blog/2020/10/03/jekyll-plugins.html
  - /jekyll/3000-jekyll-plugins.html
title: My Jekyll Plugins
---
<!-- #region intro -->
<h2 id="packaging" class="smallHead">Packaging</h2>
<p>
  All plugins are packaged as {% href https://rubygems.org/profiles/mslinn RubyGems %},
  and I maintain them on
  {% href follow https://github.com/mslinn?tab=repositories&q=jekyll_&type=&language=&sort= GitHub %}.
  The plugins are available as open source under the terms of the
  {% href https://opensource.org/licenses/MIT MIT License %}.
</p>

<h2 id="use" class="smallHead">Usage</h2>
<p>
  To use Jekyll plugins, you must {% href /ruby/1000-ruby-setup.html set up a Ruby environment %}.
  To learn more about Jekyll, please read my other {% href /jekyll/ articles about Jekyll %}.
</p>
<p>
  The rules for specifying options when using Jekyll tags defined by these plugins are described
  {% href jekyll_plugin_support.html#keyword_options here %}.
</p>

<h3 id="css" class="smallHead">CSS</h3>
<p>
  Many of these plugins were designed with CSS in mind.
  Some CSS is common between certain plugins.
  I&rsquo;ve broken out the CSS into files.
</p>
<p>
  The benefit to you is that regardless of whether you only install one of these plugins
  or install several, you will have manageable CSS.
  Each project&rsquo;s <code>README.md</code> has a section that discusses the CSS files.
</p>


<h2 id="nugem"class="smallHead"><span class="code">Jekyll_plugin_support</span> and <span class="code">Nugem</span></h2>
<p>
  The {% href jekyll_plugin_support.html <code>jekyll_plugin_support</code> %} gem
  is a framework for building Jekyll plugins.
</p>
<p>
  <code>Nugem</code> is a code generator for writing &ldquo;plain old gems&rdquo; and Jekyll plugins that are packaged as gems.
  <code>Nugem</code> uses the <code>jekyll_<wbr>plugin_<wbr>support</code> framework.
</p>
<p>
  Most of my Jekyll plugins began their existence as the scaffolding generated by
  {% href url="/ruby/6800-nugem.html" <code>Nugem</code> %}.
</p>
<!-- endregion -->

{% outline attribution fields="title </a> &ndash; description" jekyll_plugins %}
0000: Organization
2000: Filters
6000: HTML
8000: Utilities
10000: Plugin Components
{% endoutline %}

Installation

Add the following line to your Jekyll project's Gemfile, within the jekyll_plugins group:

Shell
group :jekyll_plugins do 
  gem 'jekyll_outline'
end 

And then execute:

Shell
$ bundle

Fields

By default, each displayed entry consists of a document title, wrapped within an <a href> HTML tag that links to the page for that entry, followed by an indication of whether the document is visible (a draft) or not.

Entries can include following fields: draft, categories, description, date, last_modified_at, layout, order, title, slug, ext, tags, and excerpt.

Specify the fields like this:

Shell
{% outline fields="title  –  description " %}
000: Topic 0..19
020: Topic 20..39
040: Topic 40..
{% endoutline %}

Words that are not a known field are transcribed into the output.

In the above example, notice that the HTML is space delimited from the field names. The parser is simple and stupid: each token is matched against the known keywords. Tokens are separated by white space.

CSS

The CSS used for the demo website should be copied to your project. See the sections of demo/assets/css/styles.css as shown:

demo/assets/css/styles.css
/* Start of jekyll_plugin_support css */
  ... copy this portion ...
/* End of jekyll_plugin_support css */

/* Start of jekyll_outline css */
  ... copy this portion ...
/* End of jekyll_outline css */

JavaScript

This project's outline_js tag returns the JavaScript necessary to position images relating to the outline. If used without parameters it just returns the JavaScript; use the tag this way:

Shell
<script> 
  {%= outline_js %}
</script> 

If passed the wrap_in_script_tag parameter, it wraps the JavaScript in <script></script>. Use the tag this way:

Shell
{% outline_js wrap_in_script_tag %}

Explanation

Given an outline that looks like this:

Shell
{% outline my_collection %}
000: Topic 0..19
020: Topic 20..39
040: Topic 40..
{% endoutline %}

...and given pages in the stuff collection with the following names:

  • 010-published.html has title Published Stuff Post 010
  • 020-unpublished.html has title Unpublished Post 020
  • 030-unpublished.html has title Unpublished Post 030

Then links to the pages in the stuff collection's pages are interleaved into the generated outline like this:

Shell
<div class="outer_posts">
  <h3 class='post_title clear' id="title_0">Topic 0..19</h3>
  <div id='posts_wrapper_0' class='clearfix'>
    <div id='posts_0' class='posts'>
      <span>2022-04-01</span> <span><a href='/stuff/010-published.html'>Published Stuff Post 010</a></span>
      <span>2022-04-17</span> <span><a href='/stuff/020-unpublished.html'>Unpublished Post 020</a> <i class='jekyll_draft'>Draft</i></span>
    </div>
  </div>
  <h3 class='post_title clear' id="title_20">Topic 20..39</h3>
  <div id='posts_wrapper_20' class='clearfix'>
    <div id='posts_20' class='posts'>
      <span>2022-04-17</span> <span><a href='/stuff/030-unpublished.html'>Unpublished Post 030</a> <i class='jekyll_draft'>Draft</i></span>
    </div>
  </div>
</div>

The JavaScript searches for images in the current page that were created by the jekyll_img plugin, and have ids that correspond to outline sections.

Each of following image’s ids have an outline_ prefix, followed by a number, which corresponds to one of the sections. Note that leading zeros in the first column above are not present in the ids below.

If you want to provide images to embed at appropriate locations within the outline, wrap them within an invisible div so the web page does not jump around as the images are loaded.

Shell
<div style="display: none;">
  {% img align="right"
    id="outline_0"
    size="quartersize"
    src="/assets/images/porcelain_washbasin.webp"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
  {% img align="right"
    id="outline_20"
    size="quartersize"
    src="/assets/images/pipes.webp"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
  {% img align="right"
    id="outline_40"
    size="quartersize"
    src="/assets/images/libgit2.webp"
    style="margin-top: 0"
    wrapper_class="clear"
  %}
</div>

The JavaScript identifies the images and repositions them in the DOM such that they follow the appropriate heading. If no image corresponds to a heading, no error or warning is generated. The images can be located anywhere on the page; they will be relocated appropriately. If an image does not correspond to a heading, it is deleted.

Attribution

See the jekyll_plugin_support plugin for an explanation of attribution.

Demo

A demo / test website is provided in the demo directory. It can be used to debug the plugin or to run freely. Please examine the HTML files in the demo to see how the plugin works.

  1. To run the demo freely from the command line, type:
    Shell
    $ demo/_bin/debug -r
  2. View the generated website at localhost:4444.


* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.