Add an automatic TOC to pages that don't have no_toc in front-matter

This commit is contained in:
Misty Stanley-Jones 2017-01-30 15:34:25 -08:00
parent b547d91606
commit 8d3145ff3b
7 changed files with 150 additions and 115 deletions

View File

@ -5,6 +5,7 @@ kramdown:
html_to_native: true html_to_native: true
hard_wrap: false hard_wrap: false
syntax_highlighter: rouge syntax_highlighter: rouge
toc_levels: 2..3
incremental: true incremental: true
permalink: pretty permalink: pretty
safe: false safe: false
@ -29,6 +30,8 @@ defaults:
layout: docs layout: docs
defaultassignee: johndmulhausen defaultassignee: johndmulhausen
enginebranch: 1.13.x enginebranch: 1.13.x
toc_min: 2
toc_max: 3
- scope: - scope:
path: "compose" path: "compose"
values: values:

View File

@ -0,0 +1,70 @@
{% capture tocWorkspace %}
{% comment %}
"...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
Usage:
{% include toc_pure_liquid.html html=content sanitize=true class="inline_toc" id="my_toc" toc_min=2 toc_max=3 my_name="unnamed" %}
Variables:
* html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
* sanitize (bool) - when set to true, the headers will be sanitized in the TOC
* class (string) - a CSS class assigned to the TOC
* id (string) - an ID to assigned to the TOC
* toc_min (int) - the minimum TOC header level to use (if not set, check page, then site, then default to 2)
* toc_max (int) - the maximum TOC header level to use (if not set, check page, then site, then default to 3)
* page_name (string) - the URL of the page
Output:
An unordered list representing the table of contents of a markdown block. This snippet will only generate the table of contents and will NOT output the markdown given to it
{% endcomment %}
{% capture my_toc %}{% endcapture %}
{% assign minHeader = include.toc_min | default: 2 %}
{% assign maxHeader = include.toc_max | default: 3 %}
{% assign my_name = include.page_name | default: "unnamed" %}
{% assign nodes = include.html | split: '<h' %}
{% for node in nodes %}
{% if node == "" %}
{% continue %}
{% endif %}
{% assign headerLevel = node | replace: '"', '' | slice: 0, 1 %}
{% assign headerLevel = headerLevel | times: 1 %}
{% assign indentAmount = headerLevel | minus: minHeader | add: 1 %}
{% assign _workspace = node | split: '</h' %}
{% unless headerLevel >= minHeader %}
{% continue %}
{% endunless %}
{% if headerLevel > maxHeader %}
{% continue %}
{% endif %}
{% assign _idWorkspace = _workspace[0] | split: '"' %}
{% assign html_id = _idWorkspace[1] %}
{% capture _hAttrToStrip %}{{ headerLevel }} id="{{ html_id }}">{% endcapture %}
{% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
{% assign space = '' %}
{% for i in (1..indentAmount) %}
{% assign space = space | prepend: ' ' %}
{% endfor %}
{% capture my_toc %}{{ my_toc }}
{{ space }}- [{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}]({{ my_name }}#{{ html_id }}){: class="nomunge" }{% endcapture %}
{% endfor %}
{% if include.class %}
{% capture my_toc %}{:.{{ include.class }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% if include.id %}
{% capture my_toc %}{: #{{ include.id }}}
{{ my_toc | lstrip }}{% endcapture %}
{% endif %}
{% endcapture %}{% assign tocWorkspace = '' %}
{{ my_toc | markdownify }}

View File

@ -201,12 +201,12 @@ ng\:form {
</ul> </ul>
</div> </div>
</div> </div>
<div {% if page.notoc %} class="col-xs-12 col-sm-9 col-md-10" {% else %} class="col-xs-12 col-sm-9 col-md-8 col-xl-9" {% endif %} > <div class="col-xs-12 col-sm-9 col-md-8 col-xl-9">
<section class="section" id="DocumentationText"> <section class="section" id="DocumentationText">
{% if page.title %}<h1>{{ page.title }}</h1>{% endif %} {% if page.title %}<h1>{{ page.title }}</h1>{% endif %}
{% if page.advisory %}<blockquote style="border-left: 6px solid #FFD601; background: -webkit-gradient(linear, left top, left bottom, from(#FBFCFC), to(#EBEDEF));">{{ site.data.advisories.texts[page.advisory] | markdownify }}</blockquote>{% endif %} {% if page.advisory %}<blockquote style="border-left: 6px solid #FFD601; background: -webkit-gradient(linear, left top, left bottom, from(#FBFCFC), to(#EBEDEF));">{{ site.data.advisories.texts[page.advisory] | markdownify }}</blockquote>{% endif %}
{% include read_time.html %} {% include read_time.html %}
{{ content }} {{ content }}
{% if page.noratings != true %} {% if page.noratings != true %}
<div style="text-align: center; margin-top: 50px"> <div style="text-align: center; margin-top: 50px">
<img src="/images/chat.png" alt="chat icon" style="margin-right: 10px"> <img src="/images/chat.png" alt="chat icon" style="margin-right: 10px">
@ -252,26 +252,34 @@ ng\:form {
} }
</script> </script>
</div> </div>
{% if page.notoc != true %} <div class="hidden-xs hidden-sm col-md-2 col-xl-1 right_column_section">
<div class="hidden-xs hidden-sm col-md-2 col-xl-1 tableofcontents_section"> <section class="section" id="RightColumnSection">
<section class="section" id="TableOfContentsSection">
<span class="title_section"> <span class="title_section">
<form class="search-form form-inline ng-pristine ng-valid" id="searchForm" action="/search/"> <div id="search-div">
<form class="search-form form-inline ng-pristine ng-valid" id="searchForm" action="/search/">
<input class="search-field form-control ds-input" id="st-search-input" value="" name="q" placeholder="Search the docs" type="search" autocomplete="off" spellcheck="false" dir="auto" style="position: relative; vertical-align: top;"> <input class="search-field form-control ds-input" id="st-search-input" value="" name="q" placeholder="Search the docs" type="search" autocomplete="off" spellcheck="false" dir="auto" style="position: relative; vertical-align: top;">
<div id="autocompleteContainer"> <div id="autocompleteContainer">
<div id="autocompleteResults"></div> <div id="autocompleteResults"></div>
</div> </div>
<button type="submit" class="search-submit btn btn-default">Search</button> <button type="submit" class="search-submit btn btn-default">Search</button>
</form> </form>
</div>
</span> </span>
{% if edit_url != "" %} <div id="feedback-links">
<span><a href="{{ edit_url }}" class="button darkblue-btn nomunge" style="color:#FFFFFF; width:100%; margin: 0px;">Edit This Page</a></span> <ul>
{% endif %} {% if edit_url != "" %}<li><a href="{{ edit_url }}">&#9998;&nbsp;Edit this page</a></li>{% endif %}
<nav id="TableOfContents"> <li><a href="https://github.com/docker/docker.github.io/issues/new?title=Feedback for: {{ page.path }}&assignee={% if page.assignee %}{{ page.assignee }}{% else %}{{ page.defaultassignee }}{% endif %}&body=File: [{{ page.path }}](https://docs.docker.com{{ page.url }})" class="nomunge">&#10003;&nbsp;Request docs changes</a></li>
</nav> <li><a href="https://www.docker.com/docker-support-services">&#x0003F;&nbsp;Get support</a></li>
</ul>
</div>
{% unless page.notoc %}
{% assign my_min = page.toc_min | default: site.toc_min | default: 2 %}
{% assign my_max = page.toc_max | default: site.toc_max | default: 3 %}
{% assign my_name = page.url | default: "unnamed" %}
<div id="side-toc"><div id="side-toc-title">On this page:</div><div id="side-toc-contents">{% include toc_pure_liquid.html html=content sanitize=true class="inline_toc" id="my_toc" toc_min=my_min toc_max=my_max page_name=my_name %}</div></div>
{% endunless %}
</section> </section>
</div> </div>
{% endif %}<!-- end check for notoc != true -->
</div> </div>
</div> </div>
</div> </div>

View File

@ -131,9 +131,10 @@
color: black; color: black;
} }
#DocumentationText a, #DocumentationText a,
#TableOfContentsSection a { #RightColumnSection a {
color: #008AB5; color: #008AB5;
} }
/* reset away from #008AB5 */ /* reset away from #008AB5 */
/* removed hard-coded color for button links pre new CSS from Design */ /* removed hard-coded color for button links pre new CSS from Design */
@ -303,7 +304,7 @@ div.docsidebarnav_section.affix ul {
} }
/* TableOfContents */ /* TableOfContents */
.tableofcontents_section { .right_column_section {
padding: 0; padding: 0;
} }
.rating-msg { .rating-msg {
@ -316,55 +317,15 @@ div.docsidebarnav_section.affix ul {
display: none !important; display: none !important;
} }
#TableOfContents { #RightColumnSection {
padding-top: 10px;
}
#TableOfContentsSection {
margin: 0;
padding: 0 !important; padding: 0 !important;
background-color: #f5fdff;
} }
#TableOfContents li a, #RightColumnSection ul {
#TableOfContentsSection .heading { padding-top: 5px;
font-size: 14px;
font-style: normal;
margin: 0;
padding: 10px 15px;
display: block !important;
line-height: 15px;
}
#TableOfContentsSection .heading {
background-color: #6db9d1;
color: #ffffff;
}
#TableOfContentsSection #TableOfContents {
background-color: #F5FDFF;
margin: 0;
padding: 15px;
}
#TableOfContentsSection #TableOfContents ul {
list-style: none;
padding: 0;
left: 0;
padding: 0;
width: 100%;
text-align: left;
overflow: hidden;
margin-left: 0;
padding-left: 0.30rem;
font-size: 14px;
}
#TableOfContentsSection #TableOfContents > ul {
padding-left: 0;
list-style: none; list-style: none;
margin-left: 0;
} }
#TableOfContentsSection #TableOfContents > ul > li > ul {
padding-left: 0;
}
.advisory { .advisory {
color: #F04124; color: #F04124;
margin: -20px -20px 20px -20px; margin: -20px -20px 20px -20px;
@ -388,7 +349,6 @@ color: #F04124;
} }
.search-form .algolia-autocomplete .search-field, #st-search-input { .search-form .algolia-autocomplete .search-field, #st-search-input {
width: 100%; width: 100%;
border: 1px solid lightgray;
} }
.search-form{ .search-form{
max-width: 420px; max-width: 420px;
@ -451,3 +411,49 @@ span.reading-time-label {
img.with-border { img.with-border {
border: 1px solid #eaeaea; border: 1px solid #eaeaea;
} }
#search-div {
margin-top: 10px;
border: 1px solid #2294ca;
}
#side-toc {
background-color: #f5fdff;
border: 1px solid #2294ca;
padding: 10px;
margin-top: 20px;
width: 100%;
}
#feedback-links {
border: 1px solid #2294ca;
padding: 10px;
margin-top: 20px;
width: 100%;
}
#side-toc-contents,
#feedback-links {
list-style-type: none;
font-size: 80%;
}
#side-toc-contents ul,
#feedback-links ul {
margin-bottom: 0;
}
#side-toc-contents li {
padding-bottom: 7px;
padding-top: 7px;
line-height: 1!important;
}
#side-toc-contents ul ul li {
padding-left: 14px;
}
#side-toc-title {
font-weight: bolder;
font-size: 90%;
color: #155A74;
padding-bottom: 5px;
}

