From ae502fee357dad7cad3c4cf1c178a2d57053bf16 Mon Sep 17 00:00:00 2001
From: Jake <j.vondoemming@stud.uni-goettingen.de>
Date: Wed, 12 Oct 2022 00:22:19 +0200
Subject: [PATCH] added support for TOC

closes GAUMI-fginfo/fg-website#46
---
 fgs/pandoc.py                                | 62 ++++++++++++++++++--
 lang.json                                    |  3 +
 theme/templates/macros/content_renderer.html | 34 ++++++++++-
 theme/templates/macros/renderers.html        |  4 +-
 4 files changed, 93 insertions(+), 10 deletions(-)

diff --git a/fgs/pandoc.py b/fgs/pandoc.py
index 2139783..541c8e0 100644
--- a/fgs/pandoc.py
+++ b/fgs/pandoc.py
@@ -51,11 +51,7 @@ def run_pandoc(source, factories, lang, base="markdown", extensions=[], extra_ar
     elementlist = convert_elements_to_list(blocks)
 
     contentmetadata = {}
-    contentmetadata["toc_list"] = []
-    contentmetadata["toc_count"] = 0
 
-    # TODO TOC
-    #contentmetadata["toc"] = build_toc(n["toc_list"].copy())
 
     # Get all images on page
     images = []
@@ -67,13 +63,69 @@ def run_pandoc(source, factories, lang, base="markdown", extensions=[], extra_ar
     if contentmetadata["has_image"]:
         contentmetadata["image"] = images[0]
 
+    # Get all headers on page
+    headers = []
+    for e in elementlist:
+        if e.etype == "header":
+            headers.append(e)
+    contentmetadata["headers"] = headers
+    contentmetadata["has_header"] = (len(headers) > 0)
 
+    # Add Table-of-Contents
+    contentmetadata["toc"] = build_toc(headers)
 
     #blocks = json.loads(json.dumps(blocks, cls=ElementEncoder)) # Reduce to 'simple' dict, which can be converted to JSON in Jinja2.
 
     return (blocks, contentmetadata)
 
 
+def build_toc(headerslist):
+    cache = {
+                "l1": None,
+                "l2": None,
+                "l3": None,
+                "l4": None,
+                "l5": None,
+                "l6": None
+            }
+
+    trees = []
+
+    for cur in headerslist:
+        lvl = cur.level
+        if lvl not in range(1,7):
+            raise Exception("Header has invalid Level.",lvl,cur)
+        higher_key = "l"+str(lvl-1)
+
+        # Create new tree element
+        tree = {
+                    "element": cur,
+                    "children": []
+                }
+        if lvl == 1 or cache[higher_key] == None:
+            # This is a new root tree
+            trees.append(tree)
+        else:
+            # This is just a child of another tree
+            cache[higher_key]["children"].append(tree)
+
+        # Overwrite cache entries [lvl,6]
+        for curlevel in range(lvl, 7):
+            cache["l"+str(curlevel)] = tree
+
+    #def print_toc(toc, indent):
+    #    x=' '
+    #    for tree in toc:
+    #        print(f"{x*indent}- h{tree['element'].level}({tree['element'].attr['id']}):")
+    #        print_toc(tree["children"], indent+1)
+    #print("===TOC===")
+    #print_toc(trees,0)
+    #print("===/TOC===")
+
+    return trees
+
+
+
 def convert_elements_to_list(elements):
     res = []
     for element in elements:
@@ -706,7 +758,7 @@ class CustomSyntaxHandler:
 class CustomBlockTOC(Block):
     etype = "toc"
     def replace(self, origelement, custom_sytax_register_entry):
-        pass # TODO continue
+        pass # Necessary information for rendering can be found in page.metadata['toc']
 
 
 ############################## REGISTER #######################################
diff --git a/lang.json b/lang.json
index d752806..1ecf58e 100644
--- a/lang.json
+++ b/lang.json
@@ -56,6 +56,9 @@
 				"left": "&sbquo;",
 				"right": "&lsquo;"
 			}
+		},
+		"toc": {
+			"header": "Inhaltsverzeichnis"
 		}
 	}
 }
