|
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 |
})(); |