1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
#!/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 => "preprocess", id => "toc", call => \&preprocess);
hook(type => "format", id => "toc", call => \&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<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 $curlevel;
my $startlevel=0;
my $liststarted=0;
my $indent=sub { "\t" x $curlevel };
$p->handler(start => sub {
my $tagname=shift;
my $text=shift;
if ($tagname =~ /^h(\d+)$/i) {
my $level=$1;
my $anchor="index".++$anchors{$level}."h$level";
$page.="$text<a name=\"$anchor\"></a>";
# 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 < $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");
$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
|