Diff of /docs/bootstrap-toc.js [000000] .. [409433]

Switch to unified view

a b/docs/bootstrap-toc.js
1
/*!
2
 * Bootstrap Table of Contents v0.4.1 (http://afeld.github.io/bootstrap-toc/)
3
 * Copyright 2015 Aidan Feldman
4
 * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */
5
(function() {
6
  'use strict';
7
8
  window.Toc = {
9
    helpers: {
10
      // return all matching elements in the set, or their descendants
11
      findOrFilter: function($el, selector) {
12
        // http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/
13
        // http://stackoverflow.com/a/12731439/358804
14
        var $descendants = $el.find(selector);
15
        return $el.filter(selector).add($descendants).filter(':not([data-toc-skip])');
16
      },
17
18
      generateUniqueIdBase: function(el) {
19
        var text = $(el).text();
20
        var anchor = text.trim().toLowerCase().replace(/[^A-Za-z0-9]+/g, '-');
21
        return anchor || el.tagName.toLowerCase();
22
      },
23
24
      generateUniqueId: function(el) {
25
        var anchorBase = this.generateUniqueIdBase(el);
26
        for (var i = 0; ; i++) {
27
          var anchor = anchorBase;
28
          if (i > 0) {
29
            // add suffix
30
            anchor += '-' + i;
31
          }
32
          // check if ID already exists
33
          if (!document.getElementById(anchor)) {
34
            return anchor;
35
          }
36
        }
37
      },
38
39
      generateAnchor: function(el) {
40
        if (el.id) {
41
          return el.id;
42
        } else {
43
          var anchor = this.generateUniqueId(el);
44
          el.id = anchor;
45
          return anchor;
46
        }
47
      },
48
49
      createNavList: function() {
50
        return $('<ul class="nav"></ul>');
51
      },
52
53
      createChildNavList: function($parent) {
54
        var $childList = this.createNavList();
55
        $parent.append($childList);
56
        return $childList;
57
      },
58
59
      generateNavEl: function(anchor, text) {
60
        var $a = $('<a></a>');
61
        $a.attr('href', '#' + anchor);
62
        $a.text(text);
63
        var $li = $('<li></li>');
64
        $li.append($a);
65
        return $li;
66
      },
67
68
      generateNavItem: function(headingEl) {
69
        var anchor = this.generateAnchor(headingEl);
70
        var $heading = $(headingEl);
71
        var text = $heading.data('toc-text') || $heading.text();
72
        return this.generateNavEl(anchor, text);
73
      },
74
75
      // Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
76
      getTopLevel: function($scope) {
77
        for (var i = 1; i <= 6; i++) {
78
          var $headings = this.findOrFilter($scope, 'h' + i);
79
          if ($headings.length > 1) {
80
            return i;
81
          }
82
        }
83
84
        return 1;
85
      },
86
87
      // returns the elements for the top level, and the next below it
88
      getHeadings: function($scope, topLevel) {
89
        var topSelector = 'h' + topLevel;
90
91
        var secondaryLevel = topLevel + 1;
92
        var secondarySelector = 'h' + secondaryLevel;
93
94
        return this.findOrFilter($scope, topSelector + ',' + secondarySelector);
95
      },
96
97
      getNavLevel: function(el) {
98
        return parseInt(el.tagName.charAt(1), 10);
99
      },
100
101
      populateNav: function($topContext, topLevel, $headings) {
102
        var $context = $topContext;
103
        var $prevNav;
104
105
        var helpers = this;
106
        $headings.each(function(i, el) {
107
          var $newNav = helpers.generateNavItem(el);
108
          var navLevel = helpers.getNavLevel(el);
109
110
          // determine the proper $context
111
          if (navLevel === topLevel) {
112
            // use top level
113
            $context = $topContext;
114
          } else if ($prevNav && $context === $topContext) {
115
            // create a new level of the tree and switch to it
116
            $context = helpers.createChildNavList($prevNav);
117
          } // else use the current $context
118
119
          $context.append($newNav);
120
121
          $prevNav = $newNav;
122
        });
123
      },
124
125
      parseOps: function(arg) {
126
        var opts;
127
        if (arg.jquery) {
128
          opts = {
129
            $nav: arg
130
          };
131
        } else {
132
          opts = arg;
133
        }
134
        opts.$scope = opts.$scope || $(document.body);
135
        return opts;
136
      }
137
    },
138
139
    // accepts a jQuery object, or an options object
140
    init: function(opts) {
141
      opts = this.helpers.parseOps(opts);
142
143
      // ensure that the data attribute is in place for styling
144
      opts.$nav.attr('data-toggle', 'toc');
145
146
      var $topContext = this.helpers.createChildNavList(opts.$nav);
147
      var topLevel = this.helpers.getTopLevel(opts.$scope);
148
      var $headings = this.helpers.getHeadings(opts.$scope, topLevel);
149
      this.helpers.populateNav($topContext, topLevel, $headings);
150
    }
151
  };
152
153
  $(function() {
154
    $('nav[data-toggle="toc"]').each(function(i, el) {
155
      var $nav = $(el);
156
      Toc.init($nav);
157
    });
158
  });
159
})();