154 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
		
		
			
		
	
	
			154 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
|   | {{- $headers := findRE "<h[1-6].*?>(.|\n])+?</h[1-6]>" .Content -}} | ||
|  | {{- $has_headers := ge (len $headers) 1 -}} | ||
|  | {{- if $has_headers -}} | ||
|  | <aside id="toc-container" class="toc-container wide"> | ||
|  |     <div class="toc"> | ||
|  |         <details {{if (.Param "TocOpen") }} open{{ end }}> | ||
|  |             <summary accesskey="c" title="(Alt + C)"> | ||
|  |                 <span class="details">{{- i18n "toc" | default "Table of Contents" }}</span> | ||
|  |             </summary> | ||
|  | 
 | ||
|  |             <div class="inner"> | ||
|  |                 {{- $largest := 6 -}} | ||
|  |                 {{- range $headers -}} | ||
|  |                 {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}} | ||
|  |                 {{- $headerLevel := len (seq $headerLevel) -}} | ||
|  |                 {{- if lt $headerLevel $largest -}} | ||
|  |                 {{- $largest = $headerLevel -}} | ||
|  |                 {{- end -}} | ||
|  |                 {{- end -}} | ||
|  | 
 | ||
|  |                 {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} | ||
|  | 
 | ||
|  |                 {{- $.Scratch.Set "bareul" slice -}} | ||
|  |                 <ul> | ||
|  |                     {{- range seq (sub $firstHeaderLevel $largest) -}} | ||
|  |                     <ul> | ||
|  |                         {{- $.Scratch.Add "bareul" (sub (add $largest .) 1) -}} | ||
|  |                         {{- end -}} | ||
|  |                         {{- range $i, $header := $headers -}} | ||
|  |                         {{- $headerLevel := index (findRE "[1-6]" . 1) 0 -}} | ||
|  |                         {{- $headerLevel := len (seq $headerLevel) -}} | ||
|  | 
 | ||
|  |                         {{/* get id="xyz" */}} | ||
|  |                         {{- $id := index (findRE "(id=\"(.*?)\")" $header 9) 0 }} | ||
|  | 
 | ||
|  |                         {{- /* strip id="" to leave xyz, no way to get regex capturing groups in hugo */ -}} | ||
|  |                         {{- $cleanedID := replace (replace $id "id=\"" "") "\"" "" }} | ||
|  |                         {{- $header := replaceRE "<h[1-6].*?>((.|\n])+?)</h[1-6]>" "$1" $header -}} | ||
|  | 
 | ||
