Major changes to accordion nav menu. Table of contents auto-generated below the loaded page. Removed manual TOCs from individual pages. TOC uses jQuery plugin Sticky to remain visible when it would scroll out of view.

This commit is contained in:
Andrew Manning
2016-11-29 22:08:19 -05:00
parent 36acd34874
commit b32bce9be2
8 changed files with 674 additions and 126 deletions

View File

@@ -8,17 +8,25 @@
.doco-list-group-item {
padding-left: 15px;
}
#region_1 .widget {
position: fixed;
top:70px;
width: inherit;
height: 80%;
overflow-y: scroll;
.doco-list-group-item > a {
font-weight: bold;
font-size: 1.1em;
}
#doco-content h1 {
border-bottom: #cccccc thin solid;
padding-bottom: 0.3em;
}
@media screen and (max-width: 767px) {
#region_1 .widget {
position: static;
}
#region_1 .widget ul ul {
list-style-type: none;
padding-left: 15px;
}
.toc-content {
background-color: white;
border-left: #cccccc 2px solid;
margin-left: 10px;
}
</style>
@@ -26,70 +34,65 @@
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#about"><span class="glyphicon glyphicon-folder-close">
</span>About</a>
<a data-toggle="collapse" data-parent="#accordion" href="#about">
About</a>
</h4>
</div>
<div id="about" class="panel-collapse collapse">
<div id="about" class="panel-collapse collapse in">
<ul class="list-group">
<li class="doco-list-group-item"><a href="/help/about/about_hubzilla#about-hubzilla-toc"><span class="glyphicon glyphicon-file"></span>Hubzilla project</a></li>
<li class="doco-list-group-item"><a href="/help/about/about_hub"><span class="glyphicon glyphicon-file"></span>About this hub</a></li>
<li class="doco-list-group-item"><a href="/help/about/about_hubzilla">Hubzilla project</a></li>
<li class="doco-list-group-item"><a href="/help/about/about_hub">About this hub</a></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#members"><span class="glyphicon glyphicon-folder-close">
</span>Members</a>
<a data-toggle="collapse" data-parent="#accordion" href="#members">
Members</a>
</h4>
</div>
<div id="members" class="panel-collapse collapse">
<div id="members" class="panel-collapse collapse in">
<ul class="list-group">
<li class="doco-list-group-item"><a href="/help/member/member_guide#member-guide-toc">Contents</a></li>
<li class="doco-list-group-item"><a href="/help/member/member_faq#member-faq-toc">FAQ</a></li>
<li class="doco-list-group-item"><a href="/help/member/member_guide">Guide</a></li>
<li class="doco-list-group-item"><a href="/help/member/member_faq">FAQ</a></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#administrators"><span class="glyphicon glyphicon-folder-close">
</span>Administrators</a>
<a data-toggle="collapse" data-parent="#accordion" href="#administrators">
Administrators</a>
</h4>
</div>
<div id="administrators" class="panel-collapse collapse">
<div id="administrators" class="panel-collapse collapse in">
<ul class="list-group">
<li class="doco-list-group-item"><a href="/help/admin/administrator_guide#admin-guide-toc">Contents</a></li>
<li class="doco-list-group-item"><a href="/help/admin/administrator_guide#Before_you_begin">Installation</a></li>
<li class="doco-list-group-item"><a href="/help/admin/administrator_guide">Guide</a></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#developers"><span class="glyphicon glyphicon-folder-close">
</span>Developers</a>
<a data-toggle="collapse" data-parent="#accordion" href="#developers">
Developers</a>
</h4>
</div>
<div id="developers" class="panel-collapse collapse">
<div id="developers" class="panel-collapse collapse in">
<ul class="list-group">
<li class="doco-list-group-item"><a href="/help/developer/developer_guide#developer-guide-toc">Contents</a></li>
<li class="doco-list-group-item"><a href="/help/developer/developer_guide#Who_is_a_Hubzilla_developer_Should_I_read_this_">Overview</a></li>
<li class="doco-list-group-item"><a href="/help/developer/developer_guide">Guide</a></li>
</ul>
</div>
</div>
<div class="panel">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-parent="#accordion" href="#tutorials"><span class="glyphicon glyphicon-folder-close">
</span>Tutorials</a>
<a data-toggle="collapse" data-parent="#accordion" href="#tutorials">
Tutorials</a>
</h4>
</div>
<div id="tutorials" class="panel-collapse collapse">
<div id="tutorials" class="panel-collapse collapse in">
<ul class="list-group">
<li class="doco-list-group-item"><a href="/help/tutorials/personal_channel">Personal Channel</a></li>
</ul>
@@ -97,54 +100,320 @@
</div>
</div>
<div id="page-toc-container" style="margin-top:1.0em;">
<h3>Contents</h3>
</div>
<script>
// Sticky Plugin v1.0.4 for jQuery
// =============
// Author: Anthony Garand
// Improvements by German M. Bravo (Kronuz) and Ruud Kamphuis (ruudk)
// Improvements by Leonardo C. Daronco (daronco)
// Created: 02/14/2011
// Date: 07/20/2015
// Website: http://stickyjs.com/
// Description: Makes an element on the page stick on the screen as you scroll
// It will only set the 'top' and 'position' of your element, you
// might need to adjust the width in some cases.
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else if (typeof module === 'object' && module.exports) {
// Node/CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var slice = Array.prototype.slice; // save ref to original slice()
var splice = Array.prototype.splice; // save ref to original slice()
var defaults = {
topSpacing: 0,
bottomSpacing: 0,
className: 'is-sticky',
wrapperClassName: 'sticky-wrapper',
center: false,
getWidthFrom: '',
widthFromWrapper: true, // works only when .getWidthFrom is empty
responsiveWidth: false,
zIndex: 'auto'
},
$window = $(window),
$document = $(document),
sticked = [],
windowHeight = $window.height(),
scroller = function() {
var scrollTop = $window.scrollTop(),
documentHeight = $document.height(),
dwh = documentHeight - windowHeight,
extra = (scrollTop > dwh) ? dwh - scrollTop : 0;
for (var i = 0, l = sticked.length; i < l; i++) {
var s = sticked[i],
elementTop = s.stickyWrapper.offset().top,
etse = elementTop - s.topSpacing - extra;
//update height in case of dynamic content
s.stickyWrapper.css('height', s.stickyElement.outerHeight());
if (scrollTop <= etse) {
if (s.currentTop !== null) {
s.stickyElement
.css({
'width': '',
'position': '',
'top': '',
'z-index': ''
});
s.stickyElement.parent().removeClass(s.className);
s.stickyElement.trigger('sticky-end', [s]);
s.currentTop = null;
}
}
else {
var newTop = documentHeight - s.stickyElement.outerHeight()
- s.topSpacing - s.bottomSpacing - scrollTop - extra;
if (newTop < 0) {
newTop = newTop + s.topSpacing;
} else {
newTop = s.topSpacing;
}
if (s.currentTop !== newTop) {
var newWidth;
if (s.getWidthFrom) {
newWidth = $(s.getWidthFrom).width() || null;
} else if (s.widthFromWrapper) {
newWidth = s.stickyWrapper.width();
}
if (newWidth == null) {
newWidth = s.stickyElement.width();
}
s.stickyElement
.css('width', newWidth)
.css('position', 'fixed')
.css('top', newTop)
.css('z-index', s.zIndex);
s.stickyElement.parent().addClass(s.className);
if (s.currentTop === null) {
s.stickyElement.trigger('sticky-start', [s]);
} else {
// sticky is started but it have to be repositioned
s.stickyElement.trigger('sticky-update', [s]);
}
if (s.currentTop === s.topSpacing && s.currentTop > newTop || s.currentTop === null && newTop < s.topSpacing) {
// just reached bottom || just started to stick but bottom is already reached
s.stickyElement.trigger('sticky-bottom-reached', [s]);
} else if(s.currentTop !== null && newTop === s.topSpacing && s.currentTop < newTop) {
// sticky is started && sticked at topSpacing && overflowing from top just finished
s.stickyElement.trigger('sticky-bottom-unreached', [s]);
}
s.currentTop = newTop;
}
// Check if sticky has reached end of container and stop sticking
var stickyWrapperContainer = s.stickyWrapper.parent();
var unstick = (s.stickyElement.offset().top + s.stickyElement.outerHeight() >= stickyWrapperContainer.offset().top + stickyWrapperContainer.outerHeight()) && (s.stickyElement.offset().top <= s.topSpacing);
if( unstick ) {
s.stickyElement
.css('position', 'absolute')
.css('top', '')
.css('bottom', 0)
.css('z-index', '');
} else {
s.stickyElement
.css('position', 'fixed')
.css('top', newTop)
.css('bottom', '')
.css('z-index', s.zIndex);
}
}
}
},
resizer = function() {
windowHeight = $window.height();
for (var i = 0, l = sticked.length; i < l; i++) {
var s = sticked[i];
var newWidth = null;
if (s.getWidthFrom) {
if (s.responsiveWidth) {
newWidth = $(s.getWidthFrom).width();
}
} else if(s.widthFromWrapper) {
newWidth = s.stickyWrapper.width();
}
if (newWidth != null) {
s.stickyElement.css('width', newWidth);
}
}
},
methods = {
init: function(options) {
return this.each(function() {
var o = $.extend({}, defaults, options);
var stickyElement = $(this);
var stickyId = stickyElement.attr('id');
var wrapperId = stickyId ? stickyId + '-' + defaults.wrapperClassName : defaults.wrapperClassName;
var wrapper = $('<div></div>')
.attr('id', wrapperId)
.addClass(o.wrapperClassName);
stickyElement.wrapAll(function() {
if ($(this).parent("#" + wrapperId).length == 0) {
return wrapper;
}
});
var stickyWrapper = stickyElement.parent();
if (o.center) {
stickyWrapper.css({width:stickyElement.outerWidth(),marginLeft:"auto",marginRight:"auto"});
}
if (stickyElement.css("float") === "right") {
stickyElement.css({"float":"none"}).parent().css({"float":"right"});
}
o.stickyElement = stickyElement;
o.stickyWrapper = stickyWrapper;
o.currentTop = null;
sticked.push(o);
methods.setWrapperHeight(this);
methods.setupChangeListeners(this);
});
},
setWrapperHeight: function(stickyElement) {
var element = $(stickyElement);
var stickyWrapper = element.parent();
if (stickyWrapper) {
stickyWrapper.css('height', element.outerHeight());
}
},
setupChangeListeners: function(stickyElement) {
if (window.MutationObserver) {
var mutationObserver = new window.MutationObserver(function(mutations) {
if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {
methods.setWrapperHeight(stickyElement);
}
});
mutationObserver.observe(stickyElement, {subtree: true, childList: true});
} else {
if (window.addEventListener) {
stickyElement.addEventListener('DOMNodeInserted', function() {
methods.setWrapperHeight(stickyElement);
}, false);
stickyElement.addEventListener('DOMNodeRemoved', function() {
methods.setWrapperHeight(stickyElement);
}, false);
} else if (window.attachEvent) {
stickyElement.attachEvent('onDOMNodeInserted', function() {
methods.setWrapperHeight(stickyElement);
});
stickyElement.attachEvent('onDOMNodeRemoved', function() {
methods.setWrapperHeight(stickyElement);
});
}
}
},
update: scroller,
unstick: function(options) {
return this.each(function() {
var that = this;
var unstickyElement = $(that);
var removeIdx = -1;
var i = sticked.length;
while (i-- > 0) {
if (sticked[i].stickyElement.get(0) === that) {
splice.call(sticked,i,1);
removeIdx = i;
}
}
if(removeIdx !== -1) {
unstickyElement.unwrap();
unstickyElement
.css({
'width': '',
'position': '',
'top': '',
'float': '',
'z-index': ''
})
;
}
});
}
};
// should be more efficient than using $window.scroll(scroller) and $window.resize(resizer):
if (window.addEventListener) {
window.addEventListener('scroll', scroller, false);
window.addEventListener('resize', resizer, false);
} else if (window.attachEvent) {
window.attachEvent('onscroll', scroller);
window.attachEvent('onresize', resizer);
}
$.fn.sticky = function(method) {
if (methods[method]) {
return methods[method].apply(this, slice.call(arguments, 1));
} else if (typeof method === 'object' || !method ) {
return methods.init.apply( this, arguments );
} else {
$.error('Method ' + method + ' does not exist on jQuery.sticky');
}
};
$.fn.unstick = function(method) {
if (methods[method]) {
return methods[method].apply(this, slice.call(arguments, 1));
} else if (typeof method === 'object' || !method ) {
return methods.unstick.apply( this, arguments );
} else {
$.error('Method ' + method + ' does not exist on jQuery.sticky');
}
};
$(function() {
setTimeout(scroller, 0);
});
}));
</script>
<script>
// Determine the content section from the URL and expand the appropriate menu section
var url = document.createElement('a');
url.href = window.location;
switch (url.pathname.split('/')[2]) {
case 'tutorials':
$('#tutorials').addClass('in');
break;
case 'about':
$('#about').addClass('in');
break;
case 'admin':
$('#administrators').addClass('in');
break;
case 'member':
$('#members').addClass('in');
break;
case 'developer':
$('#developers').addClass('in');
break;
default:
break;
}
// Generate the table of contents in the side nav menu (see view/tpl/help.tpl)
$(document).ready(function () {
var tocUl = $('#page-toc-container').append('<ul>').find('ul');
tocUl.removeClass(); // Classes are automatically added to <ul> elements by something else
tocUl.toc({content: "#doco-content", headings: "h1,h2,h3,h4"});
$('#region_1 .widget').scrollTop($(tocUl).offset().top - $('#accordion').offset().top);
$('#accordion .panel-title').click(function(event) {
if(!$('#accordion .panel-title').find('a').hasClass('collapsed') && !$('#accordion .panel-title').is(this)) {
$('#accordion .panel-title').find('span').removeClass('glyphicon-folder-close').addClass('glyphicon-folder-open');
} else {
$('#accordion .panel-title').find('span').removeClass('glyphicon-folder-open').addClass('glyphicon-folder-close');
$(".panel-collapse.in").find('a').each(function(){
window.console.log($(this).attr('href'));
var url = document.createElement('a');
url.href = window.location;
var pageName = url.href.split('/').pop();
window.console.log('pageName: ' + pageName);
var linkName = $(this).attr('href').split('/').pop();
window.console.log('linkName: ' + linkName);
if(pageName === linkName) {
var tocUl = $(this).closest('li').append('<ul>').find('ul');
tocUl.removeClass(); // Classes are automatically added to <ul> elements by something else
tocUl.toc({content: "#doco-content", headings: "h1,h2"});
tocUl.addClass('toc-content');
tocUl.sticky({topSpacing:$('nav').outerHeight(true), zIndex: 1000});
}
if(!$(this).find('a').hasClass('collapsed')) {
$(this).find('span').removeClass('glyphicon-folder-open').addClass('glyphicon-folder-close');
} else {
$(this).find('span').removeClass('glyphicon-folder-close').addClass('glyphicon-folder-open');
}
});
//$('#accordion').sticky({topSpacing:$('nav').outerHeight(true)});
});
</script>