View File

@ -8,11 +8,6 @@ title: Post-installation steps for Linux
This section contains optional procedures for configuring Linux hosts to work This section contains optional procedures for configuring Linux hosts to work
better with Docker. better with Docker.
* [Manage Docker as a non-root user](#manage-docker-as-a-non-root-user)
* [Configure Docker to start on boot](#configure-docker-to-start-on-boot)
* [Allow access to the remote API through a firewall](#allow-access-to-the-remote-api-through-a-firewall)
* [Troubleshooting](#troubleshooting)
## Manage Docker as a non-root user ## Manage Docker as a non-root user
The `docker` daemon binds to a Unix socket instead of a TCP port. By default The `docker` daemon binds to a Unix socket instead of a TCP port. By default
@ -213,7 +208,7 @@ at `/etc/docker/daemon.json`.
2. Add a `dns` key with one or more IP addresses as values. If the file has 2. Add a `dns` key with one or more IP addresses as values. If the file has
existing contents, you only need to add or edit the `dns` line. existing contents, you only need to add or edit the `dns` line.
```json ```json
{ {
"dns": ["8.8.8.8", "8.8.4.4"] "dns": ["8.8.8.8", "8.8.4.4"]

View File

@ -3,6 +3,7 @@ description: Home page for Docker's documentation
keywords: Docker, documentation, manual, guide, reference, api keywords: Docker, documentation, manual, guide, reference, api
layout: docs layout: docs
title: Docker Documentation title: Docker Documentation
notoc: true
--- ---
Docker packages your app with its dependencies, freeing you from worrying about your Docker packages your app with its dependencies, freeing you from worrying about your

View File

@ -199,52 +199,4 @@ jQuery(document).ready(function(){
metadata = data; metadata = data;
hookupTOCEvents(); hookupTOCEvents();
}); });
$("#TableOfContents ul").empty();
var prevH2Item = null;
var prevH2List = null;
var index = 0;
var currentHeader = 0, lastHeader = 0;
var output = "<ul>";
$("h1, h2, h3, h4").each(function() {
var li= "<li><a href='" + window.location + "#" + $(this).attr('id') + "'>" + $(this).text().replace("¶","") + "</a></li>";
if( $(this).is("h2") ){
// h2
currentHeader = 2;
} else if( $(this).is("h3") ){
// h3
currentHeader = 3;
} else if( $(this).is("h4") ) {
// h4
currentHeader = 4;
}
//console.log("currentHeader ",currentHeader, "lastHeader ",lastHeader, "text ", $(this).text());
if (currentHeader > lastHeader) {
// nest further
output += "<ul>"
}
if (currentHeader < lastHeader && lastHeader > 0) {
// close nesting
//console.log("Closing nesting because ", lastHeader, "is <", currentHeader);
for (i=0; i < (lastHeader - currentHeader); i++)
{
output += "</ul>"
}
}
output += li;
lastHeader = currentHeader;
/*
if( $(this).is("h2") ){
prevH2List = $("<ul></ul>");
prevH2Item = $(li);
prevH2Item.append(prevH2List);
prevH2Item.appendTo("#TableOfContents ul");
} else {
prevH2List.append(li);
}
index++;*/
});
output += "</ul>";
$("#TableOfContents").html(output);
}); });