File Coverage

blib/lib/HTML/LinkList.pm
Criterion Covered Total %
statement 281 307 91.5
branch 134 168 79.7
condition 60 96 62.5
subroutine 21 21 100.0
pod 18 18 100.0
total 514 610 84.2


line stmt bran cond sub pod time code
1             package HTML::LinkList;
2             $HTML::LinkList::VERSION = '0.1701';
3 5     5   106380 use strict;
  5         12  
  5         134  
4 5     5   25 use warnings;
  5         9  
  5         212  
5              
6             =head1 NAME
7              
8             HTML::LinkList - Create a 'smart' list of HTML links.
9              
10             =head1 VERSION
11              
12             version 0.1701
13              
14             =head1 SYNOPSIS
15              
16             use HTML::LinkList qw(link_list);
17              
18             # default formatting
19             my $html_links = link_list(current_url=>$url,
20             urls=>\@links_in_order,
21             labels=>\%labels,
22             descriptions=>\%desc);
23              
24             # paragraph with ' :: ' separators
25             my $html_links = link_list(current_url=>$url,
26             urls=>\@links_in_order,
27             labels=>\%labels,
28             descriptions=>\%desc,
29             links_head=>'

',

30             links_foot=>'

',
31             pre_item=>'',
32             post_item=>''
33             pre_active_item=>'',
34             post_active_item=>'',
35             item_sep=>" :: ");
36              
37             # multi-level list
38             my $html_links = link_tree(
39             current_url=>$url,
40             link_tree=>\@list_of_lists,
41             labels=>\%labels,
42             descriptions=>\%desc);
43              
44              
45             =head1 DESCRIPTION
46              
47             This module contains a number of functions for taking sets of URLs and
48             labels and creating suitably formatted HTML. These links are "smart"
49             because, if given the url of the current page, if any of the links in
50             the list equal it, that item in the list will be formatted as a special
51             label, not as a link; this is a Good Thing, since the user would be
52             confused by clicking on a link back to the current page.
53              
54             While many website systems have plugins for "smart" navbars, they are
55             specialized for that system only, and can't be reused elsewhere, forcing
56             people to reinvent the wheel. I hereby present one wheel, free to be
57             reused by anybody; just the simple functions, a backend, which can be
58             plugged into whatever system you want.
59              
60             The default format for the HTML is to make an unordered list, but there
61             are many options, enabling one to have a flatter layout with any
62             separators you desire, or a more complicated list with differing
63             formats for different levels.
64              
65             The "link_list" function uses a simple list of links -- good for a
66             simple navbar.
67              
68             The "link_tree" function takes a set of nested links and makes the HTML
69             for them -- good for making a table of contents, or a more complicated
70             navbar.
71              
72             The "full_tree" function takes a list of paths and makes a full tree of
73             all the pages and index-pages in those paths -- good for making a site
74             map.
75              
76             The "breadcrumb_trail" function takes a url and makes a "breadcrumb trail"
77             from it.
78              
79             The "nav_tree" function creates a set of nested links to be
80             used as a multi-level navbar; one can give it a list of paths
81             (as for full_tree) and it will only show the links related
82             to the current URL.
83              
84             =cut
85              
86             =head1 FUNCTIONS
87              
88             To export a function, add it to the 'use' call.
89              
90             use HTML::LinkList qw(link_list);
91              
92             To export all functions do:
93              
94             use HTML::LinkList ':all';
95              
96             =cut
97              
98 5     5   4849 use Data::Dumper;
  5         50764  
  5         25524  
