Create toc2.py
diff --git a/theme/plugins/toc2.py b/theme/plugins/toc2.py
new file mode 100644
index 0000000..87885c3
--- /dev/null
+++ b/theme/plugins/toc2.py
@@ -0,0 +1,152 @@
+'''
+toc
+===================================
+Generates Table of Contents for markdown.
+Only generates a ToC for the headers FOLLOWING th [TOC] tag,
+so you can insert it after a specific section that need not be
+include in the ToC.
+'''
+
+from __future__ import unicode_literals
+
+import logging
+import re
+
+from bs4 import BeautifulSoup, Comment
+
+from pelican import contents, signals
+from pelican.utils import slugify
+
+
+logger = logging.getLogger(__name__)
+
+'''
+https://github.com/waylan/Python-Markdown/blob/master/markdown/extensions/headerid.py
+'''
+IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
+
+
+def unique(id, ids):
+ """ Ensure id is unique in set of ids. Append '_1', '_2'... if not """
+ while id in ids or not id:
+ m = IDCOUNT_RE.match(id)
+ if m:
+ id = '%s_%d' % (m.group(1), int(m.group(2)) + 1)
+ else:
+ id = '%s_%d' % (id, 1)
+ ids.add(id)
+ return id
+'''
+end
+'''
+
+
+class HtmlTreeNode(object):
+ def __init__(self, parent, header, level, id):
+ self.children = []
+ self.parent = parent
+ self.header = header
+ self.level = level
+ self.id = id
+
+ def add(self, new_header, ids):
+ new_level = new_header.name
+ new_string = new_header.string
+ new_id = new_header.attrs.get('id')
+
+ if not new_string:
+ new_string = new_header.find_all(
+ text=lambda t: not isinstance(t, Comment),
+ recursive=True)
+ new_string = "".join(new_string)
+
+ if not new_id:
+ new_id = slugify(new_string, ())
+
+ new_id = unique(new_id, ids) # make sure id is unique
+
+ new_header.attrs['id'] = new_id
+ if(self.level < new_level):
+ new_node = HtmlTreeNode(self, new_string, new_level, new_id)
+ self.children += [new_node]
+ return new_node, new_header
+ elif(self.level == new_level):
+ new_node = HtmlTreeNode(self.parent, new_string, new_level, new_id)
+ self.parent.children += [new_node]
+ return new_node, new_header
+ elif(self.level > new_level):
+ return self.parent.add(new_header, ids)
+
+ def __str__(self):
+ ret = ""
+ if self.parent:
+ ret = "<a class='toc-href' href='#{0}' title='{1}'>{1}</a>".format(
+ self.id, self.header)
+
+ if self.children:
+ ret += "<ul>{}</ul>".format('{}'*len(self.children)).format(
+ *self.children)
+
+ if self.parent:
+ ret = "<li>{}</li>".format(ret)
+
+ if not self.parent:
+ ret = "<div id='toc'>{}</div>".format(ret)
+
+ return ret
+
+
+def init_default_config(pelican):
+ from pelican.settings import DEFAULT_CONFIG
+
+ TOC_DEFAULT = {
+ 'TOC_HEADERS': '^h[1-6]',
+ 'TOC_RUN': 'true'
+ }
+
+ DEFAULT_CONFIG.setdefault('TOC', TOC_DEFAULT)
+ if(pelican):
+ pelican.settings.setdefault('TOC', TOC_DEFAULT)
+
+
+def generate_toc(content):
+ if isinstance(content, contents.Static):
+ return
+
+ all_ids = set()
+ title = content.metadata.get('title', 'Title')
+ tree = node = HtmlTreeNode(None, title, 'h0', '')
+ soup = BeautifulSoup(content._content, 'html.parser')
+ settoc = False
+
+ try:
+ header_re = re.compile(content.metadata.get(
+ 'toc_headers', content.settings['TOC']['TOC_HEADERS']))
+ except re.error as e:
+ logger.error("TOC_HEADERS '%s' is not a valid re\n%s",
+ content.settings['TOC']['TOC_HEADERS'])
+ raise e
+
+ # Find TOC tag
+ tocTag = soup.find('p', text = '[TOC]')
+ if tocTag:
+ for header in tocTag.findAllNext(header_re):
+ settoc = True
+ node, new_header = node.add(header, all_ids)
+ header.replaceWith(new_header) # to get our ids back into soup
+
+ if settoc:
+ print("Generating ToC for %s" % content.slug)
+ tree_string = '{}'.format(tree)
+ tree_soup = BeautifulSoup(tree_string, 'html.parser')
+ content.toc = tree_soup.decode(formatter='html')
+ itoc = soup.find('p', text = '[TOC]')
+ if itoc:
+ itoc.replaceWith(tree_soup)
+
+ content._content = soup.decode(formatter='html')
+
+
+def register():
+ signals.initialized.connect(init_default_config)
+signals.content_object_init.connect(generate_toc)