File Coverage

blib/lib/CGI/Widget/Tabs.pm
Criterion Covered Total %
statement 75 162 46.3
branch 21 64 32.8
condition 5 39 12.8
subroutine 15 21 71.4
pod 14 14 100.0
total 130 300 43.3


line stmt bran cond sub pod time code
1             =head1 MODULE FOR SALE
2              
3             I am not planning to make any changes to this module as I have not had to use
4             it in any projects of my own for the last couple of years. I am aware that
5             others are using it.
6              
7             If anyone would like to to take over maintenance/development of this module
8             pleas get in touch.
9              
10             =head1 NAME
11              
12             CGI::Widget::Tabs - Create tab widgets in HTML
13              
14             =head1 SYNOPSIS
15              
16             use CGI::Widget::Tabs;
17             my $tab = CGI::Widget::Tabs->new;
18              
19             use CGI;
20             my $cgi = CGI->new; # interface to the query params
21              
22             $tab->headings(@titles); # e.g. qw/Drivers Cars Courses/
23             $tab->default("Courses"); # the default active tab
24             $tab->force_active("Courses"); # forceably make this the active tab
25             $tab->active; # the currently active tab
26             $tab->class("my_tab"); # the CSS class to use for markup
27             $tab->cgi_object($cgi); # the object holding the query params
28             $tab->cgi_param("t"); # the CGI query parameter to use
29             $tab->drop_params("ays"); # do NOT pass on "Are You Sure?" answers
30             $tab->wrap(4); # wrap after 4 headings...
31             $tab->indent(1); # ...and add indentation
32             $tab->render; # the resulting HTML code
33             $tab->display; # same as `print $tab->render'
34              
35              
36             $h = $tab->heading; # new OO heading for this tab
37             $h->text("TV Listings"); # heading text
38             $h->key("tv"); # key identifying this heading
39             $h->raw(1); # switch off HTML encoding
40             $h->url("whatsontonight.com"); # redirect URL for this heading
41             $h->class("red"); # this heading has it's own class
42              
43             # See the EXAMPLE section for a complete example
44              
45             =head1 DESCRIPTION
46              
47             =head2 Introduction
48              
49             CGI::Widget::Tabs lets you simulate tab widgets in HTML. You could benefit
50             from a tab widget if you want to serve only one page. Depending on the tab
51             selected you fetch and display the underlying data. There are three main
52             reasons for taking this approach:
53              
54             1. For the end user not to be directed to YAL or YAP (yet another link / yet
55             another page), but keep it all together: The single point of entry paradigm.
56              
57             2. As a consequence the end user deals with a more consistent and integrated
58             GUI. This will give a better "situational awareness" within the application.
59              
60             3. For the Perl hacker to handle multiple related data sources within the
61             same script environment.
62              
63              
64             As an example the following tabs could be used on a web page for someone's
65             spotting hobby:
66              
67             __________ __________ __________
68             / Planes \ / Trains \ / Classics \
69             ------------------------------------------------------
70             _________
71             / Bikes \
72             ------------------------
73              
74             As you can see, the headings wrap at three and a small indentation is added
75             to the start of the next row. The nice thing about CGI::Widget::Tabs is that
76             the tabs know their internal state. So you can ask a tab for instance which
77             heading has been clicked by the user. This way you get instant feedback.
78              
79             =head2 "Hey Gorgeous!"
80              
81             Of course tabs are useless if you can't "see" them. Without proper make up
82             they print as ordinary text. So you really need to fancy them up with some
83             eye candy. The designed way is that you provide a CSS style sheet and have
84             CGI::Widget::Tabs use that. See the class() method for how to do this.
85              
86              
87             =head1 EXAMPLE
88              
89             Before digging into the API and all accessor methods, this example will
90             illustrate how to implement the spotting page from above. So you have
91             something to start with. It will give you enough clues to get on the road
92             quickly. The following code is a simple but complete example. Copy it and run
93             it through the webservers CGI engine. (For a even more complete and useful
94             demo with multiple tabs, see the file tabs-demo.pl in the CGI::Widget::Tabs
95             installation directory.) To fully appreciate it, it would be best to run it
96             in a performance environment, like mod_perl or SpeedyCGI.
97              
98             #! /usr/bin/perl -w
99              
100             use CGI::Widget::Tabs;
101             use CGI;
102              
103             print <
104             Content-Type: text/html;
105              
106            
107            
114            
115             EOT
116              
117             my $cgi = CGI->new;
118             my $tab = CGI::Widget::Tabs->new;
119             $tab->cgi_object($cgi);
120             $tab->headings( qw/Planes Traines Classics Bikes/ );
121             $tab->wrap(3);
122             # $tab->wrap(1); # |uncomment to see the effect of
123             # $tab->indent(0); # |wrapping at 1 without indentation
124             $tab->default("Traines");
125             $tab->display;
126             print "
We now should run some intelligent code ";
127             print "to process ", $tab->active, "
";
128             print "";
129              
130             =head1 PUBLIC INTERFACE
131              
132              
133             =cut
134              
135             package CGI::Widget::Tabs;
136              
137              
138             # pragmata
139 2     2   75146 use strict;
  2         4  
  2         84  