|  |                         {{- if ne $i 0 -}} | ||
|  |                         {{- $prevHeaderLevel := index (findRE "[1-6]" (index $headers (sub $i 1)) 1) 0 -}} | ||
|  |                         {{- $prevHeaderLevel := len (seq $prevHeaderLevel) -}} | ||
|  |                         {{- if gt $headerLevel $prevHeaderLevel -}} | ||
|  |                         {{- range seq $prevHeaderLevel (sub $headerLevel 1) -}} | ||
|  |                         <ul> | ||
|  |                             {{/* the first should not be recorded */}} | ||
|  |                             {{- if ne $prevHeaderLevel . -}} | ||
|  |                             {{- $.Scratch.Add "bareul" . -}} | ||
|  |                             {{- end -}} | ||
|  |                             {{- end -}} | ||
|  |                             {{- else -}} | ||
|  |                             </li> | ||
|  |                             {{- if lt $headerLevel $prevHeaderLevel -}} | ||
|  |                             {{- range seq (sub $prevHeaderLevel 1) -1 $headerLevel -}} | ||
|  |                             {{- if in ($.Scratch.Get "bareul") . -}} | ||
|  |                         </ul> | ||
|  |                         {{/* manually do pop item */}} | ||
|  |                         {{- $tmp := $.Scratch.Get "bareul" -}} | ||
|  |                         {{- $.Scratch.Delete "bareul" -}} | ||
|  |                         {{- $.Scratch.Set "bareul" slice}} | ||
|  |                         {{- range seq (sub (len $tmp) 1) -}} | ||
|  |                         {{- $.Scratch.Add "bareul" (index $tmp (sub . 1)) -}} | ||
|  |                         {{- end -}} | ||
|  |                         {{- else -}} | ||
|  |                     </ul> | ||
|  |                     </li> | ||
|  |                     {{- end -}} | ||
|  |                     {{- end -}} | ||
|  |                     {{- end -}} | ||
|  |                     {{- end }} | ||
|  |                     <li> | ||
|  |                         <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a> | ||
|  |                         {{- else }} | ||
|  |                     <li> | ||
|  |                         <a href="#{{- $cleanedID -}}" aria-label="{{- $header | plainify -}}">{{- $header | safeHTML -}}</a> | ||
|  |                         {{- end -}} | ||
|  |                         {{- end -}} | ||
|  |                         <!-- {{- $firstHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers 0) 1) 0)) -}} --> | ||
|  |                         {{- $firstHeaderLevel := $largest }} | ||
|  |                         {{- $lastHeaderLevel := len (seq (index (findRE "[1-6]" (index $headers (sub (len $headers) 1)) 1) 0)) }} | ||
|  |                     </li> | ||
|  |                     {{- range seq (sub $lastHeaderLevel $firstHeaderLevel) -}} | ||
|  |                     {{- if in ($.Scratch.Get "bareul") (add . $firstHeaderLevel) }} | ||
|  |                 </ul> | ||
|  |                 {{- else }} | ||
|  |                 </ul> | ||
|  |                 </li> | ||
|  |                 {{- end -}} | ||
|  |                 {{- end }} | ||
|  |                 </ul> | ||
|  |             </div> | ||
|  |         </details> | ||
|  |     </div> | ||
|  | </aside> | ||
|  | <script> | ||
|  |     let activeElement; | ||
|  |     let elements; | ||
|  |     window.addEventListener('DOMContentLoaded', function (event) { | ||
|  |         checkTocPosition(); | ||
|  | 
 | ||
|  |         elements = document.querySelectorAll('h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]'); | ||
|  |          // Make the first header active | ||
|  |          activeElement = elements[0]; | ||
|  |          const id = encodeURI(activeElement.getAttribute('id')).toLowerCase(); | ||
|  |          document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active'); | ||
|  |      }, false); | ||
|  | 
 | ||
|  |     window.addEventListener('resize', function(event) { | ||
|  |         checkTocPosition(); | ||
|  |     }, false); | ||
|  | 
 | ||
|  |     window.addEventListener('scroll', () => { | ||
|  |         // Check if there is an object in the top half of the screen or keep the last item active | ||
|  |         activeElement = Array.from(elements).find((element) => { | ||
|  |             if ((getOffsetTop(element) - window.pageYOffset) > 0 &&  | ||
|  |                 (getOffsetTop(element) - window.pageYOffset) < window.innerHeight/2) { | ||
|  |                 return element; | ||
|  |             } | ||
|  |         }) || activeElement | ||
|  | 
 | ||
|  |         elements.forEach(element => { | ||
|  |              const id = encodeURI(element.getAttribute('id')).toLowerCase(); | ||
|  |              if (element === activeElement){ | ||
|  |                  document.querySelector(`.inner ul li a[href="#${id}"]`).classList.add('active'); | ||
|  |              } else { | ||
|  |                  document.querySelector(`.inner ul li a[href="#${id}"]`).classList.remove('active'); | ||
|  |              } | ||
|  |          }) | ||
|  |      }, false); | ||
|  | 
 | ||
|  |     const main = parseInt(getComputedStyle(document.body).getPropertyValue('--article-width'), 10); | ||
|  |     const toc = parseInt(getComputedStyle(document.body).getPropertyValue('--toc-width'), 10); | ||
|  |     const gap = parseInt(getComputedStyle(document.body).getPropertyValue('--gap'), 10); | ||
|  | 
 | ||
|  |     function checkTocPosition() { | ||
|  |         const width = document.body.scrollWidth; | ||
|  | 
 | ||
|  |         if (width - main - (toc * 2) - (gap * 4) > 0) { | ||
|  |             document.getElementById("toc-container").classList.add("wide"); | ||
|  |         } else { | ||
|  |             document.getElementById("toc-container").classList.remove("wide"); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     function getOffsetTop(element) { | ||
|  |         if (!element.getClientRects().length) { | ||
|  |             return 0; | ||
|  |         } | ||
|  |         let rect = element.getBoundingClientRect(); | ||
|  |         let win = element.ownerDocument.defaultView; | ||
|  |         return rect.top + win.pageYOffset;    | ||
|  |     } | ||
|  | </script> | ||
|  | {{- end }} |