paste-o
[ikiwiki] / IkiWiki / Plugin / map.pm
1 #!/usr/bin/perl
2 #
3 # Produce a hierarchical map of links.
4 #
5 # by Alessandro Dotti Contra <alessandro@hyboria.org>
6 #
7 # Revision: 0.2
8 package IkiWiki::Plugin::map;
9
10 use warnings;
11 use strict;
12 use IkiWiki 2.00;
13
14 sub import { #{{{
15         hook(type => "preprocess", id => "map", call => \&preprocess);
16 } # }}}
17
18 sub preprocess (@) { #{{{
19         my %params=@_;
20         $params{pages}="*" unless defined $params{pages};
21         
22         my $common_prefix;
23
24         # Get all the items to map.
25         my %mapitems;
26         foreach my $page (keys %pagesources) {
27                 if (pagespec_match($page, $params{pages}, location => $params{page})) {
28                         $mapitems{$page}=1;
29
30                         # Check for a common prefix.
31                         if (! defined $common_prefix) {
32                                 $common_prefix=$page;
33                         }
34                         elsif (length $common_prefix &&
35                                $page !~ /^\Q$common_prefix\E(\/|$)/) {
36                                 my @a=split(/\//, $page);
37                                 my @b=split(/\//, $common_prefix);
38                                 $common_prefix="";
39                                 while (@a && @b && $a[0] eq $b[0]) {
40                                         if (length $common_prefix) {
41                                                 $common_prefix.="/";
42                                         }
43                                         $common_prefix.=shift(@a);
44                                         shift @b;
45                                 }
46                         }
47                 }
48         }
49         
50         # Common prefix should not be a page in the map.
51         while (defined $common_prefix && length $common_prefix &&
52                exists $mapitems{$common_prefix}) {
53                 $common_prefix=IkiWiki::dirname($common_prefix);
54         }
55
56         # Needs to update whenever a page is added or removed, so
57         # register a dependency.
58         add_depends($params{page}, $params{pages});
59         # Explicitly add all currently shown pages, to detect when pages
60         # are removed.
61         add_depends($params{page}, join(" or ", keys %mapitems));
62
63         # Create the map.
64         my $parent="";
65         my $indent=0;
66         my $openli=0;
67         my $dummy=0;
68         my $map = "<div class='map'>\n<ul>\n";
69         foreach my $item (sort keys %mapitems) {
70                 $item=~s/^\Q$common_prefix\E\///
71                         if defined $common_prefix && length $common_prefix;
72                 my $depth = ($item =~ tr/\//\//) + 1;
73                 my $baseitem=IkiWiki::dirname($item);
74                 while (length $parent && length $baseitem && $baseitem !~ /^\Q$parent\E(\/|$)/) {
75                         $parent=IkiWiki::dirname($parent);
76                         last if !$dummy && length $parent && $baseitem =~ /^\Q$parent\E(\/|$)/;
77                         $indent--;
78                         $map .= "</li>\n";
79                         if ($indent > 0) {
80                                 $map .= "</ul>\n";
81                         }
82                 }
83                 $dummy=0;
84                 while ($depth < $indent) {
85                         $indent--;
86                         $map .= "</li>\n";
87                         if ($indent > 0) {
88                                 $map .= "</ul>\n";
89                         }
90                 }
91                 my @bits=split("/", $item);
92                 my $p="";
93                 $p.="/".shift(@bits) for 1..$indent;
94                 while ($depth > $indent) {
95                         $indent++;
96                         if ($indent > 1) {
97                                 $map .= "<ul>\n";
98                         }
99                         if ($depth > $indent) {
100                                 $dummy=1;
101                                 $p.="/".shift(@bits);
102                                 $map .= "<li>"
103                                         .htmllink($params{page}, $params{destpage},
104                                                  $p, class => "mapparent",
105                                                  noimageinline => 1)
106                                         ."\n";
107                                 $openli=1;
108                         }
109                         else {
110                                 $openli=0;
111                         }
112                 }
113                 $map .= "</li>\n" if $openli;
114                 $map .= "<li>"
115                         .htmllink($params{page}, $params{destpage}, 
116                                 "/".$common_prefix."/".$item,
117                                 class => "mapitem", noimageinline => 1)
118                         ."\n";
119                 $openli=1;
120                 $parent=$item;
121         }
122         while ($indent > 0) {
123                 $indent--;
124                 $map .= "</li>\n</ul>\n";
125         }
126         $map .= "</div>\n";
127         return $map;
128 } # }}}
129
130 1