Path: blob/main/assets/js/bootstrap-toc.min.js
2612 views
/*!1* Bootstrap Table of Contents v1.0.1 (http://afeld.github.io/bootstrap-toc/)2* Copyright 2015 Aidan Feldman3* Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) */4(function($) {5"use strict";67window.Toc = {8helpers: {9// return all matching elements in the set, or their descendants10findOrFilter: function($el, selector) {11// http://danielnouri.org/notes/2011/03/14/a-jquery-find-that-also-finds-the-root-element/12// http://stackoverflow.com/a/12731439/35880413var $descendants = $el.find(selector);14return $el15.filter(selector)16.add($descendants)17.filter(":not([data-toc-skip])");18},1920generateUniqueIdBase: function(el) {21var text = $(el).text();2223// adapted from24// https://github.com/bryanbraun/anchorjs/blob/65fede08d0e4a705f72f1e7e6284f643d5ad3cf3/anchor.js#L237-L2572526// Regex for finding the non-safe URL characters (many need escaping): & +$,:;=?@"#{}|^~[`%!'<>]./()*\ (newlines, tabs, backspace, & vertical tabs)27var nonsafeChars = /[& +$,:;=?@"#{}|^~[`%!'<>\]\.\/\(\)\*\\\n\t\b\v]/g,28urlText;2930// Note: we trim hyphens after truncating because truncating can cause dangling hyphens.31// Example string: // " ⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."32urlText = text33.trim() // "⚡⚡ Don't forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."34.replace(/\'/gi, "") // "⚡⚡ Dont forget: URL fragments should be i18n-friendly, hyphenated, short, and clean."35.replace(nonsafeChars, "-") // "⚡⚡-Dont-forget--URL-fragments-should-be-i18n-friendly--hyphenated--short--and-clean-"36.replace(/-{2,}/g, "-") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-short-and-clean-"37.substring(0, 64) // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated-"38.replace(/^-+|-+$/gm, "") // "⚡⚡-Dont-forget-URL-fragments-should-be-i18n-friendly-hyphenated"39.toLowerCase(); // "⚡⚡-dont-forget-url-fragments-should-be-i18n-friendly-hyphenated"4041return urlText || el.tagName.toLowerCase();42},4344generateUniqueId: function(el) {45var anchorBase = this.generateUniqueIdBase(el);46for (var i = 0; ; i++) {47var anchor = anchorBase;48if (i > 0) {49// add suffix50anchor += "-" + i;51}52// check if ID already exists53if (!document.getElementById(anchor)) {54return anchor;55}56}57},5859generateAnchor: function(el) {60if (el.id) {61return el.id;62} else {63var anchor = this.generateUniqueId(el);64el.id = anchor;65return anchor;66}67},6869createNavList: function() {70return $('<ul class="nav navbar-nav"></ul>');71},7273createChildNavList: function($parent) {74var $childList = this.createNavList();75$parent.append($childList);76return $childList;77},7879generateNavEl: function(anchor, text) {80var $a = $('<a class="nav-link"></a>');81$a.attr("href", "#" + anchor);82$a.text(text);83var $li = $("<li></li>");84$li.append($a);85return $li;86},8788generateNavItem: function(headingEl) {89var anchor = this.generateAnchor(headingEl);90var $heading = $(headingEl);91var text = $heading.data("toc-text") || $heading.text();92return this.generateNavEl(anchor, text);93},9495// Find the first heading level (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).96getTopLevel: function($scope) {97for (var i = 1; i <= 6; i++) {98var $headings = this.findOrFilter($scope, "h" + i);99if ($headings.length > 1) {100return i;101}102}103104return 1;105},106107// returns the elements for the top level, and the next below it108getHeadings: function($scope, topLevel) {109var topSelector = "h" + topLevel;110111var secondaryLevel = topLevel + 1;112var secondarySelector = "h" + secondaryLevel;113114return this.findOrFilter($scope, topSelector + "," + secondarySelector);115},116117getNavLevel: function(el) {118return parseInt(el.tagName.charAt(1), 10);119},120121populateNav: function($topContext, topLevel, $headings) {122var $context = $topContext;123var $prevNav;124125var helpers = this;126$headings.each(function(i, el) {127var $newNav = helpers.generateNavItem(el);128var navLevel = helpers.getNavLevel(el);129130// determine the proper $context131if (navLevel === topLevel) {132// use top level133$context = $topContext;134} else if ($prevNav && $context === $topContext) {135// create a new level of the tree and switch to it136$context = helpers.createChildNavList($prevNav);137} // else use the current $context138139$context.append($newNav);140141$prevNav = $newNav;142});143},144145parseOps: function(arg) {146var opts;147if (arg.jquery) {148opts = {149$nav: arg150};151} else {152opts = arg;153}154opts.$scope = opts.$scope || $(document.body);155return opts;156}157},158159// accepts a jQuery object, or an options object160init: function(opts) {161opts = this.helpers.parseOps(opts);162163// ensure that the data attribute is in place for styling164opts.$nav.attr("data-toggle", "toc");165166var $topContext = this.helpers.createChildNavList(opts.$nav);167var topLevel = this.helpers.getTopLevel(opts.$scope);168var $headings = this.helpers.getHeadings(opts.$scope, topLevel);169this.helpers.populateNav($topContext, topLevel, $headings);170}171};172173$(function() {174$('nav[data-toggle="toc"]').each(function(i, el) {175var $nav = $(el);176//Toc.init($nav);177});178});179})(jQuery);180181182