<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#!/usr/bin/perl
# Table Of Contents generator
package IkiWiki::Plugin::toc;

use warnings;
use strict;
use IkiWiki 2.00;
use HTML::Parser;

sub import { #{{{
	hook(type =&gt; "preprocess", id =&gt; "toc", call =&gt; \&amp;preprocess);
	hook(type =&gt; "format", id =&gt; "toc", call =&gt; \&amp;format);
} # }}}

my %tocpages;

sub preprocess (@) { #{{{
	my %params=@_;

	if ($params{page} eq $params{destpage}) {
		$params{levels}=1 unless exists $params{levels};

		# It's too early to generate the toc here, so just record the
		# info.
		$tocpages{$params{destpage}}=\%params;

		return "\n&lt;div class=\"toc\"&gt;&lt;/div&gt;\n";
	}
	else {
		# Don't generate toc in an inlined page, doesn't work
		# right.
		return "";
	}
} # }}}

sub format (@) { #{{{
	my %params=@_;
	my $content=$params{content};
	
	return $content unless exists $tocpages{$params{page}};
	%params=%{$tocpages{$params{page}}};

	my $p=HTML::Parser-&gt;new(api_version =&gt; 3);
	my $page="";
	my $index="";
	my %anchors;
	my $curlevel;
	my $startlevel=0;
	my $liststarted=0;
	my $indent=sub { "\t" x $curlevel };
	$p-&gt;handler(start =&gt; sub {
		my $tagname=shift;
		my $text=shift;
		if ($tagname =~ /^h(\d+)$/i) {
			my $level=$1;
			my $anchor="index".++$anchors{$level}."h$level";
			$page.="$text&lt;a name=\"$anchor\"&gt;&lt;/a&gt;";
	
			# Take the first header level seen as the topmost level,
			# even if there are higher levels seen later on.
			if (! $startlevel) {
				$startlevel=$level;
				$curlevel=$startlevel-1;
			}
			elsif ($level &lt; $startlevel) {
				$level=$startlevel;
			}
			
			return if $level - $startlevel &gt;= $params{levels};
	
			if ($level &gt; $curlevel) {
				while ($level &gt; $curlevel + 1) {
					$index.=&amp;$indent."&lt;ol&gt;\n";
					$curlevel++;
					$index.=&amp;$indent."&lt;li class=\"L$curlevel\"&gt;\n";
				}
				$index.=&amp;$indent."&lt;ol&gt;\n";
				$curlevel=$level;
				$liststarted=1;
			}
			elsif ($level &lt; $curlevel) {
				while ($level &lt; $curlevel) {
					$index.=&amp;$indent."&lt;/li&gt;\n" if $curlevel;
					$curlevel--;
					$index.=&amp;$indent."&lt;/ol&gt;\n";
				}
				$liststarted=0;
			}
	
			$p-&gt;handler(text =&gt; sub {
				$page.=join("", @_);
				$index.=&amp;$indent."&lt;/li&gt;\n" unless $liststarted;
				$liststarted=0;
				$index.=&amp;$indent."&lt;li class=\"L$curlevel\"&gt;".
					"&lt;a href=\"#$anchor\"&gt;".
					join("", @_).
					"&lt;/a&gt;\n";
				$p-&gt;handler(text =&gt; undef);
			}, "dtext");
		}
		else {
			$page.=$text;
		}
	}, "tagname, text");
	$p-&gt;handler(default =&gt; sub { $page.=join("", @_) }, "text");
	$p-&gt;parse($content);
	$p-&gt;eof;

	while ($startlevel &amp;&amp; $curlevel &gt;= $startlevel) {
		$index.=&amp;$indent."&lt;/li&gt;\n" if $curlevel;
		$curlevel--;
		$index.=&amp;$indent."&lt;/ol&gt;\n";
	}

	$page=~s/(&lt;div class=\"toc\"&gt;)/$1\n$index/;
	return $page;
}

1
</pre></body></html>