--- a
+++ b/docs/bootstrap-toc.js
@@ -0,0 +1,159 @@
+/*!
+ * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/)
+ * Copyright 2015 Aidan Feldman
+ * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
+(function() {
+  'use strict';
+
+  window.Toc = {
+    helpers: {
+      // return all matching elements in the set, or their descendants
+      findOrFilter: function($el, selector) {
+        // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
+        // http://stackoverflow.com/a/12731439/358804
+        var $descendants = $el.find(selector);
+        return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])');
+      },
+
+      generateUniqueIdBase: function(el) {
+        var text = $(el).text();
+        var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-');
+        return anchor || el.tagName.toLowerCase();
+      },
+
+      generateUniqueId: function(el) {
+        var anchorBase = this.generateUniqueIdBase(el);
+        for (var i = 0; ; i++) {
+          var anchor = anchorBase;
+          if (i > 0) {
+            // add suffix
+            anchor += '-' + i;
+          }
+          // check if ID already exists
+          if (!document.getElementById(anchor)) {
+            return anchor;
+          }
+        }
+      },
+
+      generateAnchor: function(el) {
+        if (el.id) {
+          return el.id;
+        } else {
+          var anchor = this.generateUniqueId(el);
+          el.id = anchor;
+          return anchor;
+        }
+      },
+
+      createNavList: function() {
+        return $('<ul class="nav"></ul>');
+      },
+
+      createChildNavList: function($parent) {
+        var $childList = this.createNavList();
+        $parent.append($childList);
+        return $childList;
+      },
+
+      generateNavEl: function(anchor, text) {
+        var $a = $('<a></a>');
+        $a.attr('href', '#' + anchor);
+        $a.text(text);
+        var $li = $('<li></li>');
+        $li.append($a);
+        return $li;
+      },
+
+      generateNavItem: function(headingEl) {
+        var anchor = this.generateAnchor(headingEl);
+        var $heading = $(headingEl);
+        var text = $heading.data('toc-text') || $heading.text();
+        return this.generateNavEl(anchor, text);
+      },
+
+      // Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
+      getTopLevel: function($scope) {
+        for (var i = 1; i <= 6; i++) {
+          var $headings = this.findOrFilter($scope, 'h' + i);
+          if ($headings.length > 1) {
+            return i;
+          }
+        }
+
+        return 1;
+      },
+
+      // returns the elements for the top level, and the next below it
+      getHeadings: function($scope, topLevel) {
+        var topSelector = 'h' + topLevel;
+
+        var secondaryLevel = topLevel + 1;
+        var secondarySelector = 'h' + secondaryLevel;
+
+        return this.findOrFilter($scope, topSelector + ',' + secondarySelector);
+      },
+
+      getNavLevel: function(el) {
+        return parseInt(el.tagName.charAt(1), 10);
+      },
+
+      populateNav: function($topContext, topLevel, $headings) {
+        var $context = $topContext;
+        var $prevNav;
+
+        var helpers = this;
+        $headings.each(function(i, el) {
+          var $newNav = helpers.generateNavItem(el);
+          var navLevel = helpers.getNavLevel(el);
+
+          // determine the proper $context
+          if (navLevel === topLevel) {
+            // use top level
+            $context = $topContext;
+          } else if ($prevNav && $context === $topContext) {
+            // create a new level of the tree and switch to it
+            $context = helpers.createChildNavList($prevNav);
+          } // else use the current $context
+
+          $context.append($newNav);
+
+          $prevNav = $newNav;
+        });
+      },
+
+      parseOps: function(arg) {
+        var opts;
+        if (arg.jquery) {
+          opts = {
+            $nav: arg
+          };
+        } else {
+          opts = arg;
+        }
+        opts.$scope = opts.$scope || $(document.body);
+        return opts;
+      }
+    },
+
+    // accepts a jQuery object, or an options object
+    init: function(opts) {
+      opts = this.helpers.parseOps(opts);
+
+      // ensure that the data attribute is in place for styling
+      opts.$nav.attr('data-toggle', 'toc');
+
+      var $topContext = this.helpers.createChildNavList(opts.$nav);
+      var topLevel = this.helpers.getTopLevel(opts.$scope);
+      var $headings = this.helpers.getHeadings(opts.$scope, topLevel);
+      this.helpers.populateNav($topContext, topLevel, $headings);
+    }
+  };
+
+  $(function() {
+    $('nav[data-toggle="toc"]').each(function(i, el) {
+      var $nav = $(el);
+      Toc.init($nav);
+    });
+  });
+})();