diff --git a/theme/templates/macros/content_renderer.html b/theme/templates/macros/content_renderer.html
index 0793c8b..04a3233 100644
--- a/theme/templates/macros/content_renderer.html
+++ b/theme/templates/macros/content_renderer.html
@@ -1,8 +1,9 @@
 
 {#- Siehe https://gitlab.gwdg.de/GAUMI-fginfo/fg-website/-/blob/better-content-renderer/docs/content.md -#}
 
-{%- macro render_content(content, lang, header_level_offset, in_footer) -%}
-	{%- set meta = namespace(header_level_offset=header_level_offset, in_footer=in_footer) -%}
+{%- macro render_content(page, lang, header_level_offset, in_footer) -%}
+	{%- set content = page.content -%}
+	{%- set meta = namespace(header_level_offset=header_level_offset, in_footer=in_footer, page=page) -%}
 	{{ render_blocks(content, lang, meta) }}
 {%- endmacro -%}
 
@@ -22,7 +23,11 @@
 
 {%- macro render_header(content, level, attr, lang, in_footer) -%}
 	{%- call common.render_header_raw(content, level, attr, lang, in_footer=in_footer) -%}
-		{{ render_inlines(content, lang) }}
+		{%- if content is string -%}
+			{{ common.encoded_span(content) }}
+		{%- else -%}
+			{{ render_inlines(content, lang) }}
+		{%- endif -%}
 	{%- endcall -%}
 {%- endmacro -%}
 
@@ -93,6 +98,8 @@
 		{{ render_block_definitionlist(block, lang, meta) }}
 	{%- elif etype == "lineblock" -%}
 		{{ render_block_lineblock(block, lang, meta) }}
+	{%- elif etype == "toc" -%}
+		{{ render_block_toc(block, lang, meta) }}
 	{%- else -%}
 		<br><strong>ERROR: Unhandled block type: '{{ etype|e }}'</strong><br>
 	{%- endif -%}
@@ -291,6 +298,27 @@
 	</div>
 {%- endmacro -%}
 
+{%- macro render_toc(tree, lang, meta) -%}
+	{%- if tree.element is defined -%}
+		<a class="internal" href="#{{ tree.element.attr.id }}">{{ render_inlines(tree.element.content, lang) }}</a>
+	{%- endif -%}
+
+	{%- if tree.children|length -%}
+		<ul>
+			{%- for tree in tree.children -%}
+				<li>{{ render_toc(tree, lang, meta) }}</li>
+			{%- endfor -%}
+		</ul>
+	{%- endif -%}
+{%- endmacro -%}
+{%- macro render_block_toc(block, lang, meta) -%}
+	{%- set toc = meta.page.metadata.toc -%}
+	<nav class="toc">
+		{{ render_header(t[lang].toc.header, 2 + meta.header_level_offset, None, lang, meta.in_footer) }}
+		{{ render_toc({"children": toc}, lang, meta) }}
+	</nav>
+{%- endmacro -%}
+
 
 {#- ############################ INLINES ################################## -#}
 
diff --git a/theme/templates/macros/renderers.html b/theme/templates/macros/renderers.html
index 189cc56..2877ad5 100644
--- a/theme/templates/macros/renderers.html
+++ b/theme/templates/macros/renderers.html
@@ -21,7 +21,7 @@
 		{%- if 'before' in s.tag.page.metadata -%}
 			{{ sections(s.tag.page.metadata.before, lang, header_level + 1, in_footer=in_footer) }}
 		{%- endif -%}
-		{{ content_renderer.render_content(s.tag.page.content, lang, header_level - 1, in_footer=in_footer) }}
+		{{ content_renderer.render_content(s.tag.page, lang, header_level - 1, in_footer=in_footer) }}
 		{%- if 'after' in s.tag.page.metadata -%}
 			{{ sections(s.tag.page.metadata.after, lang, header_level + 1, in_footer=in_footer) }}
 		{%- endif -%}
@@ -39,7 +39,7 @@
 	{%- if 'before' in s.page.metadata -%}
 		{{ sections(s.page.metadata.before, lang, header_level + 1, in_footer=in_footer) }}
 	{%- endif -%}
-	{{ content_renderer.render_content(s.page.content, lang, header_level - 1, in_footer=in_footer) }}
+	{{ content_renderer.render_content(s.page, lang, header_level - 1, in_footer=in_footer) }}
 	{%- if 'after' in s.page.metadata -%}
 		{{ sections(s.page.metadata.after, lang, header_level + 1, in_footer=in_footer) }}
 	{%- endif -%}
-- 
GitLab