File Coverage

blib/lib/HTML/Widget/SideBar.pm
Criterion Covered Total %
statement 54 56 96.4
branch 10 18 55.5
condition 4 7 57.1
subroutine 7 7 100.0
pod n/a
total 75 88 85.2


line stmt bran cond sub pod time code
1             package HTML::Widget::SideBar;
2              
3 1     1   29115 use strict; # Or get downvoted at Perl Monks :)
  1         2  
  1         31  
4              
5 1     1   1789 use CGI;
  1         14418  
  1         5  
6 1     1   49 use Tree::Numbered;
  1         2  
  1         19  
7 1     1   4 use base 'Tree::Numbered';
  1         2  
  1         108  
8 1         2565 use constant DEFAULT_STYLES => {bar => 'sidebar', list => 'list',
9 1     1   5 item => 'item'};
  1         2  
10              
11             our $VERSION = '1.02';
12              
13             # package stuff:
14             my $cgi = CGI->new; # Just for HTML shortcuts.
15              
16             #############################################################
17             # Actions - callbacks that generate javascript for onClick events.
18             # See POD below for use.
19              
20             # A default action generator. See the args passed to it:
21             my $default_action = sub {
22             my $self = shift;
23             my ($level, $unique) = @_;
24              
25             return '';
26             };
27              
28             # A show/hide (toggle) action:
29             my $toggle_action = sub {
30             my $self = shift;
31             my ($level, $unique) = @_;
32              
33             return "sb_toggle($level, 'sub_" . $self->getUniqueId . "')";
34             };
35              
36             ##################################################
37             # Constructors:
38              
39             # constructs a new tree or node.
40             # Arguments: By name:
41             # value - the value to be stored in the node. Used as caption.
42             # action - a perl sub that is responsible for generating Javascript
43             # code to be executed on click. The sub will be called as
44             # a method ($self->generator) so you have access to the
45             # object data when you construct the action (optional).
46             # URL - a url to navigate to on click (optional).
47             # render_hidden - Should submenus be rendered at all? If true,
48             # The submenus will be rendered and set to hidden via CSS.
49             # Returns: The tree object.
50              
51             sub new {
52 1     1   494 my $parent = shift;
53 1         5 my %args = @_;
54              
55 1         3 my $parent_serial;
56             my $class;
57            
58 1         3 my %nargs = (Value => $args{value});
59             # has URL propagation:
60 1 50       5 $nargs{URL} = $args{URL} if (exists $args{URL});
61             # no active propagation:
62 1 50       6 $nargs{Active} = (exists $args{active}) ? $args{active} : 0;
63              
64 1 50       4 $nargs{RenderHidden} =
65             (exists $args{render_hidden}) ?
66             $args{render_hidden} :
67             1;
68              
69 1         12 my $properties = $parent->SUPER::new(%nargs);
70              
71 1         160 my $action = $args{action};
72 1 50       4 if ($class = ref($parent)) {
73 1         4 $properties->{_Parent} = $parent->{_Serial};
74 1 50       13 $action = $parent->getAction unless (defined $action);
75             } else {
76 0         0 $class = $parent;
77 0         0 $properties->{_Parent} = 0;
78             }
79              
80             # Add basic set of fields:
81 1         58 $properties->addField(Changed => 'yes');
82 1         7 $properties->addField('Action'); # Does nothing if exists.
83 1         8 $properties->addField(HTML => undef);
84 1         6 $properties->addField(Script => undef);
85            
86 1         7 return $properties;
87             }
88              
89             # takes a Tree::Numbered and makes it a HTML::Widget::SideBar.
90             # Arguments: By name:
91             # tree - the tree to be converted to a sidebar.
92             # action - an action generator, as described in .
93             # active_num - the number of the active item (if any).
94             # parent - (internal use only) sets the _Parent property.
95             # base_URL - a url that will be appended later by a relative one.
96             # Returns: the tree, modified and re-blessed as a HTML::Widget::SideBar.
97              
98             sub convert {
99 4     4   293 my $parent = shift;
100 4   33     22 my $class = (ref($parent) or $parent);
101              
102 4         13 my %args = @_;
103 4         10 my ($tree, $parent_num, $active_num) =
104             @args{'tree', 'parent', 'active_num'};
105 4 100       11 my $def_action = (exists $args{action}) ? $args{action} : $default_action;
106             # remove undef:
107 4   100     12 $parent_num ||= 0;
108 4   50     17 $active_num ||= 0;
109              
110             # Won't change existing setting of 'Action' and 'URL' if it's there.
111 4         12 $tree->addField(Changed => 'yes');
112 4         38 $tree->addField(HTML => undef);
113 4         34 $tree->addField(Script => undef);
114 4         53 $tree->addField('Action', $def_action);
115 4 50       38 $tree->addField('URL', $args{base_URL}) if (exists $args{'base_URL'});
116 4         9 $tree->addField('Active');
117 4         37 $tree->addField('RenderHidden', 1);
118 4 50       35 $tree->setField('Active', 1) if ($active_num == $tree->getNumber);
119 4         21 $tree->{_Parent} = $parent_num;
120              
121 4         6 for (@{ $tree->{Items} }) {
  4         9  
122 3         9 my %inargs = (tree => $_, action => $def_action,
123             parent => $tree->getNumber,
124             active_num => $args{active_num});
125 3 50       23 $inargs{base_URL} = $args{base_URL} if (exists $args{'base_URL'});
126 3         49 $parent->convert(%inargs);
127             }
128 4         30 return bless $tree, $class;
129             }
130              
131             # constructs a new HTML::Widget::SideBar from a table in a DB using
132             # Tree::Numbered::DB.
133             # Arguments: By name:
134             # source_name - table name.
135             # source - a DB handle to work with.
136             # action - an action generator, as described in .
137             # cols - ref to a hash with mappings (see Tree::Numbered::DB).
138             # URL_col - shortcut to add the URL column to the cols.
139             # active_num - number of the active item if any.
140             # Returns: the tree, modified and re-blessed as an HTML::Widget::SideBar.
141              
142             sub readDB {
143             my $parent = shift;
144             my $class = (ref($parent) or $parent);
145             my %args = @_;
146              
147             my ($table, $dbh) = @args{'source_name', 'source'};
148             return undef unless ($table && $dbh);
149              
150             # Make shure there's always an action, even a void one.
151             my $def_action = (exists $args{action}) ? $args{action} : $default_action;
152             my $cols = $args{cols} || {};
153             $cols->{URL_col} = $args{URL_col} if ($args{URL_col});
154             # Default creation of Value is no longer used because we request a field.
155             $cols->{Value_col} ||= 'name';
156              
157             require Tree::Numbered::DB 1.01;
158             my @args = ($table, $dbh, $cols);
159             #read -> revert -> convert: construct a DB tree, loose DBness, make sidebar
160             my $tree = Tree::Numbered::DB->read(@args);
161             $tree->revert;
162             return $class->convert(tree => $tree,
163             action => $def_action,
164             active_num => $args{active_num});
165             }
166              
167              
168             # <_generate> creates the HTML that shows the sidebar.
169             # Arguments: By name:
170             # styles - alternative set of styles. Default will be used if this isn't
171             # supplied or malformed.
172             # caption - a starting caption. Optional.
173             # expand - if true, all branches will be displayed.
174             # no_ie - if false you'll get some extra code to make IE6 comply.
175             # Returns: nothing, works on the object; called from either or .
176              
177              
178              
179             sub _generate {
180             my $self = shift;
181             my %args = @_;
182              
183             my $caption = (exists $args{caption}) ? $args{caption} : $self->getValue;
184             $caption = $self->getFullCap($args{no_ie}, $caption);
185              
186             my $styles = $args{styles};
187             $styles = {} unless (ref $styles eq 'HASH');
188             my $def = DEFAULT_STYLES;
189             foreach (keys %$def) {
190             $styles->{$_} ||= $def->{$_}
191             }
192              
193             my $unique = $self->getUniqueId;
194             my $action = $self->getAction()->($self, -1, $unique);
195             $action =~ s/([^;])\s*$/$1;/; # Always end with ';' for future appends.
196             my @html; # return value.
197              
198             push @html, $cgi->start_div({-id => $styles->{bar}}), $caption;
199             my $js_buffer = "";
200             # Here comes the smart recursive stuff:
201             $self->buildList(
202             level => 0,
203             expand => $args{expand},
204             no_ie => $args{no_ie},
205             unique => $unique,
206             html => \@html,
207             styles => \%$styles,
208             js_code => \$js_buffer,
209             );
210             push @html, $cgi->end_div;
211              
212             # return @html if (wantarray);
213             # return (\@html, $js_buffer);
214             $self->setHTML(\@html);
215             $self->setScript($js_buffer);
216             $self->allProcess( sub { $_[0]->setChanged(0) } );
217             }
218              
219             # Helper for (actually does the real work).
220             # Recursively builds lists for each submenu and pushes the HTML into
221             # @html which is used as a stack.
222             # Arguments: $level - the submenu's level (main is 0),
223             # $expand - should all nodes be expanded or active branch only?
224             # $unique - the menu's unique identifier. This is an argument so
225             # changing the uniquifing rule, will only be in .
226             # $html - a reference to the stack.
227             # %styles - a hash of style names.
228             # Returns: Nothing. Modifies stack directly.
229              
230             sub buildList {
231             my $self = shift;
232             my $serial = $self->{_Serial};
233             my %args = (@_);
234             my $level = $args{level};
235             my $expand = $args{expand};
236             my $no_ie = $args{no_ie};
237             my $unique = $args{unique};
238             my $html = $args{html};
239             my %styles = %{$args{styles}};
240             my $js_code = $args{js_code};
241             # my ($level, $expand, $no_ie, $unique, $html, %styles) = @_;
242            
243             my $next_level = $level + 1; # why recalculate this again and again?
244              
245             my @list;
246             my $selfId = $self->getUniqueId;
247             my ($start_tag, $end_tag) = genListCode($styles{list}, $selfId,
248             ($expand ||$self->isActiveBranch));
249             push @$html, $start_tag;
250             # Code for active sidebar. see POD.
251             $$js_code .= "openLists.push(getCrossBrowser('sub_$selfId'));\n"
252             if (!$expand && $self->isActiveBranch && $level);
253              
254             $self->savePlace;
255             $self->reset;
256              
257             while (my $item = $self->nextNode) {
258             my $onClick = $item->getAction()->($item, $level, $unique);
259             my $caption = $item->getFullCap('no_ie');
260              
261             # start finding the list-item's HTML attributes:
262             my $attr = {};
263             if ($onClick) {
264             $onClick =~ s/([^;])\s*$/$1;/;
265             $attr->{-onClick} = $onClick;
266             }
267              
268             my $style = $styles{item};
269             if ($styles{"level$level"}) {
270             $style = $styles{"level$level"};
271             }
272            
273             # Active items get their own class, always.
274             $style .= 'Active' if ($item->getActive);
275             my $styleOver = $styles{"${style}Over"} ||
276             $styles{"level${level}Over"} || $styles{itemOver};
277             if ($styleOver) {
278             # Dynamically change class on mouseover. On Mozilla you can just
279             # Create a :hover CSS pseudo-class.
280             $attr->{-onMouseOver} = "this.className='$styleOver'";
281             $attr->{-onMouseOut} = "this.className='$style'";
282             }
283             $attr->{-class} = $style;
284              
285             push @$html, ($no_ie) ? $cgi->start_li($attr) : $cgi->start_li();
286            
287             push @$html, ($no_ie) ? $caption : $cgi->span($attr, $caption);
288            
289             # recursive stuff again: Renders sub-lists if needed.
290             if ($item->childCount() && ($item->getRenderHidden() || $expand || $item->isActiveBranch()) ) {
291             push @$html, $cgi->br();
292             $item->buildList(
293             level => $next_level,
294             expand => $expand,
295             no_ie => $no_ie,
296             unique => $unique,
297             html => $html,
298             styles => \%styles,
299             js_code => $js_code,
300             );
301             }
302             push @$html, $cgi->end_li();
303             }
304            
305             $self->restorePlace;
306             push @$html, $end_tag;
307             }
308              
309             # generates a caption that is a link if a link is wanted.
310             # Arguments: $no_ie - if force will force a link (useful for using :hover CSS).
311             # $caption - bare caption to optionally override a node's value.
312             # Returns: a string containing the ready HTML caption.
313             sub getFullCap {
314             my $self = shift;
315             my ($no_ie, $caption) = @_;
316             my $value = $caption || $self->getValue;
317             my $href = $self->getURL || '"javascript:void(0)"';
318             if ($no_ie && !$self->getURL) { return $value; }
319             else { return "$value";}
320             }
321              
322             # generates openning and closing tags for a main/nested list.
323             # Arguments: $class - the list's CSS class.
324             # $unique - the sidebar's unique identifier - see POD.
325             # $expand - true if the list is visible.
326             # Returns: a two item list with start and end tags for a list. push items
327             # between the two to create a full list.
328             sub genListCode {
329             my ($class, $unique, $expand) = @_;
330             my $args = {};
331             $args->{-style} = 'display: none' unless ($expand);
332             $args->{-class} = $class;
333             $args->{-id} = "sub_$unique";
334             return ($cgi->start_ul($args), $cgi->end_ul);
335             }
336              
337             # returns the html suffix id of the menu.
338             # Arguments: None.
339             # Returns: A unique suffix for HTML names which includes the lucky number and
340             # the node's serial number.
341              
342             sub getUniqueId {
343             my $self = shift;
344             return "$self->{_LuckyNumber}__$self->{_Serial}";
345             }
346              
347             # adds the handling of the 'changed' flag to the inherited sub.
348             # Arguments and return value are the same as the inherited sub.
349              
350             sub setField {
351             my $self = shift;
352             my $field = shift;
353              
354             my $rv = $self->SUPER::setField($field, @_);
355             if ($rv && $field ne 'Changed') {
356             $self->setChanged('yes') ;
357             }
358              
359             return $rv;
360             }
361              
362             # will return the HTML code for the sidebar, generating it if necessary.
363             # Arguments: same as for <_generate>.
364             #In list context returns a list of HTML lines to print. In scalar
365             # context returns a reference to same list.
366              
367             sub getHTML {
368             my $self = shift;
369              
370             # Do we need to regenerate?
371             my $regen = $self->getChanged;
372             unless ($regen) {
373             $self->deepProcess (sub { $$_[1] = 1 if $_[0]->getChanged }, \$regen);
374             # Yeah, this isn't very efficient, we need a recursive sub that can
375             # stop at a condition. I'll modify Tree::Numbered to do that after the
376             # exam in differential equations, maybe :)
377             }
378              
379             $self->_generate(@_) if $regen;
380             my $html = $self->getField('HTML');
381            
382             # Backwards compatibility issues prompted this:
383             return @$html if wantarray;
384             return $html;
385             }
386            
387             # will return the JavaScript code for the sidebar, generating it if necessary.
388             # Arguments: same as for <_generate>.
389             # In list context returns a list of HTML lines to print. In scalar
390             # context returns a reference to same list.
391              
392             sub getScript {
393             my $self = shift;
394              
395             # Do we need to regenerate?
396             my $regen = $self->getChanged;
397             unless ($regen) {
398             $self->deepProcess (sub { $$_[0] = 1 if $_[0]->getChanged }, \$regen);
399             }
400              
401             $self->_generate(@_) if $regen;
402             return $self->getField('Script');
403             }
404              
405             # sets the action on an item. if no action is given, the default
406             # do-nothing action is used.
407             # Arguments: $action - an action, or nothing - implies default.
408             # Returns: Nothing.
409              
410             sub setAction {
411             my $self = shift;
412             my $action = shift;
413            
414             $action ||= $default_action;
415             $self->setField('Action', $action);
416             }
417              
418             # sets the action to the stock toggle action.
419             sub setToggleAction {
420             my $self = shift;
421             $self->setField('Action', $toggle_action);
422             }
423              
424             # are here to make sure nobody dies when they're called even if
425             # the field doesn't exist.
426              
427             sub getURL {
428             my $self = shift;
429             return $self->getField('URL');
430             }
431              
432             sub setURL {
433             my $self = shift;
434             return $self->setField('URL', @_);
435             }
436              
437             # Supplies a default value to the Active field.
438             # Arguments: $arg - optional value for Active. default is 1.
439             # Returns: the new value of Active.
440             sub setActive {
441             my $self = shift;
442             my $arg = @_ ? shift : 1;
443             return $self->setField('Active', $arg);
444             }
445              
446             # returns true if any decendant of a node is active.
447             sub isActiveBranch {
448             my $self = shift;
449             return 1 if $self->getActive;
450            
451             foreach (@{$self->{Items}}) { # Yes, it's ugly.
452             return 1 if $_->isActiveBranch;
453             }
454             return 0;
455             }
456              
457             # spits out some useful JS for toggling menus on/of. See POD.
458             # Arguments: None.
459             # Returns: A huge multiline string containing some good JS.
460             sub baseJS {
461             return <
462             function getCrossBrowser(name) {
463             return document.getElementById(name); //Now it works on all of them...
464              
465             }
466              
467             var openLists = new Array(); //A stack of open menus.
468              
469             function sb_toggle (level, name) {
470             var smenu = getCrossBrowser(name);
471              
472             if (smenu.style.display != "block") {
473             hideLists(level);
474             smenu.style.display = "block";
475             openLists.push(smenu);
476             } else {
477             hideLists(level);
478             }
479             }
480              
481             function hideLists(level) {
482             for (i = openLists.length - 1; i >= level; i--) {
483             openLists[i].style.display = "none";
484             openLists.pop();
485             }
486             }
487              
488             EndJS
489              
490             }
491              
492             # is an example for the CSS used to format a real nice sidebar.
493             # It's taken from a real production site I made. Pass it through buildCSS
494             # (below) after you tweaked it a bit to fit your needs.
495             # Arguments: None.
496             # Returns: a hash of hashes. See buildCSS.
497             sub deepBlueCSS {
498             return {
499             '#nav' => {
500             position => 'absolute',
501             overflow => 'auto',
502             'z-index' => '1',
503             top => '0px',
504             right => '0px',
505             background => '#527BA5',
506             color => '#527BA5',
507             width => '19.1%',
508             height => '100%'},
509             list => {
510             'list-style-position' => 'outside',
511             'list-style-type' => 'none',
512             'text-align' => 'center',
513             'padding-top' => '0px',
514             'margin-right' => '0px',
515             'padding-left' => '0px',
516             'margin-left' => '0px',
517             display => 'block',
518             background => '#527BA5',
519             color => 'black',
520             'font-weight' => 'bold',
521             border => 'none',
522             width => '100%' },
523             ul => {
524             'margin-right' => '20px',
525             'padding-left' => '0px' },
526             navlink => {
527             display => 'block',
528             'text-align' => 'center',
529             background => '#2772BE',
530             color => 'black',
531             'font-weight' => 'bold',
532             border => 'solid 2px black',
533             margin => '5px 0% 5px 0%',
534             width => '93%',
535             'min-height' => '26px',
536             'line-height' => '26px'},
537             navover => {
538             display => 'block',
539             'text-align' => 'center',
540             background => '#5298E0',
541             color => 'black',
542             'font-weight' => 'bold',
543             border => 'solid 2px black',
544             margin => '5px 0% 5px 0%',
545             width => '93%',
546             'min-height' => '26px',
547             'line-height' => '26px'}
548             };
549             }
550              
551             # turns the datastructure provided by the previous two subs into
552             # valid CSS. Hash keys are converted into classes, and hash keys preceded
553             # with an underscore are converted into the "class:hover" syntax. For each
554             # one of these, the subhash is used for CSS property-value pairs.
555             # Arguments: $raw_css - The datastructure described above.
556             # Returns: A string containing the CSS.
557              
558             sub buildCSS {
559             my $self = shift; # Never used - class method.
560             my $raw_css = shift;
561             my $css = '';
562            
563             for my $class (keys %$raw_css) {
564             my %props = %{ $raw_css->{$class} };
565             my $hover = ($class =~ s/^_//) ? 1 : 0;
566              
567             $css .= ($class =~ /^\#/) ? $class : ".$class";
568             $css .= "$:hover" if ($hover);
569             $css .= " {\n";
570             $css .= join "\n",
571             map {my $under=$_; s/^_//; "\t$_: $props{$under};"}
572             keys %props;
573             $css .= "\n}\n\n";
574             }
575             return $css;
576             }
577              
578             1;
579              
580             =head1 NAME
581              
582             HTML::Widget::SideBar - Creates the HTML (and possibly some Javascript) for a navigational or otherwise active (hierarchical) sidebar for a web page.
583              
584             =head1 SYNOPSYS
585              
586             use HTML::Widget::SideBar;
587             use CGI; # Or something like that.
588              
589             # We are going to create a sidebar in which only the active (clicked) branch
590             # is visible.
591             my $tree = HTML::Widget::SideBar->new;
592             $tree->setToggleAction;
593              
594             foreach (1..3) {
595             my $list = $tree->append(value => "list$_");
596             $list->append(value => "aaa$_", URL => "http://localhost/$_");
597             $list->append(value => "bbb$_");
598             $list->append(value => "ccc$_");
599             }
600             $tree->getSubTree(3)->setActive;
601              
602             print header, start_html(-style => $tree->buildCSS($tree->deepBlueCSS),
603             -script => $tree->baseJS);
604             print join "\n", $tree->getHTML(styles => {bar => 'nav',
605             level0 => 'navlink',
606             level0Over => 'navover'},
607             expand => 1
608             );
609             print end_html;
610              
611             =head1 DESCRIPTION
612              
613             HTML::Widget::SideBar creates the HTML, and possibly some Javascript and CSS for a hirarchical side menu bar. It is very flexible, and allows you to create both simple navigational menus and complex dynamic sidebars with Javascript actions associated with items on the menu.
614              
615             This module started as a hack on my Javascript::Menu, which makes them very similar, so if you got one of them, you'll use the other with no sweat, I think.
616              
617             The module supports the notion of an 'active item' (usually the item denoting the page the user is viewing) and gives such item special treatment by marking it with a special CSS class and making it visible initially. It also has special support for selection menus where opening a branch closes all others.
618              
619             =head2 What should you expect to see?
620              
621             This depends greatly on your style definitions and action assignment (if you use that feature). Basically you'll have a vertical bar (which will take up as much of the screen as your CSS will allow). Inside that bar you'll have a tree of nested lists, and you can define the style for each level. When an item is clicked - its action is performed. A special predefined action allows you to show/hide child lists.
622              
623             By default only the active branch (the branch containing the active item) and the top level list will be visible. You can override this (see I).
624              
625             =head2 Some naming rules
626              
627             The sidebar will get an HTML id attribute. The default is 'sidebar' but this is changeable through I, as other naming rules.
628              
629             Every list will be of class 'list' unless another class is given through I.
630              
631             Every item in every list will be of the same class as all other items on the same level. The default is 'item' for all items, but you can set each level separately through I.
632              
633             The active item's class name is its level's class name, appended with 'Active'.
634              
635             Optionally, you may also set a mouseover style. For those of you who design for Mozilla, you really don't need that, just use the CSS pseudo-class :hover. For others, this will set the onMouseOver and onMouseOut attributes of an item to switch to and from that class.
636              
637             =head2 Setting up the supporting code.
638              
639             The sidebar created by this menu is formatted by CSS only. This means you'll have to supply it. I included a class method called buildCSS which takes a datastructure (described below) and turns it into CSS, and an example of a sidebar design in such datastructure (I used this design in production).
640              
641             You may also want to use the toggling support (described below), and in this case you'll need some Javascript. This is given directly through I. You can use it straight or dump to a file and tweak it to suit you best.
642              
643             =head2 But what are these actions and how do I generate them?
644              
645             An action is basically a piece of Javascript code that is executed when the user clicks on an item. It is added to the onClick attribute of the item. However, actions in this module are not plain strings. Instead, an action is a subroutine reference that is called when the item's HTML code is being processed. It does what it does, then returns a string containing the Javascript code. In order for this sub to be able to do anything useful, it gets 3 arguments passed to it:
646              
647             =over 4
648              
649             =item 1
650              
651             A reference to the node being processed, so you can get information on the node via object methods.
652              
653             =item 2
654              
655             The item's level in the hierarchy - the main menu is at level 0, the caption is at level -1.
656              
657             =item 3
658              
659             The menu's unique suffix.
660              
661             =back
662              
663             To make an item do nothing at all, use $item->I with no parameters.
664              
665             An important feature is that actions are inherited by new nodes from their parents. This allows you to set the beaveiour when you create the root element and not worry about it later on.
666              
667             B What this all means is that you supply some of the strings the module will be working with. This means you could, by mistake, send strings that are mixed utf8 (perl's internal encoding) and your encoding. This might break things, so if something breaks, see that your strings are in one encoding. A bitch, eh? That's the way it is when you're not in an English-speaking country.
668              
669             =head2 But I don't need all this stuff! I just want a navigational menu!
670              
671             Cool. Just set the URL property of an object either in the constructor call or using I. Menu items will be created with that URL. You can also combine a URL with an action.
672              
673             =head2 Toggling a menu on/off.
674              
675             As a convenience, a stock action is supplied that closes any list but the one clicked, and shows the clicked item's sublist if it has any. You can set this action on an item by the I method.
676              
677             =head2 Building the tree
678              
679             To get the tree that represents the structure of the sidebar, you have 3 ways:
680              
681             =over 4
682              
683             =item The hard way: HTML::Widget::SideBar->new
684              
685             This builds the root node, with your desired value and action, URL or both (which will be the default for all children of this node). You add nodes with $tree->I, and descend the hierarchy using methods found in the parent class - Tree::Numbered. For each element you supply the value (what is shown on the screen) and possibly an action.
686              
687             =item The easier way: HTML::Widget::SideBar->convert
688              
689             This just takes an existing Tree::Numbered and blesses it as a SideBar, adding an action to each node. This is easier if you already have the data structure for something else, and you want to make a menu out of it.
690              
691             =item A nice shortcut: HTML::Widget::SideBar->readDB
692              
693             If you have the module Tree::Numbered::DB (another one of mine) and you use it to store trees in a database, this method allows you to read such table directly and convert it to a menu. This is extremely useful, trust me :)
694              
695             =back
696              
697             =head2 Printing the code
698              
699             Now all you have to do is $tree->getHTML. this will return an array so you can shift out the caption and locate it inside some div while the rest of the menu is located outside, avoiding width constraints. You can also push other stuff inside and create a widget for your script.
700              
701             At any time you can also call $tree->getScript which will get you any Javascript code that was generated for the script. You won't get that code in the HTML array, which is new in version 1.02.
702              
703             Calling any of the above methods after changing the tree (setting a field or adding a node but still not deleting one) will regenerate both the HTML and the script! Pay attention to their concurrency.
704              
705             =head1 METHODS
706              
707             This section only describes methods that are not the same as in Tree::Numbered. Obligatory arguments are marked.
708              
709             =head2 Constructors
710              
711             There are three of them:
712              
713             =over 4
714              
715             =item new (I => $value, action => $action, URL => $url, render_hidden => $yes_or_not)
716              
717             Creates a new tree with one root element, whose text is specified by the value argument. If an action is not supplied, the package's default do-nothig action will be used. You'll have to add nodes manually via the I method.
718              
719             If a URL is supplied, the node will be an anchor reffering to that URL.
720              
721             If render_hidden is false, no sub-menus will appear unless the active item is inside a sub menu, in which case said sub-menu will appear.
722              
723             =item convert (I => $tree, action => $action, base_URL => $url)
724              
725             Converts a tree (given in the I argument) into an instance of Javascript::Menu. You will lose the original tree of course, so if you still need it, first use $tree->clone (see Tree::Numbered).
726              
727             Giving a value to base_URL will copy that value to the URL field of every node in the tree. you can add to this using I.
728              
729             As in new, if action is not specified, one will be created for you.
730              
731             =item readDB (I => $table, I => $dbh, cols => $cols, action => $action, URL_col => $urlcol);
732              
733             Creates a new menu from a table that contains tree data as specified in Tree::Numbered::DB. Arguments are the same as to I, except for the required source_name, which specifies the name of the table to be read, and source, which is a DBI database handle.
734              
735             The cols argument allows you to supply field mappings for the tree (see Tree::Numberd::DB). URL_col is a shortcut for giving a mapping to a collumn containing the URLs of nodes (if that's what you need). If you provide this argument, it will override any collision in the $cols hashref.
736              
737             =back
738              
739             =head2 append (value => I<$value>, action => $action, URL => $url, render_hidden => $yes_or_not)
740              
741             Adds a new child with the value (caption) $value. An action or a URL are optional, as described in I. If one of those is not given, the value is taken from its parent (if its parent has one).
742              
743             =head2 getHTML (styles => $styles, caption => 'altCaption', no_ie => true, expand => false)
744              
745             This method returns the HTML for a sidebar whose caption is the node the method was invoked on. The sidebar's caption will be the root element's value unless the caption argument is given. All arguments are optional if the tree was not changet since the last call to either I or I.
746              
747             The expand argument, if true, will cause all nodes to be shown. Otherwise only the active branch us shown.
748              
749             Unless you specify no_ie as true, the sidebar's caption will be wrapped up in a link so you cn use the :hover CSS pseudo-class on it.
750              
751             the optional styles argument allows you to change default style names described above. This should be a hash reference, with a key for each style, specifying the new name. Like:
752              
753             styles => {bar => 'nav',
754             level0 => 'navlink',
755             level0Over => 'navover'}
756              
757             You can give values to 'bar' (sidebar name), 'list' (list style) and 'item' (list item class). You can also give a certain level N its own class by giving a pair: levelB => 'someclass'. A hover style for this property can be given either with 'levelBOver', or the level's class appended with 'Over'. The first level is level 0. if a style called 'itemOver' is given, it will apply to all items regardless of their class, unless they already have some other mouseover setting.
758              
759             In aray context will return the array as said, in scalar context will return a ref to that array.
760              
761             =head2 getScript (styles => $styles, caption => 'altCaption', no_ie => true, expand => false)
762              
763             Returns the script generated for the sidebar. All arguments are ignored if the tree was not changed since the last call to either I or I. If the arguments are used, they mean the same as described under I
764              
765             =head2 Accessors
766              
767             HTML::Widget::SideBar adds to the methods of its base class the following accessors:
768              
769             =over 4
770              
771             =item getUniqueId
772              
773             Returns the unique Id that the menu will recieve when built with this node as root.
774              
775             =item getAction / setAction ($action)
776              
777             Gets and sets the item's action. If no action is given to setAction, the default do-nothing action is used.
778              
779             =item setToggleAction
780              
781             Sets the item's action to the stock toggle action described above.
782              
783             =item getURL / setURL ($url)
784              
785             Gets and sets the item's URL.
786              
787             =item getActive / setActive ($status)
788              
789             Gets and sets the item's active status. if the argument for setActive is ommitted, 1 is assumed.
790              
791             =back
792              
793             =head2 Class methods
794              
795             The following class methods help you generate supporting code for your menus:
796              
797             =over 4
798              
799             =item baseJS
800              
801             Returns the basic Javascript code for use with this module's toggle feature.
802              
803             =item deepBlueCSS
804              
805             Returns a datastructure for buildCSS. Using the properties provided by this function will result in a sidebar in shades of blue. You can tweak this to your satisfaction.
806              
807             =item buildCSS ($css)
808              
809             Takes a data structure and returns a string with valid CSS you can incorporate into your document.
810              
811             The data structure is as follows:
812              
813             A main hash with one key for each element of the sidebar (bar, list, item etc). The value for each key is again a hash with CSS property - value pairs, like top => 1, left => 1 etc. If a key is preceded by an underscore, it is converted into the :hover definition for the class of that name (this should be a name given to one of the other classes).
814              
815             =back
816              
817             =head1 METHOD SUMMARY (NEW + INHERITED)
818              
819             The following is a categorized list of all available meyhods, for quick reference. Methods that do not appear in the source of this module are marked:
820              
821             =over 4
822              
823             =item Object lifecycle:
824              
825             new, readDB, *delete, *append.
826              
827             =item Iterating and managing children:
828              
829             *nextNode, *reset, *savePlace, *restorePlace, *childCount, *getSubTree, *follow
830              
831             =item Generating code:
832              
833             deepBlueCSS, buildCSS, baseJS, getHTML
834              
835             =item Fields:
836              
837             *addField, *removeField, setField, *setFields, *getField, *getFields, *hasField.
838              
839             =back
840              
841              
842             =head1 BROWSER COMPATIBILITY
843              
844             Tested on IE6 and Firefox 1.0PR and worked. On Konqueror it's OK too but not thoroughly tested. If you test it on other browsers, please let me know what is the result.
845              
846             =head1 EXAMPLES
847              
848             testbar.pl in the examples directory shows how it's done.
849             perl-begin.css in the same place is a full style sheet taken from a site that uses this module.
850              
851             =head1 BUGS
852              
853             Directly to the author, please.
854              
855             =head1 SEE ALSO
856              
857             Tree::Numbered, Tree::Numbered::Tools, Tree::Numbered::DB, Javascript::Menu
858              
859             =head1 AUTHOR
860              
861             Yosef Meller, Emellerf@netvision.net.ilE
862              
863             =head1 CREDITS
864              
865             Shlomi Fish added the render_hidden attribute, and some of the code that separates the script from the HTML. Also supplied the CSS for examples/perl-begin.css
866              
867             =head1 COPYRIGHT AND LICENSE
868              
869             Copyright 2004 by Yosef Meller
870              
871             This library is free software; you can redistribute it and/or modify
872             it under the same terms as Perl itself.
873              
874             Exception: the file examples/perl-begin.css is not by me and uses a different license. See the head of that file for details.
875              
876             =cut