#!/usr/bin/perl # Table Of Contents generator package IkiWiki::Plugin::toc; use warnings; use strict; use IkiWiki 3.00; use HTML::Parser; sub import { hook(type => "getsetup", id => "toc", call => \&getsetup); hook(type => "preprocess", id => "toc", call => \&preprocess); hook(type => "format", id => "toc", call => \&format); } sub getsetup () { return plugin => { safe => 1, rebuild => undef, section => "widget", }, } 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
\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->new(api_version => 3); my $page=""; my $index=""; my %anchors; my $startlevel=($params{startlevel} ? $params{startlevel} : 0); my $curlevel=$startlevel-1; my $liststarted=0; my $indent=sub { "\t" x $curlevel }; $p->handler(start => sub { my ($tagname, $text, $attr) = @_; if ($tagname =~ /^h(\d+)$/i) { my $level=$1; my $anchor="index".++$anchors{$level}."h$level"; $page.="$text"; # if the heading already has a unique ID, use that instead in TOC if ($attr->{id}) { $anchor = $attr->{id}; } # Unless we're given startlevel as a parameter, # 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 (defined $params{startlevel} && $level < $params{startlevel}) { return; } elsif ($level < $startlevel) { $level=$startlevel; } return if $level - $startlevel >= $params{levels}; if ($level > $curlevel) { while ($level > $curlevel + 1) { $index.=&$indent."