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