Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
galaxyproject
GitHub Repository: galaxyproject/training-material
Path: blob/main/assets/js/bootstrap-toc.min.js
2612 views
1
/*!
2
* Bootstrap Table of Contents v1.0.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
16
.filter(selector)
17
.add($descendants)
18
.filter(":not([data-toc-skip])");
19
},
20
21
generateUniqueIdBase: function(el) {
22
var text = $(el).text();
23
24
// adapted from
25
// https://github.com/bryanbraun/anchorjs/blob/65fede08d0e4a705f72f1e7e6284f643d5ad3cf3/anchor.js#L237-L257
26
27
// Regex for finding the non-safe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ (newlines, tabs, backspace, & vertical tabs)
28
var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\\n\t\b\v]/g,
29
urlText;
30
31
// Note: we trim hyphens after truncating because truncating can cause dangling hyphens.
32
// Example string: // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
33
urlText = text
34
.trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
35
.replace(/\'/gi, "") // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."
36
.replace(nonsafeChars, "-") // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-"
37
.replace(/-{2,}/g, "-") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-"
38
.substring(0, 64) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-"
39
.replace(/^-+|-+$/gm, "") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated"
40
.toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated"
41
42
return urlText || el.tagName.toLowerCase();
43
},
44
45
generateUniqueId: function(el) {
46
var anchorBase = this.generateUniqueIdBase(el);
47
for (var i = 0; ; i++) {
48
var anchor = anchorBase;
49
if (i > 0) {
50
// add suffix
51
anchor += "-" + i;
52
}
53
// check if ID already exists
54
if (!document.getElementById(anchor)) {
55
return anchor;
56
}
57
}
58
},
59
60
generateAnchor: function(el) {
61
if (el.id) {
62
return el.id;
63
} else {
64
var anchor = this.generateUniqueId(el);
65
el.id = anchor;
66
return anchor;
67
}
68
},
69
70
createNavList: function() {
71
return $('<ul class="nav navbar-nav"></ul>');
72
},
73
74
createChildNavList: function($parent) {
75
var $childList = this.createNavList();
76
$parent.append($childList);
77
return $childList;
78
},
79
80
generateNavEl: function(anchor, text) {
81
var $a = $('<a class="nav-link"></a>');
82
$a.attr("href", "#" + anchor);
83
$a.text(text);
84
var $li = $("<li></li>");
85
$li.append($a);
86
return $li;
87
},
88
89
generateNavItem: function(headingEl) {
90
var anchor = this.generateAnchor(headingEl);
91
var $heading = $(headingEl);
92
var text = $heading.data("toc-text") || $heading.text();
93
return this.generateNavEl(anchor, text);
94
},
95
96
// Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
97
getTopLevel: function($scope) {
98
for (var i = 1; i <= 6; i++) {
99
var $headings = this.findOrFilter($scope, "h" + i);
100
if ($headings.length > 1) {
101
return i;
102
}
103
}
104
105
return 1;
106
},
107
108
// returns the elements for the top level, and the next below it
109
getHeadings: function($scope, topLevel) {
110
var topSelector = "h" + topLevel;
111
112
var secondaryLevel = topLevel + 1;
113
var secondarySelector = "h" + secondaryLevel;
114
115
return this.findOrFilter($scope, topSelector + "," + secondarySelector);
116
},
117
118
getNavLevel: function(el) {
119
return parseInt(el.tagName.charAt(1), 10);
120
},
121
122
populateNav: function($topContext, topLevel, $headings) {
123
var $context = $topContext;
124
var $prevNav;
125
126
var helpers = this;
127
$headings.each(function(i, el) {
128
var $newNav = helpers.generateNavItem(el);
129
var navLevel = helpers.getNavLevel(el);
130
131
// determine the proper $context
132
if (navLevel === topLevel) {
133
// use top level
134
$context = $topContext;
135
} else if ($prevNav && $context === $topContext) {
136
// create a new level of the tree and switch to it
137
$context = helpers.createChildNavList($prevNav);
138
} // else use the current $context
139
140
$context.append($newNav);
141
142
$prevNav = $newNav;
143
});
144
},
145
146
parseOps: function(arg) {
147
var opts;
148
if (arg.jquery) {
149
opts = {
150
$nav: arg
151
};
152
} else {
153
opts = arg;
154
}
155
opts.$scope = opts.$scope || $(document.body);
156
return opts;
157
}
158
},
159
160
// accepts a jQuery object, or an options object
161
init: function(opts) {
162
opts = this.helpers.parseOps(opts);
163
164
// ensure that the data attribute is in place for styling
165
opts.$nav.attr("data-toggle", "toc");
166
167
var $topContext = this.helpers.createChildNavList(opts.$nav);
168
var topLevel = this.helpers.getTopLevel(opts.$scope);
169
var $headings = this.helpers.getHeadings(opts.$scope, topLevel);
170
this.helpers.populateNav($topContext, topLevel, $headings);
171
}
172
};
173
174
$(function() {
175
$('nav[data-toggle="toc"]').each(function(i, el) {
176
var $nav = $(el);
177
//Toc.init($nav);
178
});
179
});
180
})(jQuery);
181
182