HTML-TreeStructured
view release on metacpan or search on metacpan
lib/HTML/TreeStructured.pm view on Meta::CPAN
package HTML::TreeStructured;
use 5.006;
use strict;
use warnings;
use Carp qw(croak);
use HTML::Template 2.6;
=head1 NAME
HTML::TreeStructured - Perl extension for generating tree structured HTML
=head1 SYNOPSIS
use HTML::TreeStructured;
### Describe tree via collection of Node and its properties
### Method 1: Via ArrayRef
###
### Node can be a string or '/' concatenated strings to show ancestry
### Properties are name/value pairs
my $tree1 = [
['/aaa', color => 'green'],
['/aaa/bbb' mouseover => 'This is addl info'],
['/aaa/ccc', color => 'red', active => 0]
];
### Method 2: Via Hashref
my $tree2 = {
aaa => {
color => 'green',
bbb => {
mouseover => 'This is addl info',
},
ccc => {
color => 'red',
active => 0,
},
};
Interpreted Node Properties:
color = Color of the node name
mouseover = Mouse Over text for the node (Info image is displayed next to node)
active = 0 would cause strike thru on node
highlight = color code used for marker highlight of node
url = URL to hyperlink the node
tooltip = popup when mouse is over the link (together with url) (See HTML::Tooltip::Javascript)
closed = 1 if node be closed on default display (default, all nodes are open)
comment = Text to display next to node in bold
weight = A numeric value on node which will be used for sorting node position in at sibling level
(Default, nodes are sorted in ascending order per dictionary order)
### Now get HTML equivalent for the tree
### The associated JavaScript for nodes close/open and ExpandAll/CollapseAll is generated alongside
$tree_html = HTML::TreeStructured->new(
name => 'tree_name',
image_path => '/images/',
data => $tree1,
title => "My Tree",
title_width => 300,
level => {}, ### If scalar, close BEYOND this depth. Depth start at 0.
### If Hash, close for depths specified in keys
)->output;
### The same module can be used to generate FAQ - see "examples/faq.cgi"
=cut
our $VERSION = "1.01";
sub new
{
my $pkg = shift;
# setup defaults and get parameters
my $self = bless({
title_width => 300,
child_indent => 20,
image_path => ".",
level => {}, ### Default open up the full tree - This would contain
### Scalar => depth BEYOND which nodes are closed for tree display
### Hash => depths matching keys are closed for tree display
### NB: Depth starts at ZERO
### E.g. Value of
### level=2 means Tree nodes are closed at depth 3 and more (all others are open)
### level={2=>1} means Tree node at depth 2 are closed (all others are open)
@_,
}, $pkg);
# fix up image_path to always end in a /
$self->{image_path} .= "/" unless $self->{image_path} =~ m!/$!;
# check required params
foreach my $req (qw(name data title)) {
croak("Missing required parameter '$req'") unless exists $self->{$req};
}
if (ref($self->{data}) eq 'ARRAY') {
$self->{data} = process_arrayref($self->{data});
} else {
my $res;
my $data = $self->{data};
my @kkk = grep { ref($data->{$_}) eq 'HASH' } keys %$data;
if (1 == @kkk) {
$res = process_hashref($kkk[0], $data->{$kkk[0]});
} else {
$res = process_hashref('ROOT', $data);
}
$self->{data} = $res;
}
#use Data::Dumper;
#print '<pre>', Dumper($self->{data}), '</pre>';
return $self;
}
sub output
{
my $self = shift;
our $TEMPLATE_SRC;
my $template = HTML::Template->new(scalarref => \$TEMPLATE_SRC,
die_on_bad_params => 0,
global_vars => 1,
);
# build node loop
my @loop;
$self->_output_node(node => $self->{data},
loop => \@loop,
depth => 1,
level => $self->{level},
);
my @parents; ### Collect all nodes with children - for use in ExpandAll/CollapseAll
map { push(@parents, {id => $_->{id}}) if ($_->{has_children}) } @loop;
# setup template parameters
$template->param(loop => \@loop);
$template->param(parents => \@parents);
$template->param(map { ($_, $self->{$_}) } qw(name title title_width child_indent image_path));
# get output for the widget
my $output = $template->output;
return $output;
}
# recursively add nodes to the output loop
sub _output_node
{
my ($self, %arg) = @_;
my $node = $arg{node};
my $depth = $arg{depth};
my $level = $arg{level};
#use Data::Dumper;
#print "<pre>", Dumper($node), "</pre>";
my $id = next_id();
push @{$arg{loop}}, { label => $node->{label}, ### Label to appear in tree
value => $node->{value}, ### Hidden Value (whats the use?, but good to have)
id => $id, ### Unique Id (no immediate use, but good to have)
open => display_closed_node($depth, $level, $node->{closed}) ? 0 : 1,
### During Display, whether to close/open
url => $node->{url}, ### Url to link
mouseover => $node->{mouseover}, ### mouseover message
tooltip => $node->{tooltip}, ### Tooltip popup box (See HTML::Tooltip::Javascript)
active => ((defined($node->{active}) and $node->{active} == 0) ? 0 : 1),
### Is this node active? If not, strike thru during display
color => $node->{color} || 'black', ### Color of the label
highlight => $node->{highlight}, ### Color to use for marker highlight
comment => $node->{comment}, ### Comment in bold within parentheses next to label
};
if ($node->{children} and @{$node->{children}}) {
$arg{loop}[-1]{has_children} = 1;
for my $child (@{$node->{children}}) {
$self->_output_node(node => $child,
loop => $arg{loop},
depth => $depth + 1,
level => $level,
);
}
push @{$arg{loop}}, { end_block => 1 };
}
}
sub display_closed_node
{
my $ddd = shift; # Depth Info
my $lll = shift; # Level Info
# scalar ==> Close all nodes BEYOND this depth
# hash ==> Close all nodes for specified keys
my $closed = shift;
if (defined($closed)) {
return $closed;
}
if (ref($lll) eq 'HASH') {
return ($lll->{$ddd} ? 1 : 0);
} else {
return ($ddd > $lll ? 1 : 0);
}
}
{
my $id = 1;
sub next_id { $id++ }
}
our $TEMPLATE_SRC = <<END;
<style type="text/css">
<!--
/* title bar style. The width here will define a minimum width for
the widget. */
.hpts-title {
padding: 2px;
margin-bottom: 4px;
font-size: large;
color: #ffffff;
background-color: #666666;
width: <tmpl_var title_width>px;
}
( run in 0.763 second using v1.01-cache-2.11-cpan-364913b4093 )