146 lines
3.3 KiB
Perl
146 lines
3.3 KiB
Perl
#!/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<div class=\"toc\"></div>\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<a name=\"$anchor\"></a>";
|
|
# 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."<ol>\n";
|
|
$curlevel++;
|
|
$index.=&$indent."<li class=\"L$curlevel\">\n";
|
|
}
|
|
$index.=&$indent."<ol>\n";
|
|
$curlevel=$level;
|
|
$liststarted=1;
|
|
}
|
|
elsif ($level < $curlevel) {
|
|
while ($level < $curlevel) {
|
|
$index.=&$indent."</li>\n" if $curlevel;
|
|
$curlevel--;
|
|
$index.=&$indent."</ol>\n";
|
|
}
|
|
$liststarted=0;
|
|
}
|
|
|
|
$index.=&$indent."</li>\n" unless $liststarted;
|
|
$liststarted=0;
|
|
$index.=&$indent."<li class=\"L$curlevel\">".
|
|
"<a href=\"#$anchor\">";
|
|
|
|
$p->handler(text => sub {
|
|
$page.=join("", @_);
|
|
$index.=join("", @_);
|
|
}, "dtext");
|
|
$p->handler(end => sub {
|
|
my $tagname=shift;
|
|
if ($tagname =~ /^h(\d+)$/i) {
|
|
$p->handler(text => undef);
|
|
$p->handler(end => undef);
|
|
$index.="</a>\n";
|
|
}
|
|
$page.=join("", @_);
|
|
}, "tagname, text");
|
|
}
|
|
else {
|
|
$page.=$text;
|
|
}
|
|
}, "tagname, text, attr");
|
|
$p->handler(default => sub { $page.=join("", @_) }, "text");
|
|
$p->parse($content);
|
|
$p->eof;
|
|
|
|
while ($startlevel && $curlevel >= $startlevel) {
|
|
$index.=&$indent."</li>\n" if $curlevel;
|
|
$curlevel--;
|
|
$index.=&$indent."</ol>\n";
|
|
}
|
|
|
|
$page=~s/(<div class=\"toc\">)/$1\n$index/;
|
|
return $page;
|
|
}
|
|
|
|
1
|