From: Julien Moutinho Date: Thu, 13 Feb 2014 18:43:45 +0000 (+0100) Subject: init from git://git.ikiwiki.info/ X-Git-Url: http://git.cyclocoop.org/?p=ikiwiki%2Ftoc.git;a=commitdiff_plain;h=26b7807f82a3d1ce5e19af3abf07200b281e6a5b init from git://git.ikiwiki.info/ --- 26b7807f82a3d1ce5e19af3abf07200b281e6a5b diff --git a/toc.pm b/toc.pm new file mode 100644 index 0000000..ac07b9a --- /dev/null +++ b/toc.pm @@ -0,0 +1,142 @@ +#!/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=shift; + my $text=shift; + if ($tagname =~ /^h(\d+)$/i) { + my $level=$1; + my $anchor="index".++$anchors{$level}."h$level"; + $page.="$text"; + + # 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."
    \n"; + $curlevel++; + $index.=&$indent."
  1. \n"; + } + $index.=&$indent."
      \n"; + $curlevel=$level; + $liststarted=1; + } + elsif ($level < $curlevel) { + while ($level < $curlevel) { + $index.=&$indent."\n" if $curlevel; + $curlevel--; + $index.=&$indent."
    \n"; + } + $liststarted=0; + } + + $index.=&$indent."
  2. \n" unless $liststarted; + $liststarted=0; + $index.=&$indent."
  3. ". + ""; + + $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.="\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."
  4. \n" if $curlevel; + $curlevel--; + $index.=&$indent."
\n"; + } + + $page=~s/(
)/$1\n$index/; + return $page; +} + +1