diff --git a/.gitignore b/.gitignore index 205e9a0..abfdb17 100644 --- a/.gitignore +++ b/.gitignore @@ -138,6 +138,7 @@ package-lock.json # PHP / Composer tooling # ============================================================ vendor/ +!src/media/vendor/ composer.lock *.phar codeception.phar diff --git a/src/media/vendor/bootstrap-toc/bootstrap-toc.css b/src/media/vendor/bootstrap-toc/bootstrap-toc.css new file mode 100644 index 0000000..35ba3c3 --- /dev/null +++ b/src/media/vendor/bootstrap-toc/bootstrap-toc.css @@ -0,0 +1,46 @@ +/*! + * Bootstrap Table of Contents v1.0.1 (https://afeld.github.io/bootstrap-toc/) + * Copyright 2015 Aidan Feldman + * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) + */ + +/* All levels of nav */ +nav[data-toggle='toc'] .nav > li > a { + display: block; + padding: 4px 20px; + font-size: 13px; + font-weight: 500; + color: var(--cassiopeia-color-link, #0d6efd); +} +nav[data-toggle='toc'] .nav > li > a:hover, +nav[data-toggle='toc'] .nav > li > a:focus { + padding-left: 19px; + color: var(--cassiopeia-color-hover, #0a58ca); + text-decoration: none; + background-color: transparent; + border-left: 1px solid var(--cassiopeia-color-primary, #0d6efd); +} +nav[data-toggle='toc'] .nav > .active > a, +nav[data-toggle='toc'] .nav > .active:hover > a, +nav[data-toggle='toc'] .nav > .active:focus > a { + padding-left: 18px; + font-weight: bold; + color: var(--cassiopeia-color-primary, #0d6efd); + background-color: transparent; + border-left: 2px solid var(--cassiopeia-color-primary, #0d6efd); +} + +/* Nav: second level (shown on .active) */ +nav[data-toggle='toc'] .nav .nav { + display: none; /* Hide by default, but at >768px, show it */ + padding-bottom: 10px; +} +nav[data-toggle='toc'] .nav > .active > ul { + display: block; +} + +@media (min-width: 768px) { + nav[data-toggle='toc'] .nav .nav { + display: block; + } +} diff --git a/src/media/vendor/bootstrap-toc/bootstrap-toc.js b/src/media/vendor/bootstrap-toc/bootstrap-toc.js new file mode 100644 index 0000000..3cc08bf --- /dev/null +++ b/src/media/vendor/bootstrap-toc/bootstrap-toc.js @@ -0,0 +1,170 @@ +/*! + * Bootstrap Table of Contents v1.0.1 (https://afeld.github.io/bootstrap-toc/) + * Copyright 2015 Aidan Feldman + * Licensed under MIT (https://github.com/afeld/bootstrap-toc/blob/gh-pages/LICENSE.md) + */ +(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])'); + }, + + generateUniqueIdBase: function(el) { + var text = $(el).text(); + + // adapted from + // https://github.com/bryanbraun/anchorjs/blob/5a7f01cbd56f8aa8413084a64e7d1cbb1d4b0e56/anchor.js#L237-L257 + // and + // https://github.com/twbs/bootstrap/blob/b8a84c3e48ce62e5d1954c52d3796b3fcbeab8a9/site/docs/4.1/assets/js/src/application.js#L47-L53 + // Remove punctuation and spaces + var base = text + .toLowerCase() + .replace(/[^\w\s-]/g, '') + .replace(/[\s]+/g, '-'); + + return base || el.tagName.toLowerCase(); + }, + + generateUniqueId: function(el) { + var anchorBase = this.generateUniqueIdBase(el); + for (var i = 0; ; i++) { + var anchor = anchorBase; + if (i > 0) { + // add suffix + anchor += '-' + i; + } + // check if ID already exists + if (!document.getElementById(anchor)) { + return anchor; + } + } + }, + + generateAnchor: function(el) { + if (el.id) { + return el.id; + } else { + var anchor = this.generateUniqueId(el); + el.id = anchor; + return anchor; + } + }, + + createNavList: function() { + return $(''); + }, + + createChildNavList: function($parent) { + var $childList = this.createNavList(); + $parent.append($childList); + return $childList; + }, + + generateNavEl: function(anchor, text) { + var $a = $(''); + $a.attr('href', '#' + anchor); + $a.text(text); + var $li = $(''); + $li.append($a); + return $li; + }, + + generateNavItem: function(headingEl) { + var anchor = this.generateAnchor(headingEl); + var $heading = $(headingEl); + var text = $heading.data('toc-text') || $heading.text(); + 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) { + for (var i = 1; i <= 6; i++) { + var $headings = this.findOrFilter($scope, 'h' + i); + if ($headings.length > 1) { + return i; + } + } + + return 1; + }, + + // returns the elements for the top level, and the next below it + getHeadings: function($scope, topLevel) { + var topSelector = 'h' + topLevel; + + var secondaryLevel = topLevel + 1; + var secondarySelector = 'h' + secondaryLevel; + + 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; + + var helpers = this; + $headings.each(function(i, el) { + var $newNav = helpers.generateNavItem(el); + var navLevel = helpers.getNavLevel(el); + + // determine the proper $context + if (navLevel === topLevel) { + // use top level + $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.append($newNav); + + $prevNav = $newNav; + }); + }, + + parseOps: function(arg) { + var opts; + if (arg.jquery) { + opts = { + $nav: arg + }; + } else { + opts = arg; + } + opts.$scope = opts.$scope || $(document.body); + return opts; + } + }, + + // accepts a jQuery 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'); + + 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); + }); + }); +})(jQuery); diff --git a/src/media/vendor/bootstrap-toc/index.html b/src/media/vendor/bootstrap-toc/index.html new file mode 100644 index 0000000..2efb97f --- /dev/null +++ b/src/media/vendor/bootstrap-toc/index.html @@ -0,0 +1 @@ + diff --git a/src/templates/html/com_content/article/toc-left.php b/src/templates/html/com_content/article/toc-left.php index 89aa8bd..c903a3f 100644 --- a/src/templates/html/com_content/article/toc-left.php +++ b/src/templates/html/com_content/article/toc-left.php @@ -11,7 +11,7 @@ * INGROUP: MokoCassiopeia * PATH: ./templates/mokocassiopeia/html/com_content/article/toc-left.php * VERSION: 03.06.02 - * BRIEF: Article layout with table of contents on the left side + * BRIEF: Article layout with table of contents on the left side using Bootstrap TOC */ defined('_JEXEC') or die; @@ -20,7 +20,11 @@ use Joomla\CMS\Factory; use Joomla\CMS\HTML\HTMLHelper; use Joomla\CMS\Language\Associations; use Joomla\CMS\Layout\LayoutHelper; -use Joomla\Component\Content\Administrator\Extension\ContentComponent; + +// Load Bootstrap TOC assets +$wa = Factory::getApplication()->getDocument()->getWebAssetManager(); +$wa->useStyle('vendor.bootstrap-toc'); +$wa->useScript('vendor.bootstrap-toc.js'); // Get article params $params = $this->item->params; @@ -29,7 +33,7 @@ $urls = json_decode($this->item->urls); $canEdit = $params->get('access-edit'); $info = $params->get('info_block_position', 0); -// Check if associations are implemented. If they are, define the parameter. +// Check if associations are implemented $assocParam = (Associations::isEnabled() && $params->get('show_associations')); ?> @@ -38,15 +42,14 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
-
-
+
params->get('show_page_heading')) : ?> @@ -112,52 +115,6 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
- -