Update Bootstrap TOC to v1.0.1 release reference and specify Soft Offline Mode with persistent links #69
1
.gitignore
vendored
1
.gitignore
vendored
@@ -138,6 +138,7 @@ package-lock.json
|
|||||||
# PHP / Composer tooling
|
# PHP / Composer tooling
|
||||||
# ============================================================
|
# ============================================================
|
||||||
vendor/
|
vendor/
|
||||||
|
!src/media/vendor/
|
||||||
composer.lock
|
composer.lock
|
||||||
*.phar
|
*.phar
|
||||||
codeception.phar
|
codeception.phar
|
||||||
|
|||||||
46
src/media/vendor/bootstrap-toc/bootstrap-toc.css
vendored
Normal file
46
src/media/vendor/bootstrap-toc/bootstrap-toc.css
vendored
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
170
src/media/vendor/bootstrap-toc/bootstrap-toc.js
vendored
Normal file
170
src/media/vendor/bootstrap-toc/bootstrap-toc.js
vendored
Normal file
@@ -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 $('<ul class="nav"></ul>');
|
||||||
|
},
|
||||||
|
|
||||||
|
createChildNavList: function($parent) {
|
||||||
|
var $childList = this.createNavList();
|
||||||
|
$parent.append($childList);
|
||||||
|
return $childList;
|
||||||
|
},
|
||||||
|
|
||||||
|
generateNavEl: function(anchor, text) {
|
||||||
|
var $a = $('<a class="nav-link"></a>');
|
||||||
|
$a.attr('href', '#' + anchor);
|
||||||
|
$a.text(text);
|
||||||
|
var $li = $('<li class="nav-item"></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 (`<h1>`, then `<h2>`, etc.) that has more than one element. Defaults to 1 (for `<h1>`).
|
||||||
|
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);
|
||||||
1
src/media/vendor/bootstrap-toc/index.html
vendored
Normal file
1
src/media/vendor/bootstrap-toc/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><title></title>
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
* INGROUP: MokoCassiopeia
|
* INGROUP: MokoCassiopeia
|
||||||
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-left.php
|
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-left.php
|
||||||
* VERSION: 03.06.02
|
* 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;
|
defined('_JEXEC') or die;
|
||||||
@@ -20,7 +20,11 @@ use Joomla\CMS\Factory;
|
|||||||
use Joomla\CMS\HTML\HTMLHelper;
|
use Joomla\CMS\HTML\HTMLHelper;
|
||||||
use Joomla\CMS\Language\Associations;
|
use Joomla\CMS\Language\Associations;
|
||||||
use Joomla\CMS\Layout\LayoutHelper;
|
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
|
// Get article params
|
||||||
$params = $this->item->params;
|
$params = $this->item->params;
|
||||||
@@ -29,7 +33,7 @@ $urls = json_decode($this->item->urls);
|
|||||||
$canEdit = $params->get('access-edit');
|
$canEdit = $params->get('access-edit');
|
||||||
$info = $params->get('info_block_position', 0);
|
$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'));
|
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
||||||
?>
|
?>
|
||||||
|
|
||||||
@@ -38,15 +42,14 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
|||||||
<!-- Table of Contents - Left Side -->
|
<!-- Table of Contents - Left Side -->
|
||||||
<div class="col-lg-3 col-md-4 order-md-1 mb-4">
|
<div class="col-lg-3 col-md-4 order-md-1 mb-4">
|
||||||
<div class="sticky-top toc-wrapper" style="top: 20px;">
|
<div class="sticky-top toc-wrapper" style="top: 20px;">
|
||||||
<nav id="toc" class="toc-container">
|
<nav id="toc" data-toggle="toc" class="toc-container">
|
||||||
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
|
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
|
||||||
<nav id="toc-nav" class="nav flex-column"></nav>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Article Content -->
|
<!-- Article Content -->
|
||||||
<div class="col-lg-9 col-md-8 order-md-2">
|
<div class="col-lg-9 col-md-8 order-md-2" data-toc-scope>
|
||||||
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
|
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
|
||||||
|
|
||||||
<?php if ($this->params->get('show_page_heading')) : ?>
|
<?php if ($this->params->get('show_page_heading')) : ?>
|
||||||
@@ -112,52 +115,6 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const content = document.querySelector('.article-content');
|
|
||||||
const tocNav = document.getElementById('toc-nav');
|
|
||||||
|
|
||||||
if (!content || !tocNav) return;
|
|
||||||
|
|
||||||
const headings = content.querySelectorAll('h2, h3, h4, h5, h6');
|
|
||||||
|
|
||||||
if (headings.length === 0) {
|
|
||||||
document.getElementById('toc').style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
headings.forEach((heading, index) => {
|
|
||||||
if (!heading.id) {
|
|
||||||
heading.id = 'heading-' + index;
|
|
||||||
}
|
|
||||||
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.className = 'nav-link';
|
|
||||||
link.href = '#' + heading.id;
|
|
||||||
link.textContent = heading.textContent;
|
|
||||||
|
|
||||||
const level = parseInt(heading.tagName.substring(1));
|
|
||||||
link.style.paddingLeft = ((level - 2) * 15 + 10) + 'px';
|
|
||||||
|
|
||||||
tocNav.appendChild(link);
|
|
||||||
});
|
|
||||||
|
|
||||||
tocNav.querySelectorAll('a').forEach(link => {
|
|
||||||
link.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const targetId = this.getAttribute('href').substring(1);
|
|
||||||
const target = document.getElementById(targetId);
|
|
||||||
if (target) {
|
|
||||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.toc-container {
|
.toc-container {
|
||||||
background: var(--cassiopeia-color-bg, #fff);
|
background: var(--cassiopeia-color-bg, #fff);
|
||||||
@@ -171,28 +128,8 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--cassiopeia-color-text, #212529);
|
color: var(--cassiopeia-color-text, #212529);
|
||||||
}
|
border-bottom: 1px solid var(--cassiopeia-color-border, #dee2e6);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
#toc-nav .nav-link {
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--cassiopeia-color-link, #0d6efd);
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
border-left: 2px solid transparent;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toc-nav .nav-link:hover {
|
|
||||||
color: var(--cassiopeia-color-hover, #0a58ca);
|
|
||||||
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
|
|
||||||
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#toc-nav .nav-link.active {
|
|
||||||
color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* INGROUP: MokoCassiopeia
|
* INGROUP: MokoCassiopeia
|
||||||
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-right.php
|
* PATH: ./templates/mokocassiopeia/html/com_content/article/toc-right.php
|
||||||
* VERSION: 03.06.02
|
* VERSION: 03.06.02
|
||||||
* BRIEF: Article layout with table of contents on the right side
|
* BRIEF: Article layout with table of contents on the right side using Bootstrap TOC
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined('_JEXEC') or die;
|
defined('_JEXEC') or die;
|
||||||
@@ -20,7 +20,11 @@ use Joomla\CMS\Factory;
|
|||||||
use Joomla\CMS\HTML\HTMLHelper;
|
use Joomla\CMS\HTML\HTMLHelper;
|
||||||
use Joomla\CMS\Language\Associations;
|
use Joomla\CMS\Language\Associations;
|
||||||
use Joomla\CMS\Layout\LayoutHelper;
|
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
|
// Get article params
|
||||||
$params = $this->item->params;
|
$params = $this->item->params;
|
||||||
@@ -29,14 +33,14 @@ $urls = json_decode($this->item->urls);
|
|||||||
$canEdit = $params->get('access-edit');
|
$canEdit = $params->get('access-edit');
|
||||||
$info = $params->get('info_block_position', 0);
|
$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'));
|
$assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
|
<div class="com-content-article item-page<?php echo $this->pageclass_sfx; ?>">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Article Content -->
|
<!-- Article Content -->
|
||||||
<div class="col-lg-9 col-md-8 order-md-1">
|
<div class="col-lg-9 col-md-8 order-md-1" data-toc-scope>
|
||||||
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
|
<meta itemprop="inLanguage" content="<?php echo ($this->item->language === '*') ? Factory::getApplication()->get('language') : $this->item->language; ?>" />
|
||||||
|
|
||||||
<?php if ($this->params->get('show_page_heading')) : ?>
|
<?php if ($this->params->get('show_page_heading')) : ?>
|
||||||
@@ -103,61 +107,14 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
|||||||
<!-- Table of Contents - Right Side -->
|
<!-- Table of Contents - Right Side -->
|
||||||
<div class="col-lg-3 col-md-4 order-md-2 mb-4">
|
<div class="col-lg-3 col-md-4 order-md-2 mb-4">
|
||||||
<div class="sticky-top toc-wrapper" style="top: 20px;">
|
<div class="sticky-top toc-wrapper" style="top: 20px;">
|
||||||
<nav id="toc" class="toc-container">
|
<nav id="toc" data-toggle="toc" class="toc-container">
|
||||||
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
|
<h5 class="toc-title"><?php echo HTMLHelper::_('string.truncate', $this->item->title, 50); ?></h5>
|
||||||
<nav id="toc-nav" class="nav flex-column"></nav>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const content = document.querySelector('.article-content');
|
|
||||||
const tocNav = document.getElementById('toc-nav');
|
|
||||||
|
|
||||||
if (!content || !tocNav) return;
|
|
||||||
|
|
||||||
const headings = content.querySelectorAll('h2, h3, h4, h5, h6');
|
|
||||||
|
|
||||||
if (headings.length === 0) {
|
|
||||||
document.getElementById('toc').style.display = 'none';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
headings.forEach((heading, index) => {
|
|
||||||
if (!heading.id) {
|
|
||||||
heading.id = 'heading-' + index;
|
|
||||||
}
|
|
||||||
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.className = 'nav-link';
|
|
||||||
link.href = '#' + heading.id;
|
|
||||||
link.textContent = heading.textContent;
|
|
||||||
|
|
||||||
const level = parseInt(heading.tagName.substring(1));
|
|
||||||
link.style.paddingLeft = ((level - 2) * 15 + 10) + 'px';
|
|
||||||
|
|
||||||
tocNav.appendChild(link);
|
|
||||||
});
|
|
||||||
|
|
||||||
tocNav.querySelectorAll('a').forEach(link => {
|
|
||||||
link.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const targetId = this.getAttribute('href').substring(1);
|
|
||||||
const target = document.getElementById(targetId);
|
|
||||||
if (target) {
|
|
||||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.toc-container {
|
.toc-container {
|
||||||
background: var(--cassiopeia-color-bg, #fff);
|
background: var(--cassiopeia-color-bg, #fff);
|
||||||
@@ -171,28 +128,8 @@ $assocParam = (Associations::isEnabled() && $params->get('show_associations'));
|
|||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--cassiopeia-color-text, #212529);
|
color: var(--cassiopeia-color-text, #212529);
|
||||||
}
|
border-bottom: 1px solid var(--cassiopeia-color-border, #dee2e6);
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
#toc-nav .nav-link {
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--cassiopeia-color-link, #0d6efd);
|
|
||||||
text-decoration: none;
|
|
||||||
display: block;
|
|
||||||
border-left: 2px solid transparent;
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#toc-nav .nav-link:hover {
|
|
||||||
color: var(--cassiopeia-color-hover, #0a58ca);
|
|
||||||
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
|
|
||||||
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
}
|
|
||||||
|
|
||||||
#toc-nav .nav-link.active {
|
|
||||||
color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
border-left-color: var(--cassiopeia-color-primary, #0d6efd);
|
|
||||||
background-color: var(--cassiopeia-color-hover-bg, rgba(0, 0, 0, 0.05));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 767.98px) {
|
@media (max-width: 767.98px) {
|
||||||
|
|||||||
@@ -217,6 +217,19 @@
|
|||||||
"type": "style",
|
"type": "style",
|
||||||
"uri": "media/templates/site/mokocassiopeia/css/vendor/vm.css",
|
"uri": "media/templates/site/mokocassiopeia/css/vendor/vm.css",
|
||||||
"attributes": {"media": "all"}
|
"attributes": {"media": "all"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vendor.bootstrap-toc",
|
||||||
|
"type": "style",
|
||||||
|
"uri": "media/templates/site/mokocassiopeia/vendor/bootstrap-toc/bootstrap-toc.css",
|
||||||
|
"attributes": {"media": "all"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "vendor.bootstrap-toc.js",
|
||||||
|
"type": "script",
|
||||||
|
"uri": "media/templates/site/mokocassiopeia/vendor/bootstrap-toc/bootstrap-toc.js",
|
||||||
|
"dependencies": ["jquery"],
|
||||||
|
"attributes": {"defer": true}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user