140 2     2   11 use vars qw/$VERSION/;
  2         3  
  2         89  
141              
142             # Standard Perl Library and CPAN modules
143 2     2   17 use Carp;
  2         8  
  2         133  
144 2     2   1681 use URI::Escape();
  2         2953  
  2         61  
145 2     2   2282 use HTML::Entities();
  2         14525  
  2         111  
146              
147             # CGI::Widget::Tabs modules
148 2     2   1677 use CGI::Widget::Tabs::Heading;
  2         13  
  2         3848  
149              
150              
151             $VERSION = "1.14";
152              
153              
154              
155             =head2 Public Class Interface
156              
157             =head3 new
158              
159             new()
160              
161             Creates and returns a new CGI::Widget::Tabs object. new() does not take any
162             arguments.
163              
164             =cut
165              
166             sub new {
167 10     10 1 21910 my $proto = shift;
168 10   33     44 my $class = ref($proto) || $proto;
169 10         15 my $self = {};
170 10         28 bless ($self, $class);
171 10         25 $self->indent(1);
172 10         512 return $self;
173             }
174              
175              
176             =head2 Public Object Interface
177              
178             =head3 active
179              
180             active()
181              
182             Returns a string indicating the current active tab heading. This is (in order of
183             precedence) the heading set by force_active(), the heading being clicked on, the
184             default heading, or the first in the list. The string value will either be the
185             heading key or the heading text, depending on if you chose to use keys. Example:
186              
187             if ( $tab->active() eq "Trains" ) { # heading text only
188              
189             if ( $tab->active() eq "-t" ) { # key value ISO heading text
190              
191             =cut
192              
193             sub active {
194              
195             #
196             # Returns the active heading. In order of precendence:
197             # 1. A mandatory heading
198             # 2. The heading clicked by the user
199             # 3. The default heading
200             # 4. The first heading in the list
201             #
202 10     10 1 39 my $self = shift;
203 10         11 my $active;
204              
205             # 1. Heading clicked
206             # 1. Mandatory heading
207 10         19 $active = $self->force_active();
208 10 50       21 return $active if defined $active;
209              
210             # 2. Heading clicked
211 10         19 $active = $self->cgi_object->param($self->cgi_param);
212 10 100       200 return $active if defined $active;
213              
214             # 3. Default
215 6         12 $active = $self->default;
216 6 100       39 return $active if defined $active;
217              
218             # 4. First
219 3         8 my $h = ($self->headings)[0]; # headings are always OO objects
220 3   66     11 return $h->key || $h->text;
221             }
222              
223             =head3 cgi_object
224              
225             cgi_object(OBJECT)
226              
227             Sets/returns the CGI or CGI::Minimal object. If the optional argument OBJECT is
228             given, the CGI object is set, otherwise it is returned. CGI::Widget::Tabs uses
229             this object internally to process the CGI query parameters. If you want you can
230             use some other CGI object handler. However such an object handler must provide a
231             param() method with corresponding behaviour as do CGI or CGI::Minimal. Note that
232             currently only CGI and CGI::Minimal have been tested. Example:
233              
234             # set
235             my $cgi = CGI::Minimal->new;
236             $tab->cgi_object($cgi);
237              
238             # get
239             my $cgi = $tab->cgi_object;
240              
241             =cut
242              
243             sub cgi_object {
244              
245             #
246             # The cgi object to retrieve the parameters from.
247             # Could be a CGI object or a CGI::Minimal object.
248             #
249 20     20 1 50 my $self = shift;
250 20         23 my $cgi = shift;
251 20 100       37 if ( $cgi ) {
252 10 50 33     35 if ( ref $cgi ne "CGI" and ref $cgi ne "CGI::Minimal") {
253 0         0 carp "Warning: Expected CGI or CGI::Minimal object";
254             }
255 10         19 $self->{cgi_object} = $cgi;
256             }
257 20         45 return $self->{cgi_object};
258             }
259              
260             =head3 cgi_param
261              
262             cgi_param(STRING)
263              
264             Sets/returns the CGI query parameter. This parameter identifies the tab in the
265             CGI query string (the funny part of the URL with the ? = & # characters). If
266             the optional argument STRING is given, the query parameter is set. Otherwise it
267             is returned. Usually you can leave this untouched. In that case the default
268             parameter "tab" is used. You will need to set this if you have more CGI query
269             parameters on the URL with "tab" already being taken. Another situation is if
270             you use multiple tab widgets on one page. They both would use "tab" by default
271             causing conflicts. Example:
272              
273             # Lets paint a fruit tab and a vegetable tab
274             my $fruits_tab = CGI::Widget::Tabs->new;
275             my $vegies_tab = CGI::Widget::Tabs->new;
276              
277             # this is our link with the outside world
278             my $cgi = CGI::Minimal->new;
279             $fruits_tab->cgi_object($cgi);
280             $vegies_tab->cgi_object($cgi);
281              
282             # In the CGI params collection the first is
283             # identified by 'ft' and the second by 'vt'
284             $fruits_tab->cgi_param("ft");
285             $vegies_tab->cgi_param("vt");
286              
287             =cut
288              
289             sub cgi_param {
290              
291             #
292             # CGI parameter specifing the tab. Defaults to "tab".
293             #
294 44     44 1 403 my $self = shift;
295 44 100       152 if ( @_ ) {
296 10         22 $self->{cgi_param} = shift;
297             }
298 44   50     429 return $self->{cgi_param} || "tab";
299             }
300              
301             =head3 drop_params
302              
303             drop_params(LIST)
304              
305             Sets/retrieves the list of CGI parameters to be dropped from the parameter
306             list. If the optional argument LIST is given the list is set, otherwise it is
307             retrieved. Suppose you have clicked "Yes" to some "Are you sure?" question. You
308             certainly want that question to be asked every time, right? Especially if the
309             actions that go with it are destructive. If you did NOT specify the parameter
310             to be dropped, "Yes" would have been silently passed on to the parameter
311             list. That would effectively preset "Are you sure" with "Yes" causing disastrous
312             results. Examples:
313              
314             $tab->drop_params("ays"); # drop the "Are you sure" param
315              
316             =cut
317              
318             sub drop_params {
319              
320             #
321             # These parameters should not be passed on.
322             #
323 0     0 1 0 my $self = shift;
324 0 0       0 if ( @_ ) {
325 0         0 $self->{drop_params} = [@_];
326             }
327 0 0       0 return @{ $self->{drop_params} || [] };
  0         0  
328             }
329              
330              
331              
332             =head3 class
333              
334             class(STRING)
335              
336             Sets/returns the name of the CSS class used for the tabs markup. If the optional
337             argument STRING is given the class is set, otherwise it is returned. If not
338             set, the widget will be based on the class "tab". In the accompanying style
339             sheet, there are five class elements you need to provide:
340              
341             =over 4
342              
343             =item 1. A table element for containment of the entire tab widget
344              
345             =item 2. A td element for a normal tab
346              
347             =item 3. A td element for the active tab
348              
349             =item 4. A td element for the spacers
350              
351             =item 5. A td element for the indentation (if needed)
352              
353             =back
354              
355             The class names of these elements are directly borrowed from the class()
356             method. The td elements for the active tab, the spacers and the indentations are
357             suffixed with "_actv", "_spc" and "_ind" respectively. For instance, if you'd
358             run
359              
360             $tab->class("my_tab");
361              
362             then the elements look like:
363              
364             # the entire table ); ); \n"; # | row if it didn't just
365             # normal tab
366             # active tab
367             # spacer
368             # indentation
369              
370             If you don't wrap headings, then ofcourse you won't need to specify the
371             indentation td's. By the way, the indentation will usually look most natural if
372             it has the same width as the spacers or a multiple thereof. Look at the example
373             in the EXAMPLE section to see how this all works out.
374              
375             =cut
376              
377             sub class {
378              
379             #
380             # The CSS class for display of the tabs
381             # Defaults to 'tab'.
382             #
383 0     0 1 0 my $self = shift;
384 0 0       0 if ( @_ ) {
385 0         0 $self->{class} = shift;
386             }
387 0   0     0 return $self->{class} || "tab";
388             }
389              
390              
391             =head3 default
392              
393             default(STRING)
394              
395             Overrides which heading is the default. Normally CGI::Widget::Tabs will make the
396             first heading active. Use the default() method if you want to deviate from
397             this. The optional argument STRING must either be the heading key or the heading
398             text, depending on how you chose to initialize the headings. Example:
399              
400             # Make the "Trains" heading the default active one.
401             $tab->default("Trains");
402              
403             # ...or perhaps...
404             $tab->default("-t");
405              
406             =cut
407              
408             sub default {
409              
410             #
411             # The default active heading
412             #
413 12     12 1 33 my $self = shift;
414 12 100       29 if ( @_ ) {
415 6         10 $self->{default} = shift;
416             }
417 12         23 return $self->{default}
418             }
419              
420             =head3 display
421              
422             display()
423              
424             Renders the tab widget and prints the resulting HTML to the default output
425             handle (usually STDOUT). Example:
426              
427              
428             $tab->display; # this is the same as...
429              
430             print $tab->render; # ...but saves a few keystrokes
431              
432             See also the render() method.
433              
434             =cut
435              
436             sub display {
437              
438             #
439             # save a few keystrokes
440             #
441 0     0 1 0 my $self = shift;
442 0         0 print $self->render;
443             }
444              
445              
446              
447             =head3 force_active
448              
449             force_active(STRING)
450              
451             Forces the activation of a specific tab identified by it's heading text
452             or key. This is useful if you have an application which must show a
453             certain tab after doing someting. Or if you're paranoid and you've been
454             given a CGI query string which you don't trust. In both cases you can
455             make sure the tab of your preference is activated. Example:
456              
457             $tab->force_active("Trains"); # heading text only
458              
459             $tab->force_active("-t"); # key
460              
461             $tab->force_active(undef); # forget all about it
462              
463              
464             =cut
465              
466             sub force_active {
467              
468             #
469             # Activates a heading. Takes heading text, key or undef.
470             #
471 10     10 1 13 my $self = shift;
472 10 50       22 if ( @_ ) {
473 0         0 $self->{force_active} = shift;
474             }
475 10         17 return $self->{force_active};
476             }
477              
478              
479              
480             =head3 heading
481              
482             heading()
483              
484             Creates, appends and returns a new heading. The return value will always be an
485             OO heading object. Example:
486              
487             my $h = $tab->heading();
488              
489             In general you will use OO headings if the headings() method is not flexible
490             enough. For trivial applications the headings() method mostly suffices. Look at
491             section PROPERTIES OF OO HEADINGS for more information on OO headings.
492              
493             =cut
494              
495             sub heading {
496              
497             #
498             # Create, add, and return a new heading object
499             #
500 24     24 1 65 my $self = shift;
501 24         79 my $h = CGI::Widget::Tabs::Heading->new();
502 24         48 push @{ $self->{headings} }, $h;
  24         53  
503 24         49 return $h;
504             }
505              
506             =head3 headings
507              
508             headings(LIST)
509              
510             Sets/returns the tab headings. Without arguments the currently defined headings
511             are returned. If no headings are defined, the empty list is returned. Any
512             returned heading will always be an OO heading, regardless of if and how the
513             initializing LIST argument is used. Look at section PROPERTIES OF OO HEADINGS
514             for more info on how to deal with OO headings.
515              
516             The optional LIST argument is a short-cut to the OO headings interface. The
517             elements of LIST can take various forms. Let's take a moment to take a close
518             look at the headings of a tab. Tab headings are the things that --from human
519             perspective-- identify a tab page. Observe the spotting example above. Here the
520             different tab pages are identified by the strings "Planes", "Trains", "Classics"
521             and "Bikes". They form the heading for each seperate tab. The LIST elements can
522             be used to preset these tab headings.
523              
524             An element of LIST can be any one of:
525              
526             =over 4
527              
528             =item * a string. E.g.:
529              
530             qw/Planes Trains Classics Bikes/
531              
532             This is the simplest initializer. In the spotting example the four tabs headings
533             are easily created by feeding these words as a list to the headings()
534             method. And then you are almost done: the headings can be displayed and each
535             heading gets it's own self referencing URL.
536              
537             =item * a key/value pair. E.g.:
538              
539             ( -p => "Planes",
540             -t => "Trains",
541             -c => "Classics,
542             -b => "Bikes" )
543              
544             For trivial CGI::Widget::Tabs applications, the k/v pairs are the ones you will
545             probably use the most. They come in handy because you don't need to check the
546             value returned by active() against very long words. Even better, if you change
547             the tab headings (upper/lower case, typo's) but use the same keys you don't need
548             to change your code. So it is less error prone. As a pleasant side effect, the
549             URL's get to be significantly shorter. Do notice that the keys want to be
550             unique. Keys in a k/v list are not at all magical. You can choose any string you
551             like with the provision that they start with the '-' (hyphen) sign. The starting
552             '-' of a list entry is what triggers CGI::Widget::Tabs to decide this is a k/v
553             entry. Single or dual character strings tend to be the most convenient keys.
554              
555             =item * a hash
556              
557             This use of the headings() method will clutter up your code. The hash tries to
558             mimic and encapsulate all OO accessor methods. If think you need an initializer
559             hash, you probably want OO headings. Use it only if you must. If you can stick
560             with the strings or k/v pairs. That said, the hash keys are the named
561             equivalents of the OO heading properties. E.g.:
562              
563             ( { text => "Planes",
564             key => "p",
565             url => "www.aviation-mag.com",
566             class => "heavens_blue",
567             raw => 0 },
568              
569             =back
570              
571             You can mix these types in any way you like. The various types will be
572             translated on the fly to OO headings and then processed. Thus you can safely
573             say:
574              
575             $tab->headings( "Plaines",
576             -t => "Traines",
577             { text => "Classics",
578             key => "c",
579             ... } )
580              
581             Just as the hash initializer, this use does clutter up your code. The reason is
582             that different concepts of information are piled up on one big heep. You will
583             need to scrutinize the code to understand what it is going on. Although it is
584             supported you should refrain yourself from making use of these combinations.
585              
586             As a summary, here are a three examples of the headings() method for the
587             spotting page.
588              
589             # Example 1: Set the headings with a list of strings
590             my $tab = CGI::Widget::Tabs->new();
591             $tab->headings( qw/Planes Trains Classics Bikes/ );
592              
593             # Example 2: Set the headings with a list of k/v pairs
594             my $tab = CGI::Widget::Tabs->new();
595             $tab->headings( -p => "Planes",
596             -t => "Trains",
597             -c => "Classics,
598             -b => "Bikes" );
599              
600             # Example 3: Isolate the "Classics" heading
601             my $h = ($tab->headings)[2];
602              
603             Note that these few statements provide almost enough logic to generate the HTML
604             for the tab widget!
605              
606             =cut
607              
608             sub headings {
609              
610             # Takes optional user defined simple headings as arguments,
611             # which will be transformed into OO headings. E.g.:
612             # ( "Software", -hw => "Hardware", { text => "Wetware", key => "ww" } )
613             #
614 9     9 1 534 my $self = shift;
615 9 100       24 if ( @_ ) { # any arguments?
616              
617 6         8 my $h; # OO heading
618             my $ht; # _heading _text
619              
620 6         25 HEADING: while ( my $arg = shift @_ ) {
621 14         30 $h = $self->heading(); # add a new heading
622              
623 14 50       31 if ( ! ref $arg ) { # Not a hash initializer
624             # -- k/v pair
625 14 100       40 ( $arg =~ /^-/ ) && do {
626 7         21 $h->key($arg);
627 7         19 $h->text(shift @_);
628 7         21 next HEADING;
629             };
630              
631             # -- text only
632 7         25 $h->text($arg);
633 7         23 next HEADING;
634             }
635              
636             # -- hash initializer
637 0 0       0 ( ref($arg) eq "HASH" ) && do {
638 0 0       0 if ( ! $arg->{text} ) {
639 0         0 croak "Hash initializer is missing mandatory text element";
640             }
641              
642 0         0 $h->text($arg->{text});
643 0 0 0     0 if ( exists( $arg->{key} ) && $arg->{key} ) { $h->key( $arg->{key} ) }
  0         0  
644 0 0 0     0 if ( exists( $arg->{url} ) && $arg->{url} ) { $h->url( $arg->{url} ) }
  0         0  
645 0 0 0     0 if ( exists( $arg->{raw} ) && $arg->{raw} ) { $h->raw( $arg->{raw} ) }
  0         0  
646 0 0 0     0 if ( exists( $arg->{class} ) && $arg->{class} ) { $h->class( $arg->{class} ) }
  0         0  
647 0         0 next HEADING;
648             };
649              
650 0         0 croak "Unsupported heading type";
651 0         0 next;
652             }
653             }
654 9 50       11 return @{ $self->{headings} || [] };
  9         33  
655             }
656              
657             =head3 indent
658              
659             indent(BOOLEAN)
660              
661             Sets/returns the indentation setting. Without arguments the current setting is
662             returned. indent() specifies if indentation should be added to the next row when
663             the headings get wrapped. indent() is a toggle. By default indent() is set to
664             TRUE. You must explicitely switch it off for the desired effect. The optional
665             argument BOOLEAN can be any argument evaluating to a logical value.
666              
667             The purpose of swithing off indentation is to simulate a vertical menu. In the
668             spotting example, running
669              
670             $tab->wrap(1);
671             $tab->indent(0);
672              
673             would result in something like:
674              
675             __________
676             | Planes |
677             --------------
678             __________
679             | Trains |
680             --------------
681             __________
682             | Classics |
683             --------------
684             __________
685             | Bikes |
686             --------------
687              
688              
689             You probably need to tweak your style sheet to have it look nicely.
690              
691             =cut
692              
693             sub indent {
694              
695             #
696             # Indentation after wrapping to next line?
697             #
698 10     10 1 13 my $self = shift;
699 10         12 my $arg = shift;
700              
701 10 50       21 if ( defined $arg ) {
702 10 50       34 $self->{indent} = $arg ? 1 : 0;
703             }
704 10         20 return $self->{indent};
705             }
706              
707              
708             =head3 render
709              
710             render()
711              
712             Renders the tab widget and returns the resulting HTML code. This is useful if
713             you need to print the tab to a different file handle. Another use is if you want
714             to manipulate the HTML. For instance to insert session id's or the like. See
715             the class() method and the EXAMPLE section somewhere else in this document to
716             see how you can influence the markup of the tab widget. Example:
717              
718             my $html = $tab->render;
719             print HTML $html; # there's a session id filter behind HTML
720              
721             =cut
722              
723             sub render {
724              
725             #
726             # Process the lot and display it.
727             #
728 0     0 1   my $self = shift;
729 0           my $cgi = $self->cgi_object;
730 0           my @headings = $self->headings;
731 0           my $class = $self->class;
732 0           my $cgi_param = $self->cgi_param;
733 0           my $active = $self->active;
734 0           my $wrap = $self->wrap;
735 0           my $indent = $self->indent;
736 0           my $spacer = qq(
737 0           my $indentation = qq(
738 0           my @html;
739             my $url;
740 0           my $query_string_min_min; # the query string minus the varying tab
741              
742             # -- reproduce the CGI query string EXCEPT the varying tab
743 0           my @param_list = grep( $_ ne $cgi_param,$cgi->param() );
744              
745             # - From this list remove the wannabe-dropped
746 0           my %drop_params = ();
747 0           foreach ( $self->drop_params() ) { $drop_params{$_} = 1 };
  0            
748 0           @param_list = grep (!exists $drop_params{$_}, @param_list);
749              
750 0 0         if ( @param_list ) {
751 0   0       $query_string_min_min = join "&", map ( "$_=".URI::Escape::uri_escape($cgi->param($_)||"") , @param_list );
752 0           $query_string_min_min .= "&";
753             } else {
754 0           $query_string_min_min = "";
755             }
756              
757              
758 0 0         if ( @headings ) {
759 0           @html = ();
760 0           push @html, "\n";
761              
762 0           my $heading_nr = 1; # we're about to render the first heading...
763 0           my $row_nr = 1; # ...of the first row
764 0           my $param_value;
765             my $h;
766 0           my $url;
767              
768 0           foreach $h ( @headings ) {
769 0 0         if ( $heading_nr == 1 ) { # first one in the row?
770 0           push @html, qq(\n\n); $spacer\n"; \n"; # | yes, end this row
771 0 0 0       if ( $indent && $row_nr > 1 ) { # = print indents if
772 0           push @html, ( $indentation x ($row_nr - 1)); # = necessary
773             } # =
774 0           push @html, "$spacer\n"; # each row starts with a spacer
775             }
776              
777             # -- actual headings
778 0   0       $param_value = $h->key || $h->text;
779 0 0         if ( defined $h->class() ) { # heading has local class?
780 0           push @html, qq(';
781             } else {
782 0           push @html, qq( 783 0 0         push @html, qq(_actv) if $param_value eq $active;
784 0           push @html, qq(">);
785             }
786              
787             # -- user defined URL or default self ref. URL?
788 0   0       my $url = $h->url || ( "?$query_string_min_min$cgi_param=".URI::Escape::uri_escape($param_value) );
789 0           push @html, _link( $h->text , $url );
790 0           push @html, "
791              
792             # -- end of row
793 0 0 0       if ( $wrap && ( $heading_nr == $wrap ) ) { # last one on this row?
794 0           push @html, "
795 0           push @html, "
\n"; # |
796 0           $heading_nr = 0;
797 0           $row_nr++;
798             }
799 0           $heading_nr++;
800             }
801              
802             # --- all headings printed
803 0 0         if ( $heading_nr > 1 ) { # | We need to end this
804 0           push @html, "
805 0           push @html, "
\n"; # | get wrapped.
806             }
807             }
808              
809 0           push @html, "\n";
810 0           return join("", @html);
811             }
812              
813              
814             =head3 wrap
815              
816             wrap(NUMBER)
817              
818             Sets or returns the wrap setting. Without arguments the current wrap setting is
819             returned. If the argument NUMBER is given the headings will wrap to the next row
820             after NUMBER headings. By default headings are not wrapped.
821              
822             =cut
823              
824             sub wrap {
825              
826             #
827             # wrap to next row after this num of headings
828             #
829 0     0 1   my $self = shift;
830 0 0         if ( @_ ) {
831 0           $self->{wrap} = shift;
832             }
833 0           return $self->{wrap};
834             }
835              
836              
837             =head1 INTERNALS
838              
839             =head2 Private Class Methods
840              
841             =head3 _link
842              
843             link($text, $href)
844              
845             Returns a HTML 'a' tag pair linking to $href with text $text
846              
847             =cut
848              
849             sub _link {
850              
851             #
852             # Create a link for some text to a href
853             # Expects = (,) pair.
854             #
855 0     0     return qq($_[0]);
856             }
857              
858              
859              
860             1;
861              
862             __END__