diff --git a/_layouts/default.html b/_layouts/default.html index 2ca41b4..dfcbb6c 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -36,11 +36,6 @@ media="screen" charset="utf-8" /> - diff --git a/bootstrap-toc.js b/bootstrap-toc.js index 534f100..14b1109 100644 --- a/bootstrap-toc.js +++ b/bootstrap-toc.js @@ -2,24 +2,40 @@ * Bootstrap Table of Contents v<%= version %> (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($) { +(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])"); + findOrFilter: function(el, selector) { + var parent = el.parentElement || el; + var descendants = parent.querySelectorAll(selector); + var arr = []; + + for (var i = 0; i < descendants.length; i++) { + var descendent = descendants[i]; + if ( + descendent === el || + (descendent.parentElement !== el.parentElement && + descendent.getAttribute("data-toc-skip") === null) + ) { + arr.push(descendent); + } + } + + return arr.filter(function(item) { + if (item === el) return true; + while (item !== null) { + if (item.parentElement === el) return true; + item = item.parentElement; + } + return false; + }); }, generateUniqueIdBase: function(el) { - var text = $(el).text(); + var text = el.textContent; // adapted from // https://github.com/bryanbraun/anchorjs/blob/65fede08d0e4a705f72f1e7e6284f643d5ad3cf3/anchor.js#L237-L257 @@ -68,36 +84,39 @@ }, createNavList: function() { - return $(''); + var ul = document.createElement("ul"); + ul.classList.add("nav", "navbar-nav"); + return ul; }, - createChildNavList: function($parent) { - var $childList = this.createNavList(); - $parent.append($childList); - return $childList; + createChildNavList: function(parent) { + var childList = this.createNavList(); + parent.appendChild(childList); + return childList; }, generateNavEl: function(anchor, text) { - var $a = $(''); - $a.attr("href", "#" + anchor); - $a.text(text); - var $li = $("
  • "); - $li.append($a); - return $li; + var a = document.createElement("a"); + a.classList.add("nav-link"); + a.setAttribute("href", "#" + anchor); + a.innerText = text; + var li = document.createElement("li"); + li.appendChild(a); + return li; }, generateNavItem: function(headingEl) { var anchor = this.generateAnchor(headingEl); - var $heading = $(headingEl); - var text = $heading.data("toc-text") || $heading.text(); + var text = + headingEl.getAttribute("data-toc-text") || headingEl.textContent; return this.generateNavEl(anchor, text); }, // Find the first heading level (`

    `, then `

    `, etc.) that has more than one element. Defaults to 1 (for `

    `). - getTopLevel: function($scope) { + getTopLevel: function(scope) { for (var i = 1; i <= 6; i++) { - var $headings = this.findOrFilter($scope, "h" + i); - if ($headings.length > 1) { + var headings = this.findOrFilter(scope, "h" + i); + if (headings.length > 1) { return i; } } @@ -106,75 +125,83 @@ }, // returns the elements for the top level, and the next below it - getHeadings: function($scope, topLevel) { + getHeadings: function(scope, topLevel) { var topSelector = "h" + topLevel; var secondaryLevel = topLevel + 1; var secondarySelector = "h" + secondaryLevel; - return this.findOrFilter($scope, topSelector + "," + secondarySelector); + 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; + populateNav: function(topContext, topLevel, headings) { + var context = topContext; + var prevNav; var helpers = this; - $headings.each(function(i, el) { - var $newNav = helpers.generateNavItem(el); + headings.forEach(function(el) { + var newNav = helpers.generateNavItem(el); var navLevel = helpers.getNavLevel(el); - // determine the proper $context + // determine the proper context if (navLevel === topLevel) { // use top level - $context = $topContext; - } else if ($prevNav && $context === $topContext) { + 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 = helpers.createChildNavList(prevNav); + } // else use the current context - $context.append($newNav); + context.appendChild(newNav); - $prevNav = $newNav; + prevNav = newNav; }); }, parseOps: function(arg) { var opts; - if (arg.jquery) { + if (arg instanceof Element) { opts = { - $nav: arg + nav: arg }; } else { opts = arg; } - opts.$scope = opts.$scope || $(document.body); + opts.scope = opts.scope || document.body; return opts; } }, - // accepts a jQuery object, or an options object + // accepts an Element 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"); + opts.nav.setAttribute("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); + 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); - }); + function ready(fn) { + if (document.readyState !== "loading") { + fn(); + } else { + document.addEventListener("DOMContentLoaded", fn); + } + } + + ready(function() { + var navs = document.querySelectorAll('nav[data-toggle="toc"]'); + for (var i = 0; i < navs.length; i++) { + Toc.init(navs[i]); + } }); -})(jQuery); +})(); diff --git a/package-lock.json b/package-lock.json index aa6f8fd..ea66b8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1624,7 +1624,6 @@ "minipass": { "version": "2.2.4", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -1800,8 +1799,7 @@ }, "safe-buffer": { "version": "5.1.1", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -1879,8 +1877,7 @@ }, "yallist": { "version": "3.0.2", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -2718,11 +2715,6 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, - "jquery": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.0.tgz", - "integrity": "sha512-Xb7SVYMvygPxbFMpTFQiHh1J7HClEaThguL15N/Gg37Lri/qKyhRGZYzHRyLH8Stq3Aow0LsHO2O2ci86fCrNQ==" - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", diff --git a/package.json b/package.json index 0ba8718..3953b4c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "gulp-rename": "^1.4.0", "gulp-template": "^5.0.0", "gulp-uglify": "^3.0.1", - "jquery": "^3.5.0", "jshint": "^2.9.1-rc1", "mocha": "^5.2.0" } diff --git a/test/index.html b/test/index.html index 398a0f6..74eca62 100644 --- a/test/index.html +++ b/test/index.html @@ -8,7 +8,6 @@
    foo
    - @@ -19,7 +18,6 @@ diff --git a/test/toc-test.js b/test/toc-test.js index 73e3c9d..efdacea 100644 --- a/test/toc-test.js +++ b/test/toc-test.js @@ -1,28 +1,31 @@ -var $fixture = $("#mocha-fixture"); +var fixture = document.getElementById("mocha-fixture"); afterEach(function() { - $fixture.empty(); + fixture.innerHTML = ""; }); describe("Toc", function() { describe(".helpers", function() { describe(".generateNavItem()", function() { it("uses text within the element by default", function() { - var heading = $("

    foo

    ")[0]; - var $navItem = Toc.helpers.generateNavItem(heading); - expect($navItem.text()).to.eql("foo"); + var heading = document.createElement("h1"); + heading.innerText = "foo"; + var navItem = Toc.helpers.generateNavItem(heading); + expect(navItem.textContent).to.eql("foo"); }); it("uses text specified as a data-toc-text attribute", function() { - var heading = $('

    bar

    ')[0]; - var $navItem = Toc.helpers.generateNavItem(heading); - expect($navItem.text()).to.eql("foo"); + var heading = document.createElement("h1"); + heading.innerText = "bar"; + heading.setAttribute("data-toc-text", "foo"); + var navItem = Toc.helpers.generateNavItem(heading); + expect(navItem.textContent).to.eql("foo"); }); it("keeps the text from within the element escaped", function() { var heading = document.createElement("h1"); heading.innerText = "<script>foo</script>"; - var navItem = Toc.helpers.generateNavItem(heading)[0]; + var navItem = Toc.helpers.generateNavItem(heading); expect(navItem.textContent).to.eql("<script>foo</script>"); }); @@ -33,8 +36,8 @@ describe("Toc", function() { "<script>foo</script>" ); heading.innerText = "bar"; - var $navItem = Toc.helpers.generateNavItem(heading); - expect($navItem.text()).to.eql("<script>foo</script>"); + var navItem = Toc.helpers.generateNavItem(heading); + expect(navItem.textContent).to.eql("<script>foo</script>"); }); }); @@ -53,7 +56,8 @@ describe("Toc", function() { }); it("handles unicode", function() { - var el = $("

    💃 🕺

    ")[0]; + var el = document.createElement("h1"); + el.innerText = "💃 🕺"; var base = Toc.helpers.generateUniqueIdBase(el); expect(base).to.eql("💃-🕺"); }); @@ -67,7 +71,7 @@ describe("Toc", function() { }); it("adds a suffix when there's an existing element with that tag", function() { - $fixture.append('

    '); + fixture.innerHTML = '

    '; var el = document.createElement("h1"); var base = Toc.helpers.generateUniqueId(el); @@ -77,24 +81,22 @@ describe("Toc", function() { describe(".getTopLevel()", function() { it("returns 1 by default", function() { - var $scope = $("
    "); - var level = Toc.helpers.getTopLevel($scope); + var scope = document.createElement("div"); + var level = Toc.helpers.getTopLevel(scope); expect(level).to.eql(1); }); it("returns the first level with more than one element", function() { - var $scope = $( - "
    " + - "

    " + - "

    " + - "

    " + - "

    " + - "
    " + - "
    " + - "
    " + - "
    " - ); - var level = Toc.helpers.getTopLevel($scope); + var scope = document.createElement("div"); + scope.innerHTML = + "

    " + + "

    " + + "

    " + + "

    " + + "
    " + + "
    " + + "
    "; + var level = Toc.helpers.getTopLevel(scope); expect(level).to.eql(6); }); }); @@ -109,21 +111,21 @@ describe("Toc", function() { }); describe(".init()", function() { - var $nav; + var nav; beforeEach(function() { - $nav = $("