99             require Exporter;
100              
101             our @ISA = qw(Exporter);
102              
103              
104             # Items which are exportable.
105             #
106             # This allows declaration use HTML::LinkList ':all';
107             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
108             # will save memory.
109             our %EXPORT_TAGS = ( 'all' => [ qw(
110             link_list
111             link_tree
112             full_tree
113             breadcrumb_trail
114             nav_tree
115             ) ] );
116              
117             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
118              
119             # Items to export into callers namespace by default. Note: do not export
120             # names by default without a very good reason. Use EXPORT_OK instead.
121             # Do not simply export all your public functions/methods/constants.
122              
123             our @EXPORT = qw(
124            
125             );
126              
127             =head2 link_list
128              
129             $links = link_list(
130             current_url=>$url,
131             urls=>\@links_in_order,
132             labels=>\%labels,
133             descriptions=>\%desc,
134             pre_desc=>' ',
135             post_desc=>'',
136             links_head=>'
    ',
137             links_foot=>'',
138             pre_item=>'
  • ',
  • 139             post_item=>''
    140             pre_active_item=>'',
    141             post_active_item=>'',
    142             item_sep=>"\n");
    143              
    144             Generates a simple list of links, from list of urls
    145             (and optional labels) taking into account of the "current" URL.
    146              
    147             This provides a large number of options to customize the appearance
    148             of the list. The default setup is for a simple UL list, but setting
    149             the options can enable you to make it something other than a list
    150             altogether, or add in CSS styles or classes to make it look just
    151             like you want.
    152              
    153             Required:
    154              
    155             =over
    156              
    157             =item urls
    158              
    159             The urls in the order you want them displayed. If this list
    160             is empty, then nothing will be generated.
    161              
    162             =back
    163              
    164             Options:
    165              
    166             =over
    167              
    168             =item current_url
    169              
    170             The link to the current page. If one of the links equals this,
    171             then that is deemed to be the "active" link and is just displayed
    172             as a label rather than a link.
    173              
    174             =item descriptions
    175              
    176             Optional hash of descriptions, to put next to the links. The keys
    177             of this hash are the urls.
    178              
    179             =item hide_ext
    180              
    181             If a site is hiding link extensions (such as using MultiViews with
    182             Apache) you may wish to hide the extensions (while using the full URLs
    183             to check various things). (default: 0 (false))
    184              
    185             =item item_sep
    186              
    187             String to put between items.
    188              
    189             =item labels
    190              
    191             A hash whose keys are links and whose values are labels.
    192             These are the labels for the links; if no label
    193             is given, then the last part of the link is used
    194             for the label, with some formatting.
    195              
    196             =item links_head
    197              
    198             String to begin the list with.
    199              
    200             =item links_foot
    201              
    202             String to end the list with.
    203              
    204             =item pre_desc
    205              
    206             String to prepend to each description.
    207              
    208             =item post_desc
    209              
    210             String to append to each description.
    211              
    212             =item pre_item
    213              
    214             String to prepend to each item.
    215              
    216             =item post_item
    217              
    218             String to append to each item.
    219              
    220             =item pre_active_item
    221              
    222             An additional string to put in front of each "active" item, after pre_item.
    223             The "active" item is the link which matches 'current_url'.
    224              
    225             =item pre_item_active
    226              
    227             INSTEAD of the "pre_item" string, use this string for active items
    228              
    229             =item post_active_item
    230              
    231             An additional string to append to each active item, before post_item.
    232              
    233             =item prefix_url
    234              
    235             A prefix to prepend to all the links. (default: empty string)
    236              
    237             =back
    238              
    239             =cut
    240             sub link_list {
    241 3     3 1 2153 my %args = (
    242             current_url=>'',
    243             prefix_url=>'',
    244             labels=>undef,
    245             urls=>undef,
    246             links_head=>'
      ',
    247             links_foot=>"\n",
    248             pre_item=>'
  • ',
  • 249             post_item=>'',
    250             pre_active_item=>'',
    251             post_active_item=>'',
    252             pre_current_parent=>'',
    253             post_current_parent=>'',
    254             item_sep=>"\n",
    255             hide_ext=>0,
    256             @_
    257             );
    258              
    259 3         5 my @link_order = @{$args{urls}};
      3         11  
    260 3 50 33     27 if (!defined $args{urls}
    261 3         10 or !@{$args{urls}})
    262             {
    263 0         0 return '';
    264             }
    265             my %format = (exists $args{format}
    266 3 50       73 ? %{$args{format}}
      0         0  
    267             : make_default_format(%args));
    268             # correct the current_url
    269 3         14 $args{current_url} = make_canonical($args{current_url});
    270 3         14 my %current_parents = extract_current_parents(%args);
    271 3         8 my @items = ();
    272 3         6 foreach my $link (@link_order)
    273             {
    274             my $label = (exists $args{labels}->{$link}
    275 15 100       36 ? $args{labels}->{$link} : '');
    276 15         65 my $item = make_item(%args,
    277             format=>\%format,
    278             current_parents=>\%current_parents,
    279             this_link=>$link,
    280             this_label=>$label);
    281 15         45 push @items, $item;
    282             }
    283 3         9 my $list = join($format{item_sep}, @items);
    284             return ($list
    285             ? join('', $args{links_head}, $list, $args{links_foot})
    286 3 50       29 : '');
    287             } # link_list
    288              
    289             =head2 link_tree
    290              
    291             $links = link_tree(
    292             current_url=>$url,
    293             link_tree=>\@list_of_lists,
    294             labels=>\%labels,
    295             descriptions=>\%desc,
    296             pre_desc=>' ',
    297             post_desc=>'',
    298             links_head=>'
      ',
    299             links_foot=>'',
    300             subtree_head=>'
      ',
    301             subtree_foot=>'',
    302             pre_item=>'
  • ',
  • 303             post_item=>''
    304             pre_active_item=>'',
    305             post_active_item=>'',
    306             item_sep=>"\n",
    307             tree_sep=>"\n",
    308             formats=>\%formats);
    309              
    310             Generates nested lists of links from a list of lists of links.
    311             This is useful for things such as table-of-contents or
    312             site maps.
    313              
    314             By default, this will return UL lists, but this is highly
    315             configurable.
    316              
    317             Required:
    318              
    319             =over
    320              
    321             =item link_tree
    322              
    323             A list of lists of urls, in the order you want them displayed.
    324             If a url is not in this list, it will not be displayed.
    325              
    326             =back
    327              
    328             Options:
    329              
    330             =over
    331              
    332             =item current_url
    333              
    334             The link to the current page. If one of the links equals this,
    335             then that is deemed to be the "active" link and is just displayed
    336             as a label rather than a link.
    337              
    338             =item descriptions
    339              
    340             Optional hash of descriptions, to put next to the links. The keys
    341             of this hash are the urls.
    342              
    343             =item exclude_root_parent
    344              
    345             If this is true, then the "current_parent" display options are
    346             not used for the "root" ("/") path, it isn't counted as a "parent"
    347             of the current_url.
    348              
    349             =item formats
    350              
    351             A reference to a hash containing advanced format settings. For example:
    352              
    353             my %formats = (
    354             # level 1 and onwards
    355             '1' => {
    356             tree_head=>"
      ",
    357             tree_foot=>"\n",
    358             },
    359             # level 2 and onwards
    360             '2' => {
    361             tree_head=>"
      ",
    362             tree_foot=>"\n",
    363             },
    364             # level 3 and onwards
    365             '3' => {
    366             pre_item=>'(',
    367             post_item=>')',
    368             item_sep=>",\n",
    369             tree_sep=>' -> ',
    370             tree_head=>"
    \n",
    371             tree_foot=>"",
    372             }
    373             );
    374              
    375             The formats hash enables you to control the formatting on a per-level basis.
    376             Each key of the hash corresponds to a level-number; the sub-hashes contain
    377             format arguments which will apply from that level onwards. If an argument
    378             isn't given in the sub-hash, then it will fall back to the previous level
    379             (or to the default, if there is no setting for that format-argument
    380             for a previous level).
    381              
    382             The only difference between the names of the arguments in the sub-hash and
    383             in the global format arguments is that instead of 'subtree_head' and subtree_foot'
    384             it uses 'tree_head' and 'tree_foot'.
    385              
    386             =item hide_ext
    387              
    388             If a site is hiding link extensions (such as using MultiViews with
    389             Apache) you may wish to hide the extensions (while using the full URLs
    390             to check various things). (default: 0 (false))
    391              
    392             =item item_sep
    393              
    394             The string to separate each item.
    395              
    396             =item labels
    397              
    398             A hash whose keys are links and whose values are labels.
    399             These are the labels for the links; if no label
    400             is given, then the last part of the link is used
    401             for the label, with some formatting.
    402              
    403             =item links_head
    404              
    405             The string to prepend the top-level tree with.
    406             (default:
      )
    407              
    408             =item links_foot
    409              
    410             The string to append to the top-level tree.
    411             (default: )
    412              
    413             =item pre_desc
    414              
    415             String to prepend to each description.
    416              
    417             =item post_desc
    418              
    419             String to append to each description.
    420              
    421             =item pre_item
    422              
    423             String to prepend to each item.
    424             (default:
  • )
  • 425              
    426             =item post_item
    427              
    428             String to append to each item.
    429             (default: )
    430              
    431             =item pre_active_item
    432              
    433             An additional string to put in front of each "active" item, after pre_item.
    434             The "active" item is the link which matches 'current_url'.
    435             (default: )
    436              
    437             =item pre_item_active
    438              
    439             INSTEAD of the "pre_item" string, use this string for active items
    440              
    441             =item post_active_item
    442              
    443             An additional string to append to each active item, before post_item.
    444             (default: )
    445              
    446             =item pre_current_parent
    447              
    448             An additional string to put in front of a link which is a parent
    449             of the 'current_url' link, after pre_item.
    450              
    451             =item pre_item_current_parent
    452              
    453             INSTEAD of the "pre_item" string, use this for links which are parents
    454             of the 'current_url' link.
    455              
    456             =item post_current_parent
    457              
    458             An additional string to append to a link which is a parent
    459             of the 'current_url' link, before post_item.
    460              
    461             =item prefix_url
    462              
    463             A prefix to prepend to all the links. (default: empty string)
    464              
    465             =item subtree_head
    466              
    467             The string to prepend to lower-level trees.
    468             (default:
      )
    469              
    470             =item subtree_foot
    471              
    472             The string to append to lower-level trees.
    473             (default: )
    474              
    475             =item tree_sep
    476              
    477             The string to separate each tree.
    478              
    479             =back
    480              
    481             =cut
    482             sub link_tree {
    483 3     3 1 1484 my %args = (
    484             current_url=>'',
    485             prefix_url=>'',
    486             link_tree=>undef,
    487             links_head=>'
      ',
    488             links_foot=>"\n",
    489             subtree_head=>'
      ',
    490             subtree_foot=>"\n",
    491             last_subtree_head=>'
      ',
    492             last_subtree_foot=>"\n",
    493             pre_item=>'
  • ',
  • 494             post_item=>'',
    495             pre_active_item=>'',
    496             post_active_item=>'',
    497             pre_current_parent=>'',
    498             post_current_parent=>'',
    499             item_sep=>"\n",
    500             tree_sep=>"\n",
    501             @_
    502             );
    503              
    504             # correct the current_url
    505 3         11 $args{current_url} = make_canonical($args{current_url});
    506 3         17 my %current_parents = extract_current_parents(%args);
    507              
    508 3         8 $args{tree_depth} = 0;
    509 3         6 $args{end_depth} = 0;
    510              
    511 3 50 33     11 if (defined $args{link_tree}
    512 3         14 and @{$args{link_tree}})
    513             {
    514 3         18 my %default_format = make_default_format(%args);
    515 3         21 my %formats = make_extra_formats(%args);
    516 3         8 my @link_tree = @{$args{link_tree}};
      3         8  
    517 3         17 my $list = traverse_lol(\@link_tree,
    518             %args,
    519             formats=>\%formats,
    520             current_format=>\%default_format,
    521             current_parents=>\%current_parents);
    522 3 50       27 return $list if $list;
    523             }
    524 0         0 return '';
    525             } # link_tree
    526              
    527             =head2 full_tree
    528              
    529             $links = full_tree(
    530             paths=>\@list_of_paths,
    531             labels=>\%labels,
    532             descriptions=>\%desc,
    533             hide=>$hide_regex,
    534             nohide=>$nohide_regex,
    535             start_depth=>0,
    536             end_depth=>0,
    537             top_level=>0,
    538             preserve_order=>0,
    539             preserve_paths=>0,
    540             ...
    541             );
    542              
    543             Given a set of paths this will generate a tree of links in the style of
    544             I. This will figure out all the intermediate paths and construct
    545             the nested structure for you, clustering parents and children together.
    546              
    547             The formatting options are as for L.
    548              
    549             Required:
    550              
    551             =over
    552              
    553             =item paths
    554              
    555             A reference to a list of paths: that is, URLs relative to the top
    556             of the site.
    557              
    558             For example, if the full URL is http://www.example.com/foo.html
    559             then the path is /foo.html
    560              
    561             If the full URL is http://www.example.com/~frednurk/foo.html
    562             then the path is /foo.html
    563              
    564             This does not require that every possible path be given; all the intermediate
    565             paths will be figured out from the list.
    566              
    567             =back
    568              
    569             Options:
    570              
    571             =over
    572              
    573             =item append_list
    574              
    575             Array of paths to append to the top-level links. They are used
    576             as-is, and are not part of the processing done to the "paths" list
    577             of paths. (see L)
    578              
    579             =item descriptions
    580              
    581             Optional hash of descriptions, to put next to the links. The keys
    582             of this hash are the paths.
    583              
    584             =item end_depth
    585              
    586             End your tree at this depth. If zero, then go all the way.
    587             (see L)
    588              
    589             =item exclude_root_parent
    590              
    591             If this is true, then the "current_parent" display options are
    592             not used for the "root" ("/") path, it isn't counted as a "parent"
    593             of the current_url.
    594              
    595             =item hide
    596              
    597             If the path matches this string, don't include it in the tree.
    598              
    599             =item hide_ext
    600              
    601             If a site is hiding link extensions (such as using MultiViews with
    602             Apache) you may wish to hide the extensions (while using the full URLs
    603             to check various things). (default: 0 (false))
    604              
    605             =item labels
    606              
    607             Hash containing replacement labels for one or more paths.
    608             If no label is given for '/' (the root path) then 'Home' will
    609             be used.
    610              
    611             =item last_subtree_head
    612              
    613             The string to prepend to the last lower-level tree.
    614             Only used if end_depth is not zero.
    615              
    616             =item last_subtree_foot
    617              
    618             The string to append to the last lower-level tree.
    619             Only used if end_depth is not zero.
    620              
    621             =item nohide
    622              
    623             If the path matches this string, it will be included even if it matches
    624             the 'hide' string.
    625              
    626             =item prefix_url
    627              
    628             A prefix to prepend to all the links. (default: empty string)
    629              
    630             =item prepend_list
    631              
    632             Array of paths to prepend to the top-level links. They are used
    633             as-is, and are not part of the processing done to the "paths" list
    634             of paths.
    635              
    636             =item preserve_order
    637              
    638             Preserve the ordering of the paths in the input list of paths;
    639             otherwise the links will be sorted alphabetically. Note that if
    640             preserve_order is true, the structure is at the whims of the order
    641             of the original list of paths, and so could end up odd-looking.
    642             (default: false)
    643              
    644             =item preserve_paths
    645              
    646             Do not extract intermediate paths or reorder the input list of paths.
    647             This speeds things up, but assumes that the input paths are complete
    648             and in good order.
    649             (default: false)
    650              
    651             =item start_depth
    652              
    653             Start your tree at this depth. Zero is the root, level 1 is the
    654             files/sub-folders in the root, and so on.
    655             (default: 0)
    656              
    657             =item top_level
    658              
    659             Decide which level is the "top" level. Useful when you
    660             set the start_depth to something greater than 1.
    661              
    662             =back
    663              
    664             =cut
    665             sub full_tree {
    666 6     6 1 5046 my %args = (
    667             paths=>undef,
    668             current_url=>'',
    669             links_head=>'
      ',
    670             links_foot=>"\n",
    671             subtree_head=>'
      ',
    672             subtree_foot=>"\n",
    673             last_subtree_head=>'
      ',
    674             last_subtree_foot=>"\n",
    675             pre_item=>'
  • ',
  • 676             post_item=>'',
    677             pre_active_item=>'',
    678             post_active_item=>'',
    679             pre_current_parent=>'',
    680             post_current_parent=>'',
    681             item_sep=>"\n",
    682             tree_sep=>"\n",
    683             hide=>'',
    684             nohide=>'',
    685             preserve_order=>0,
    686             preserve_paths=>0,
    687             labels=>{},
    688             start_depth=>0,
    689             end_depth=>0,
    690             top_level=>0,
    691             @_
    692             );
    693              
    694             # correct the current_url
    695 6         21 $args{current_url} = make_canonical($args{current_url});
    696 6         39 my %current_parents = extract_current_parents(%args);
    697              
    698             # set the root label
    699 6 100       29 if (!$args{labels}->{'/'})
    700             {
    701 2         4 $args{labels}->{'/'} = 'Home';
    702             }
    703 6         13 my @path_list = ();
    704 6 50       15 if ($args{preserve_paths})
    705             {
    706 0         0 @path_list = filter_out_paths(%args, paths=>$args{paths});
    707             }
    708             else
    709             {
    710             @path_list = extract_all_paths(paths=>$args{paths},
    711 6         14 preserve_order=>$args{preserve_order});
    712 6         46 @path_list = filter_out_paths(%args, paths=>\@path_list);
    713             }
    714 6         51 my @list_of_lists = build_lol(%args, paths=>\@path_list,
    715             depth=>0);
    716 6         20 $args{tree_depth} = 0;
    717 6         13 $args{end_depth} = 0;
    718              
    719 6         30 my %default_format = make_default_format(%args);
    720 6         44 my %formats = make_extra_formats(%args);
    721 6         45 my $list = traverse_lol(\@list_of_lists,
    722             %args,
    723             formats=>\%formats,
    724             current_format=>\%default_format,
    725             current_parents=>\%current_parents);
    726 6 50       68 return $list if $list;
    727              
    728 0         0 return '';
    729             } # full_tree
    730              
    731             =head2 breadcrumb_trail
    732              
    733             $links = breadcrumb_trail(
    734             current_url=>$url,
    735             labels=>\%labels,
    736             descriptions=>\%desc,
    737             links_head=>'

    ',

    738             links_foot=>"\n

    ",
    739             subtree_head=>'',
    740             subtree_foot=>"\n",
    741             pre_item=>'',
    742             post_item=>'',
    743             pre_active_item=>'',
    744             post_active_item=>'',
    745             item_sep=>"\n",
    746             tree_sep=>' > ',
    747             ...
    748             );
    749              
    750             Given the current url, make a breadcrumb trail from it.
    751             By default, this is laid out with '>' separators, but it can
    752             be set up to give a nested set of UL lists (as for L).
    753              
    754             The formatting options are as for L.
    755              
    756             Required:
    757              
    758             =over
    759              
    760             =item current_url
    761              
    762             The current url to be made into a breadcrumb-trail.
    763              
    764             =back
    765              
    766             Options:
    767              
    768             =over
    769              
    770             =item descriptions
    771              
    772             Optional hash of descriptions, to put next to the links. The keys
    773             of this hash are the urls.
    774              
    775             =item exclude_root_parent
    776              
    777             If this is true, then the "current_parent" display options are
    778             not used for the "root" ("/") path, it isn't counted as a "parent"
    779             of the current_url.
    780              
    781             =item hide_ext
    782              
    783             If a site is hiding link extensions (such as using MultiViews with
    784             Apache) you may wish to hide the extensions (while using the full URLs
    785             to check various things). (default: 0 (false))
    786              
    787             =item labels
    788              
    789             Hash containing replacement labels for one or more URLS.
    790             If no label is given for '/' (the root path) then 'Home' will
    791             be used.
    792              
    793             =back
    794              
    795             =cut
    796             sub breadcrumb_trail {
    797 2     2 1 750 my %args = (
    798             current_url=>'',
    799             links_head=>'

    ',

    800             links_foot=>"\n

    ",
    801             subtree_head=>'',
    802             subtree_foot=>'',
    803             last_subtree_head=>'{',
    804             last_subtree_foot=>'}',
    805             pre_item=>'',
    806             post_item=>'',
    807             pre_active_item=>'',
    808             post_active_item=>'',
    809             pre_current_parent=>'',
    810             post_current_parent=>'',
    811             item_sep=>"\n",
    812             tree_sep=>' > ',
    813             hide=>'',
    814             nohide=>'',
    815             labels=>{},
    816             paths=>[],
    817             start_depth=>0,
    818             end_depth=>undef,
    819             top_level=>0,
    820             @_
    821             );
    822              
    823             # correct the current_url
    824 2         8 $args{current_url} = make_canonical($args{current_url});
    825              
    826             # set the root label
    827 2 100       8 if (!$args{labels}->{'/'})
    828             {
    829 1         3 $args{labels}->{'/'} = 'Home';
    830             }
    831              
    832             # make a list of paths consisting only of the current_url
    833 2         4 my @paths = ($args{current_url});
    834 2         6 my @path_list = extract_all_paths(paths=>\@paths);
    835 2         12 @path_list = filter_out_paths(%args, paths=>\@path_list);
    836 2         15 my @list_of_lists = build_lol(%args, paths=>\@path_list,
    837             depth=>0);
    838 2         6 $args{tree_depth} = 0;
    839 2         3 $args{end_depth} = 0;
    840              
    841 2         12 my %default_format = make_default_format(%args);
    842 2         19 my %formats = make_extra_formats(%args);
    843 2         14 my $list = traverse_lol(\@list_of_lists,
    844             %args,
    845             formats=>\%formats,
    846             current_format=>\%default_format,
    847             );
    848 2 50       23 return $list if $list;
    849              
    850 0         0 return '';
    851             } # breadcrumb_trail
    852              
    853             =head2 nav_tree
    854              
    855             $links = nav_tree(
    856             paths=>\@list_of_paths,
    857             labels=>\%labels,
    858             current_url=>$url,
    859             hide=>$hide_regex,
    860             nohide=>$nohide_regex,
    861             preserve_order=>1,
    862             descriptions=>\%desc,
    863             ...
    864             );
    865              
    866             This takes a list of links, and the current URL, and makes a nested navigation
    867             tree, consisting of (a) the top-level links (b) the links leading to the
    868             current URL (c) the links on the same level as the current URL,
    869             (d) the related links just above this level, depending on whether
    870             this is an index-page or a content page.
    871              
    872             Optionally one can hide links which match match the 'hide' option.
    873              
    874             The formatting options are as for L, with some additions.
    875              
    876             Required:
    877              
    878             =over
    879              
    880             =item current_url
    881              
    882             The link to the current page. If one of the links equals this, then that
    883             is deemed to be the "active" link and is just displayed as a label rather
    884             than a link. This is also used to determine which links to show and which
    885             ones to filter out.
    886              
    887             =item paths
    888              
    889             A reference to a list of paths: that is, URLs relative to the top
    890             of the site.
    891              
    892             For example, if the full URL is http://www.example.com/foo.html
    893             then the path is /foo.html
    894              
    895             This does not require that every possible path be given; all the intermediate
    896             paths will be figured out from the list.
    897              
    898             =back
    899              
    900             Options:
    901              
    902             =over
    903              
    904             =item append_list
    905              
    906             Array of paths to append to the top-level links. They are used
    907             as-is, and are not part of the processing done to the "paths" list
    908             of paths. (see L)
    909              
    910             =item descriptions
    911              
    912             Optional hash of descriptions, to put next to the links. The keys
    913             of this hash are the paths.
    914              
    915             =item end_depth
    916              
    917             End your tree at this depth. If zero, then go all the way.
    918             By default this is set to the depth of the current_url.
    919              
    920             =item exclude_root_parent
    921              
    922             If this is true, then the "current_parent" display options are
    923             not used for the "root" ("/") path, it isn't counted as a "parent"
    924             of the current_url.
    925              
    926             =item hide
    927              
    928             If a path matches this string, don't include it in the tree.
    929              
    930             =item hide_ext
    931              
    932             If a site is hiding link extensions (such as using MultiViews with
    933             Apache) you may wish to hide the extensions (while using the full URLs
    934             to check various things). (default: 0 (false))
    935              
    936             =item labels
    937              
    938             Hash containing replacement labels for one or more paths.
    939             If no label is given for '/' (the root path) then 'Home' will
    940             be used.
    941              
    942             =item last_subtree_head
    943              
    944             The string to prepend to the last lower-level tree.
    945              
    946             =item last_subtree_foot
    947              
    948             The string to append to the last lower-level tree.
    949              
    950             =item nohide
    951              
    952             If the path matches this string, it will be included even if it matches
    953             the 'hide' string.
    954              
    955             =item prefix_url
    956              
    957             A prefix to prepend to all the links. (default: empty string)
    958              
    959             =item prepend_list
    960              
    961             Array of paths to prepend to the top-level links. They are used
    962             as-is, and are not part of the processing done to the "paths" list
    963             of paths.
    964              
    965             =item preserve_order
    966              
    967             Preserve the ordering of the paths in the input list of paths;
    968             otherwise the links will be sorted alphabetically.
    969             (default: true)
    970              
    971             =item preserve_paths
    972              
    973             Do not extract intermediate paths or reorder the input list of paths.
    974             This speeds things up, but assumes that the input paths are complete
    975             and in good order.
    976             (default: false)
    977              
    978             =item start_depth
    979              
    980             Start your tree at this depth. Zero is the root, level 1 is the
    981             files/sub-folders in the root, and so on.
    982             (default: 1)
    983              
    984             =item top_level
    985              
    986             Decide which level is the "top" level. Useful when you
    987             set the start_depth to something greater than 1.
    988              
    989             =back
    990              
    991             =cut
    992             sub nav_tree {
    993 13     13 1 14693 my %args = (
    994             paths=>undef,
    995             current_url=>'',
    996             links_head=>'
      ',
    997             links_foot=>"\n",
    998             subtree_head=>'
      ',
    999             subtree_foot=>"\n",
    1000             last_subtree_head=>'
      ',
    1001             last_subtree_foot=>"\n",
    1002             pre_item=>'
  • ',
  • 1003             post_item=>'',
    1004             pre_active_item=>'',
    1005             post_active_item=>'',
    1006             pre_current_parent=>'',
    1007             post_current_parent=>'',
    1008             item_sep=>"\n",
    1009             tree_sep=>"\n",
    1010             hide=>'',
    1011             nohide=>'',
    1012             preserve_order=>1,
    1013             preserve_paths=>0,
    1014             include_home=>0,
    1015             labels=>{},
    1016             start_depth=>1,
    1017             end_depth=>undef,
    1018             top_level=>1,
    1019             navbar_type=>'normal',
    1020             @_
    1021             );
    1022              
    1023             # correct the current_url
    1024 13         46 $args{current_url} = make_canonical($args{current_url});
    1025 13         43 my $current_is_index = ($args{current_url} =~ m!/$!o);
    1026 13         100 my %current_parents = extract_current_parents(%args);
    1027              
    1028             # set the end depth if is not already set
    1029             # if this is an index-page, then make the depth its depth + 1
    1030             # if this is a content-page, make the depth its depth
    1031 13         60 my $current_url_depth = path_depth($args{current_url});
    1032             $args{end_depth} = ($current_is_index
    1033             ? $current_url_depth + 1 : $current_url_depth)
    1034 13 100       55 if (!defined $args{end_depth});
        50          
    1035              
    1036             # set the root label
    1037 13 100       37 if (!$args{labels}->{'/'})
    1038             {
    1039 1         3 $args{labels}->{'/'} = 'Home';
    1040             }
    1041 13         23 my @path_list = ();
    1042 13 50       27 if ($args{preserve_paths})
    1043             {
    1044 0         0 @path_list = filter_out_paths(%args, paths=>$args{paths});
    1045             }
    1046             else
    1047             {
    1048             @path_list = extract_all_paths(paths=>$args{paths},
    1049 13         48 preserve_order=>$args{preserve_order});
    1050 13         118 @path_list = filter_out_paths(%args, paths=>\@path_list);
    1051             }
    1052 13         172 my @list_of_lists = build_lol(%args, paths=>\@path_list,
    1053             depth=>0);
    1054 13         50 $args{tree_depth} = 0;
    1055              
    1056 13         86 my %default_format = make_default_format(%args);
    1057 13         118 my %formats = make_extra_formats(%args);
    1058 13         112 my $list = traverse_lol(\@list_of_lists,
    1059             %args,
    1060             formats=>\%formats,
    1061             current_format=>\%default_format,
    1062             current_parents=>\%current_parents);
    1063 13 50       230 return $list if $list;
    1064              
    1065 0         0 return '';
    1066             } # nav_tree
    1067              
    1068             =head1 Private Functions
    1069              
    1070             These functions cannot be exported.
    1071              
    1072             =head2 make_item
    1073              
    1074             $item = make_item(
    1075             this_label=>$label,
    1076             this_link=>$link,
    1077             hide_ext=>0,
    1078             current_url=>$url,
    1079             current_parents=>\%current_parents,
    1080             descriptions=>\%desc,
    1081             format=>\%format,
    1082             );
    1083              
    1084             %format = (
    1085             pre_desc=>' ',
    1086             post_desc=>'',
    1087             pre_item=>'
  • ',
  • 1088             post_item=>''
    1089             pre_active_item=>'',
    1090             post_active_item=>'',
    1091             pre_current_parent=>'',
    1092             post_current_parent=>'',
    1093             item_sep=>"\n");
    1094             );
    1095              
    1096             Format a link item.
    1097              
    1098             See L for the formatting options.
    1099              
    1100             =over
    1101              
    1102             =item this_label
    1103              
    1104             The label of the required link. If there is no label,
    1105             this uses the base-name of the last part of the link,
    1106             capitalizing it and replacing underscores and dashes with spaces.
    1107              
    1108             =item this_link
    1109              
    1110             The URL of the required link.
    1111              
    1112             =item current_url
    1113              
    1114             The link to the current page. If one of the links equals this,
    1115             then that is deemed to be the "active" link and is just displayed
    1116             as a label rather than a link.
    1117              
    1118             =item current_parents
    1119              
    1120             URLs of the parents of the current item.
    1121              
    1122             =item descriptions
    1123              
    1124             Optional hash of descriptions, to put next to the links. The keys
    1125             of this hash are the links (not the labels).
    1126              
    1127             =item defer_post_item
    1128              
    1129             Don't add the 'post_item' string if this is true.
    1130             (needed for nested lists)
    1131             (default: false)
    1132              
    1133             =item no_link
    1134              
    1135             Don't make a link for this, just a label.
    1136              
    1137             =back
    1138              
    1139             =cut
    1140             sub make_item {
    1141 185     185 1 2232 my %args = (
    1142             this_link=>'',
    1143             this_label=>'',
    1144             hide_ext=>0,
    1145             current_url=>'',
    1146             current_parents=>{},
    1147             prefix_url=>'',
    1148             defer_post_item=>0,
    1149             no_link=>0,
    1150             @_
    1151             );
    1152 185         360 my $link = $args{this_link};
    1153 185         262 my $prefix_url = $args{prefix_url};
    1154 185         260 my $label = $args{this_label};
    1155 185         209 my %format = %{$args{format}};
      185         1163  
    1156              
    1157 185 100       578 if (!$label)
    1158             {
    1159 147 50       359 $label = $link if !$label;
    1160 147 100       791 if ($link =~ /([-\w]+)\.\w+$/o) # file
        50          
    1161             {
    1162 39         91 $label = $1;
    1163             }
    1164             elsif ($link =~ /([-\w]+)\/?$/o) # dir
    1165             {
    1166 108         292 $label = $1;
    1167             }
    1168             else # give up
    1169             {
    1170 0         0 $label = $link;
    1171 0         0 $label =~ s#/# :: #go;
    1172             }
    1173            
    1174             # prettify
    1175 147         254 $label =~ s#_# #go;
    1176 147         203 $label =~ s#-# #go;
    1177 147         787 $label =~ s/(\b[a-z][-\w]+)/\u\L$1/go;
    1178             }
    1179             # if we are hiding the extensions of files
    1180             # we need to display an extensionless link
    1181             # while doing checks with the original link
    1182 185         272 my $display_link = $link;
    1183 185 50       414 if ($args{hide_ext})
    1184             {
    1185 0 0       0 if ($link =~ /(.*)\.[-\w]+$/o) # file
    1186             {
    1187 0         0 $display_link = $1;
    1188             }
    1189             }
    1190 185         251 my $item = '';
    1191 185         221 my $desc = '';
    1192 185 0 33     590 if (exists $args{descriptions}->{$link}
          33        
    1193             and defined $args{descriptions}->{$link}
    1194             and $args{descriptions}->{$link})
    1195             {
    1196             $desc = join('', $format{pre_desc},
    1197             $args{descriptions}->{$link},
    1198 0         0 $format{post_desc});
    1199             }
    1200 185 100 66     437 if (link_is_active(this_link=>$link,
        50 66        
        100          
    1201             current_url=>$args{current_url}))
    1202             {
    1203             $item = join('', $format{pre_item_active},
    1204             $format{pre_active_item},
    1205             $label,
    1206             $format{post_active_item},
    1207 15         50 $desc,
    1208             );
    1209             }
    1210             elsif ($args{no_link})
    1211             {
    1212             $item = join('', $format{pre_item},
    1213 0         0 $label,
    1214             $desc);
    1215             }
    1216             elsif ($args{current_url}
    1217             and exists $args{current_parents}->{$link}
    1218             and $args{current_parents}->{$link})
    1219             {
    1220             $item = join('', $format{pre_item_current_parent},
    1221             $format{pre_current_parent},
    1222             '',
    1223             $label, '',
    1224             $format{post_current_parent},
    1225 10         39 $desc);
    1226             }
    1227             else
    1228             {
    1229             $item = join('', $format{pre_item},
    1230 160         459 '',
    1231             $label, '',
    1232             $desc);
    1233             }
    1234 185 100       441 if (!$args{defer_post_item})
    1235             {
    1236 15         34 $item = join('', $item, $format{post_item});
    1237             }
    1238 185         1426 return $item;
    1239             } # make_item
    1240              
    1241             =head2 make_canonical
    1242              
    1243             my $new_url = make_canonical($url);
    1244              
    1245             Make a URL canonical; remove the 'index.*' and add on a needed
    1246             '/' -- this assumes that directory names never have a '.' in them.
    1247              
    1248             =cut
    1249             sub make_canonical {
    1250 708     708 1 923 my $url = shift;
    1251              
    1252 708 100       1431 return $url if (!$url);
    1253 687 100       3632 if ($url =~ m{^/index\.\w+$}o)
        50          
        100          
    1254             {
    1255 1         2 $url = '/';
    1256             }
    1257             elsif ($url =~ m{^(.*/)index\.\w+$}o)
    1258             {
    1259 0         0 $url = $1;
    1260             }
    1261             elsif ($url =~ m{/[-\w]+$}o) # no dots; a directory
    1262             {
    1263 13         33 $url = join('', $url, '/'); # add the slash
    1264             }
    1265 687         1254 return $url;
    1266             } # make_canonical
    1267            
    1268             =head2 get_index_path
    1269              
    1270             my $new_url = get_index_path($url);
    1271              
    1272             Get the "index" part of this path. That is, if this path
    1273             is not for an index-page, then get the parent index-page
    1274             path for this path.
    1275             (Removes the trailing slash).
    1276              
    1277             =cut
    1278             sub get_index_path {
    1279 50     50 1 67 my $url = shift;
    1280              
    1281 50 50       101 return $url if (!$url);
    1282 50         86 $url = make_canonical($url);
    1283 50 100 66     351 if ($url =~ m{^(.*)/[-\w]+\.\w+$}o)
        100          
    1284             {
    1285 15         39 $url = $1;
    1286             }
    1287             elsif ($url ne '/' and $url =~ m{/$}o)
    1288             {
    1289 31         58 chop $url;
    1290             }
    1291 50         109 return $url;
    1292             } # get_index_path
    1293              
    1294             =head2 get_index_parent
    1295              
    1296             my $new_url = get_index_parent($url);
    1297              
    1298             Get the parent of the "index" part of this path.
    1299             (Removes the trailing slash).
    1300              
    1301             =cut
    1302             sub get_index_parent {
    1303 35     35 1 51 my $url = shift;
    1304              
    1305 35 100       80 return $url if (!$url);
    1306 30         67 $url = get_index_path($url);
    1307 30 100       115 if ($url =~ m#^(.*)/[-\w]+$#o)
    1308             {
    1309 27         66 $url = $1;
    1310             }
    1311 30         63 return $url;
    1312             } # get_index_parent
    1313            
    1314             =head2 path_depth
    1315              
    1316             my $depth = path_depth($url);
    1317              
    1318             Calculate the "depth" of the given path.
    1319              
    1320             =cut
    1321             sub path_depth {
    1322 541     541 1 720 my $url = shift;
    1323              
    1324 541 100       1098 return 0 if ($url eq '/'); # root is zero
    1325 512 100       1446 if ($url =~ m!/$!o) # remove trailing /
    1326             {
    1327 295         542 chop $url;
    1328             }
    1329 512         881 return scalar ($url =~ tr!/!/!);
    1330             } # path_depth
    1331            
    1332             =head2 link_is_active
    1333              
    1334             if (link_is_active(this_link=>$link, current_url=>$url))
    1335             ...
    1336              
    1337             Check if the given link is "active", that is, if it
    1338             matches the 'current_url'.
    1339              
    1340             =cut
    1341             sub link_is_active {
    1342 185     185 1 592 my %args = (
    1343             this_link=>'',
    1344             current_url=>'',
    1345             @_
    1346             );
    1347             # if there is no current link, is not active.
    1348 185 100       728 return 0 if (!$args{current_url});
    1349              
    1350 108         217 my $link = make_canonical($args{this_link});
    1351              
    1352 108 100       344 return 1 if ($link eq $args{current_url});
    1353 93         621 return 0;
    1354              
    1355             } # link_is_active
    1356              
    1357             =head2 traverse_lol
    1358              
    1359             $links = traverse_lol(\@list_of_lists,
    1360             labels=>\%labels,
    1361             tree_depth=>$depth
    1362             current_format=>\%format,
    1363             ...
    1364             );
    1365              
    1366             Traverse the list of lists (of urls) to produce
    1367             a nested collection of links.
    1368              
    1369             This consumes the list_of_lists!
    1370              
    1371             =cut
    1372             sub traverse_lol {
    1373 86     86 1 202 my $lol_ref = shift;
    1374 86         900 my %args = (
    1375             current_url=>'',
    1376             labels=>undef,
    1377             prefix_url=>'',
    1378             hide_ext=>0,
    1379             @_
    1380             );
    1381              
    1382 86         144 my $tree_depth = $args{tree_depth};
    1383             my %format = (
    1384 86         757 %{$args{current_format}},
    1385             (exists $args{formats}->{$tree_depth}
    1386 86 100       102 ? %{$args{formats}->{$tree_depth}}
      8         49  
    1387             : ())
    1388             );
    1389 86         209 my @items = ();
    1390 86         106 while (@{$lol_ref})
      258         633  
    1391             {
    1392 187         224 my $ll = shift @{$lol_ref};
      187         308  
    1393 187 100       408 if (!ref $ll) # an item
    1394             {
    1395 170         210 my $link = $ll;
    1396             my $label = (exists $args{labels}->{$link}
    1397 170 100       418 ? $args{labels}->{$link} : '');
    1398 170         1049 my $item = make_item(this_link=>$link,
    1399             this_label=>$label,
    1400             defer_post_item=>1,
    1401             %args,
    1402             format=>\%format);
    1403              
    1404 170 100       786 if (ref $lol_ref->[0]) # next one is a list
    1405             {
    1406 45         59 $ll = shift @{$lol_ref};
      45         88  
    1407 45         316 my $sublist = traverse_lol($ll, %args,
    1408             tree_depth=>$tree_depth + 1,
    1409             current_format=>\%format);
    1410 45         202 $item = join($format{tree_sep}, $item, $sublist);
    1411             }
    1412 170         404 $item = join('', $item, $format{post_item});
    1413 170         350 push @items, $item;
    1414             }
    1415             else # a reference to a list
    1416             {
    1417 17 100 66     93 if (defined $args{start_depth}
    1418             && $args{tree_depth} < $args{start_depth})
    1419             {
    1420 15         105 return traverse_lol($ll, %args, current_format=>\%format);
    1421             }
    1422             else
    1423             {
    1424 2         13 my $sublist = traverse_lol($ll, %args,
    1425             tree_depth=>$tree_depth + 1,
    1426             current_format=>\%format);
    1427 2         8 my $item = join($format{tree_sep}, $format{pre_item}, $sublist);
    1428 2         6 $item = join('', $item, $format{post_item});
    1429 2         4 push @items, $item;
    1430             }
    1431             }
    1432             }
    1433 71         202 my $list = join($format{item_sep}, @items);
    1434             return join('',
    1435             (($args{end_depth} && $tree_depth == $args{end_depth} )
    1436             ? $args{last_subtree_head}
    1437             : $format{tree_head}),
    1438             $list,
    1439             (($args{end_depth} && $tree_depth == $args{end_depth} )
    1440             ? $args{last_subtree_foot}
    1441             : $format{tree_foot})
    1442 71 50 66     1025 );
        50 66        
    1443             } # traverse_lol
    1444              
    1445             =head2 extract_all_paths
    1446              
    1447             my @all_paths = extract_all_paths(paths=>\@paths,
    1448             preserve_order=>0);
    1449              
    1450             Extract all possible paths out of a list of paths.
    1451             Thus, if one has
    1452              
    1453             /foo/bar/baz.html
    1454              
    1455             then that would make
    1456              
    1457             /
    1458             /foo/
    1459             /foo/bar/
    1460             /foo/bar/baz.html
    1461              
    1462             If 'preserve_order' is true, this preserves the ordering of
    1463             the paths in the input list; otherwise the output paths
    1464             are sorted alphabetically.
    1465              
    1466             =cut
    1467             sub extract_all_paths {
    1468 21     21 1 77 my %args = (
    1469             paths=>undef,
    1470             preserve_order=>0,
    1471             @_
    1472             );
    1473            
    1474 21         37 my %paths = ();
    1475             # keep track of the order of the paths in the list of paths
    1476 21         34 my $order = 1;
    1477 21         29 foreach my $path (@{$args{paths}})
      21         61  
    1478             {
    1479 186         477 my @path_split = split('/', $path);
    1480             # first path as-is
    1481 186         428 $paths{$path} = $order;
    1482 186         213 pop @path_split;
    1483 186         424 while (@path_split)
    1484             {
    1485             # these paths are index-pages. should end in '/'
    1486 383         637 my $newpath = join('/', @path_split, '');
    1487             # give this path the same order-num as the full path
    1488             # but only if it hasn't already been added
    1489 383 100       893 $paths{$newpath} = $order if (!exists $paths{$newpath});
    1490 383         945 pop @path_split;
    1491             }
    1492 186 100       538 $order++ if ($args{preserve_order});
    1493             }
    1494             return sort {
    1495 21 100       154 return $a cmp $b if ($paths{$a} == $paths{$b});
      687         1486  
    1496 525         852 return $paths{$a} <=> $paths{$b};
    1497             } keys %paths;
    1498             } # extract_all_paths
    1499              
    1500             =head2 extract_current_parents
    1501              
    1502             my %current_parents = extract_current_parents(current_url=>$url,
    1503             exclude_root_parent=>0);
    1504              
    1505             Extract the "parent" paths of the current url
    1506              
    1507             /foo/bar/baz.html
    1508              
    1509             then that would make
    1510              
    1511             /
    1512             /foo/
    1513             /foo/bar/
    1514              
    1515             If 'exclude_root_parent' is true, then the '/' is excluded from the
    1516             list of parents.
    1517              
    1518             =cut
    1519             sub extract_current_parents {
    1520 25     25 1 222 my %args = (
    1521             current_url=>undef,
    1522             exclude_root_parent=>0,
    1523             @_
    1524             );
    1525            
    1526 25         48 my %paths = ();
    1527 25 100       87 if ($args{current_url})
    1528             {
    1529 14         26 my $current_url = $args{current_url};
    1530 14         55 my @path_split = split('/', $current_url);
    1531 14         20 pop @path_split; # remove the current url
    1532 14         40 while (@path_split)
    1533             {
    1534             # these paths are index-pages. should end in '/'
    1535 25         59 my $newpath = join('/', @path_split, '');
    1536 25         48 $paths{$newpath} = 1;
    1537 25         70 pop @path_split;
    1538             }
    1539 14 100       55 if ($args{exclude_root_parent})
    1540             {
    1541 2         5 delete $paths{"/"};
    1542             }
    1543             }
    1544              
    1545 25         146 return %paths;
    1546             } # extract_current_parents
    1547              
    1548             =head2 build_lol
    1549              
    1550             my @lol = build_lol(
    1551             paths=>\@paths,
    1552             current_url=>$url,
    1553             navbar_type=>'',
    1554             );
    1555              
    1556             Build a list of lists of paths, given a simple list of paths.
    1557             Assumes that this list has already been filtered.
    1558              
    1559             =over
    1560              
    1561             =item paths
    1562              
    1563             Reference to list of paths; this is consumed.
    1564              
    1565             =back
    1566              
    1567             =cut
    1568             sub build_lol {
    1569 78     78 1 969 my %args = (
    1570             paths=>undef,
    1571             depth=>0,
    1572             start_depth=>0,
    1573             end_depth=>0,
    1574             current_url=>'',
    1575             navbar_type=>'',
    1576             prepend_list=>undef,
    1577             append_list=>undef,
    1578             @_
    1579             );
    1580 78         130 my $paths_ref = $args{paths};
    1581 78         108 my $depth = $args{depth};
    1582              
    1583 78         116 my @list_of_lists = ();
    1584 78         96 while (@{$paths_ref})
      289         739  
    1585             {
    1586 238         364 my $path = $paths_ref->[0];
    1587 238         398 my $can_path = make_canonical($path);
    1588 238         452 my $path_depth = path_depth($can_path);
    1589 238         548 my $path_is_index = ($can_path =~ m#/$#o);
    1590 238 100       511 if ($path_depth == $depth)
        100          
        50          
    1591             {
    1592 154         173 shift @{$paths_ref}; # use this path
      154         274  
    1593 154         314 push @list_of_lists, $path;
    1594             }
    1595             elsif ($path_depth > $depth)
    1596             {
    1597             push @list_of_lists, [build_lol(
    1598             %args,
    1599             prepend_list=>undef,
    1600             append_list=>undef,
    1601             paths=>$paths_ref,
    1602             depth=>$path_depth,
    1603             navbar_type=>$args{navbar_type},
    1604             current_url=>$args{current_url},
    1605 57         441 )];
    1606             }
    1607             elsif ($path_depth < $depth)
    1608             {
    1609 27         247 return @list_of_lists;
    1610             }
    1611             }
    1612             # prepend the given list to the top level
    1613 51 100 66     151 if (defined $args{prepend_list} and @{$args{prepend_list}})
      3         14  
    1614             {
    1615             # if the list of lists is a single item which is a list
    1616             # then add the extra list to that item
    1617 3 50 33     21 if ($#list_of_lists == 0
    1618             and ref($list_of_lists[0]) eq "ARRAY")
    1619             {
    1620 3         5 unshift @{$list_of_lists[0]}, @{$args{prepend_list}};
      3         6  
      3         10  
    1621             }
    1622             else
    1623             {
    1624 0         0 unshift @list_of_lists, @{$args{prepend_list}};
      0         0  
    1625             }
    1626             }
    1627             # append the given list to the top level
    1628 51 50 33     132 if (defined $args{append_list} and @{$args{append_list}})
      0         0  
    1629             {
    1630             # if the list of lists is a single item which is a list
    1631             # then add the extra list to that item
    1632 0 0 0     0 if ($#list_of_lists == 0
    1633             and ref($list_of_lists[0]) eq "ARRAY")
    1634             {
    1635 0         0 push @{$list_of_lists[0]}, @{$args{append_list}};
      0         0  
      0         0  
    1636             }
    1637             else
    1638             {
    1639 0         0 push @list_of_lists, @{$args{append_list}};
      0         0  
    1640             }
    1641             }
    1642 51         381 return @list_of_lists;
    1643             } # build_lol
    1644              
    1645             =head2 filter_out_paths
    1646              
    1647             my @filtered_paths = filter_out_paths(
    1648             paths=>\@paths,
    1649             current_url=>$url,
    1650             hide=>$hide,
    1651             nohide=>$nohide,
    1652             start_depth=>$start_depth,
    1653             end_depth=>$end_depth,
    1654             top_level=>$top_level,
    1655             navbar_type=>'',
    1656             );
    1657              
    1658             Filter out the paths we don't want from our list of paths.
    1659             Returns a list of the paths we want.
    1660              
    1661             =cut
    1662             sub filter_out_paths {
    1663 21     21 1 279 my %args = (
    1664             paths=>undef,
    1665             start_depth=>0,
    1666             end_depth=>0,
    1667             top_level=>0,
    1668             current_url=>'',
    1669             navbar_type=>'',
    1670             hide=>'',
    1671             nohide=>'',
    1672             @_
    1673             );
    1674 21         43 my $paths_ref = $args{paths};
    1675 21         35 my $hide = $args{hide};
    1676 21         33 my $nohide = $args{nohide};
    1677              
    1678 21         33 my %canon_paths = ();
    1679 21         31 my @wantedpaths1 = ();
    1680 21         35 my %path_depth = ();
    1681              
    1682             # filter out common things
    1683             # remember canonical paths and path depths
    1684 21         45 foreach my $path (@{$paths_ref})
      21         44  
    1685             {
    1686 260         481 my $can_path = make_canonical($path);
    1687 260         485 my $path_depth = path_depth($can_path);
    1688 260 50 33     2164 if ($hide and $nohide
        50 33        
        100 0        
        100 33        
          33        
          100        
    1689             and not($path =~ /$nohide/)
    1690             and $path =~ /$hide/)
    1691             {
    1692             # skip this one
    1693             }
    1694             elsif ($hide and !$nohide and $path =~ /$hide/)
    1695             {
    1696             # skip this one
    1697             }
    1698             elsif ($path_depth < $args{start_depth})
    1699             {
    1700             # skip this one
    1701             }
    1702             elsif ($args{end_depth}
    1703             and $path_depth > $args{end_depth})
    1704             {
    1705             # skip this one
    1706             }
    1707             else
    1708             {
    1709 213         318 $path_depth{$path} = $path_depth;
    1710 213         317 $canon_paths{$path} = $can_path;
    1711 213         445 push @wantedpaths1, $path;
    1712             }
    1713             }
    1714              
    1715 21         45 my @wantedpaths = ();
    1716 21 100       44 if ($args{current_url})
    1717             {
    1718 15         27 my $current_url = $args{current_url};
    1719 15         33 my $current_url_depth = path_depth($args{current_url});
    1720 15         50 my $current_url_is_index = ($args{current_url} =~ m{/$}o);
    1721              
    1722             my $parent = make_canonical($current_url_is_index
    1723             ? get_index_parent($args{current_url})
    1724             : get_index_path($args{current_url})
    1725 15 100       64 );
    1726 15         40 my $parent_depth = path_depth($parent);
    1727 15 100       52 my $grandparent = ($parent_depth == 1
    1728             ? '/'
    1729             : make_canonical(get_index_parent($parent)));
    1730 15 100       42 my $greatgrandparent = ($parent_depth <= 1
        100          
    1731             ? ''
    1732             : ($parent_depth == 2
    1733             ? '/'
    1734             : make_canonical(get_index_parent($grandparent))
    1735             )
    1736             );
    1737 15         42 my $current_index_path = get_index_path($args{current_url});
    1738 15         35 my $current_index_parent = get_index_parent($args{current_url});
    1739              
    1740 15 100 66     77 if ($args{navbar_type} eq 'breadcrumb')
        100          
    1741             {
    1742 2         5 foreach my $path (@wantedpaths1)
    1743             {
    1744 20         34 my $pd = $path_depth{$path};
    1745             # a breadcrumb-navbar shows the parent, self,
    1746             # and the children the parent
    1747 20 100 100     376 if ($pd <= $current_url_depth
        50 100        
        100 100        
        100 100        
    1748             and $args{current_url} =~ /^$path/)
    1749             {
    1750 3         10 push @wantedpaths, $path;
    1751             }
    1752             elsif ($path eq $args{current_url})
    1753             {
    1754 0         0 push @wantedpaths, $path;
    1755             }
    1756             elsif ($pd >= $current_url_depth
    1757             and $path =~ m{^${current_url}})
    1758             {
    1759 3         8 push @wantedpaths, $path;
    1760             }
    1761             elsif ($parent
    1762             and $pd >= $current_url_depth
    1763             and $path =~ m{^$parent})
    1764             {
    1765 2         6 push @wantedpaths, $path;
    1766             }
    1767             }
    1768             }
    1769             elsif ($args{navbar_type} or $args{do_navbar})
    1770             {
    1771             # Rules for navbars:
    1772             # * if I am a leaf node, see my (great)uncles and siblings
    1773             # * if have children, use the same data as my parent,
    1774             # plus my immediate children
    1775 11         23 foreach my $path (@wantedpaths1)
    1776             {
    1777 134         195 my $pd = $path_depth{$path};
    1778 134 50       294 if ($pd > $current_url_depth + 1)
    1779             {
    1780 0         0 next;
    1781             }
    1782 134 100 100     1584 if ($pd == $current_url_depth + 1
        100 100        
        100 100        
        100 100        
          100        
          66        
    1783             and $path =~ m{^${current_url}})
    1784             {
    1785 19         50 push @wantedpaths, $path;
    1786             }
    1787             elsif ($pd == $current_url_depth
    1788             and $path =~ m{^${parent}})
    1789             {
    1790 35         76 push @wantedpaths, $path;
    1791             }
    1792             elsif ($grandparent
    1793             and $pd == $parent_depth
    1794             and $path =~ m{^$grandparent})
    1795             {
    1796 24         99 push @wantedpaths, $path;
    1797             }
    1798             elsif ($greatgrandparent
    1799             and $pd == $parent_depth - 1
    1800             and $path =~ m{^$greatgrandparent})
    1801             {
    1802 9         24 push @wantedpaths, $path;
    1803             }
    1804             }
    1805             }
    1806             else
    1807             {
    1808 2         5 push @wantedpaths, @wantedpaths1;
    1809             }
    1810             }
    1811             else
    1812             {
    1813 6         18 push @wantedpaths, @wantedpaths1;
    1814             }
    1815 21         250 return @wantedpaths;
    1816             } # filter_out_paths
    1817              
    1818             =head2 make_default_format
    1819              
    1820             my %default_format = make_default_format(%args);
    1821              
    1822             Make the default format hash from the args.
    1823             Returns a hash of format options.
    1824              
    1825             =cut
    1826             sub make_default_format {
    1827 27     27 1 370 my %args = (
    1828             links_head=>'
      ',
    1829             links_foot=>"\n",
    1830             subtree_head=>'
      ',
    1831             subtree_foot=>"\n",
    1832             last_subtree_head=>'
      ',
    1833             last_subtree_foot=>"\n",
    1834             pre_item=>'
  • ',
  • 1835             post_item=>'',
    1836             pre_active_item=>'',
    1837             post_active_item=>'',
    1838             pre_current_parent=>'',
    1839             post_current_parent=>'',
    1840             item_sep=>"\n",
    1841             tree_sep=>"\n",
    1842             @_
    1843             );
    1844             my %default_format = (
    1845             pre_item=>$args{pre_item},
    1846             post_item=>$args{post_item},
    1847             pre_active_item=>$args{pre_active_item},
    1848             post_active_item=>$args{post_active_item},
    1849             pre_current_parent=>$args{pre_current_parent},
    1850             post_current_parent=>$args{post_current_parent},
    1851             pre_desc=>$args{pre_desc},
    1852             post_desc=>$args{post_desc},
    1853             item_sep=>$args{item_sep},
    1854             tree_sep=>$args{tree_sep},
    1855             tree_head=>$args{links_head},
    1856             tree_foot=>$args{links_foot},
    1857             pre_item_active=>($args{pre_item_active}
    1858             ? $args{pre_item_active}
    1859             : $args{pre_item}),
    1860             pre_item_current_parent=>
    1861             ($args{pre_item_current_parent}
    1862             ? $args{pre_item_current_parent}
    1863 27 50       363 : $args{pre_item}),
        100          
    1864             );
    1865 27         319 return %default_format;
    1866             } # make_default_format
    1867              
    1868             =head2 make_extra_formats
    1869              
    1870             my %formats = make_extra_formats(%args);
    1871              
    1872             Transforms the subtree_head and subtree_foot into the "formats"
    1873             method of formatting.
    1874             Returns a hash of hashes of format options.
    1875              
    1876             =cut
    1877             sub make_extra_formats {
    1878 24     24 1 331 my %args = (
    1879             formats=>undef,
    1880             links_head=>'
      ',
    1881             links_foot=>"\n",
    1882             subtree_head=>'
      ',
    1883             subtree_foot=>"\n",
    1884             last_subtree_head=>'
      ',
    1885             last_subtree_foot=>"\n",
    1886             pre_item=>'
  • ',
  • 1887             post_item=>'',
    1888             pre_item_active=>'
  • ',
  • 1889             pre_item_current_parent=>'
  • ',
  • 1890             pre_active_item=>'',
    1891             post_active_item=>'',
    1892             pre_current_parent=>'',
    1893             post_current_parent=>'',
    1894             item_sep=>"\n",
    1895             tree_sep=>"\n",
    1896             @_
    1897             );
    1898 24         43 my %formats = ();
    1899 24 100       69 if (defined $args{formats})
    1900             {
    1901 2         4 %formats = %{$args{formats}};
      2         8  
    1902             }
    1903 24 100 66     141 if ($args{links_head} ne $args{subtree_head}
    1904             || $args{links_foot} ne $args{subtree_foot})
    1905             {
    1906 2 50       6 if (!exists $formats{1})
    1907             {
    1908 2         4 $formats{1} = {};
    1909             }
    1910 2         4 $formats{1}->{tree_head} = $args{subtree_head};
    1911 2         4 $formats{1}->{tree_foot} = $args{subtree_foot};
    1912             }
    1913 24         129 return %formats;
    1914             } # make_extra_formats
    1915              
    1916             =head1 REQUIRES
    1917              
    1918             Test::More
    1919              
    1920             =head1 INSTALLATION
    1921              
    1922             To install this module, run the following commands:
    1923              
    1924             perl Build.PL
    1925             ./Build
    1926             ./Build test
    1927             ./Build install
    1928              
    1929             Or, if you're on a platform (like DOS or Windows) that doesn't like the
    1930             "./" notation, you can do this:
    1931              
    1932             perl Build.PL
    1933             perl Build
    1934             perl Build test
    1935             perl Build install
    1936              
    1937             In order to install somewhere other than the default, such as
    1938             in a directory under your home directory, like "/home/fred/perl"
    1939             go
    1940              
    1941             perl Build.PL --install_base /home/fred/perl
    1942              
    1943             as the first step instead.
    1944              
    1945             This will install the files underneath /home/fred/perl.
    1946              
    1947             You will then need to make sure that you alter the PERL5LIB variable to
    1948             find the modules.
    1949              
    1950             Therefore you will need to change the PERL5LIB variable to add
    1951             /home/fred/perl/lib
    1952              
    1953             PERL5LIB=/home/fred/perl/lib:${PERL5LIB}
    1954              
    1955             =head1 SEE ALSO
    1956              
    1957             perl(1).
    1958              
    1959             =head1 BUGS
    1960              
    1961             Please report any bugs or feature requests to the author.
    1962              
    1963             =head1 AUTHOR
    1964              
    1965             Kathryn Andersen (RUBYKAT)
    1966             perlkat AT katspace dot com
    1967             http://www.katspace.com/tools/html_linklist/
    1968              
    1969             =head1 COPYRIGHT AND LICENCE
    1970              
    1971             Copyright (c) 2006 by Kathryn Andersen
    1972              
    1973             This program is free software; you can redistribute it and/or modify it
    1974             under the same terms as Perl itself.
    1975              
    1976             =cut
    1977              
    1978             1; # End of HTML::LinkList
    1979             __END__