package Data::TreeDumper::Renderer::DHTML; use 5.006; use strict; use warnings; require Exporter; use AutoLoader qw(AUTOLOAD); our @ISA = qw(Exporter); our %EXPORT_TAGS = ( 'all' => [ qw() ] ); our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); our @EXPORT = qw(); our $VERSION = '0.07'; use constant DHTML_CLASS => 'data_treedumper_dhtml' ; my $uuuid = int(rand(100_000)) ; my %ascii_to_html = ( '<' => '<' , '>' => '>' , '&' => '&' , "'" => ''' , '"' => '"' , ' ' => ' ' ) ; #------------------------------------------------------------------------------------------- sub GetRenderer { my $expand_collapse_button_id = "expand_collapse_button_${uuuid}" ; $uuuid++ ; my $search_button_id = "search_button_${uuuid}" ; $uuuid++ ; return ( { BEGIN => \&RenderDhtmlBegin , NODE => \&RenderDhtmlNode , END => \&RenderDhtmlEnd # data needed by the renderer , EXPAND_COLLAPSE_BUTTON_ID => $expand_collapse_button_id , SEARCH_BUTTON_ID => $search_button_id , PREVIOUS_LEVEL => -1 , PREVIOUS_ADDRESS => "c_${uuuid}_ROOT" , TABULATION => 0 , @_ } ) ; } #------------------------------------------------------------------------------------------- sub RenderDhtmlBegin { my ($title, $td_address, $element, $perl_size, $perl_address, $setup) = @_ ; my $class = $setup->{RENDERER}{CLASS} || DHTML_CLASS ; my $button_container = '' ; if(exists $setup->{RENDERER}{BUTTON}) { $button_container .= "
\n" ; if($setup->{RENDERER}{BUTTON}{COLLAPSE_EXPAND}) { if($setup->{RENDERER}{COLLAPSED}) { $button_container .= " \n" ; } else { $button_container .= " \n" ; } } if($setup->{RENDERER}{BUTTON}{SEARCH}) { $button_container .= " \n" ; } $button_container .= "
\n\n" ; } my $collapsed = '' ; if($setup->{RENDERER}{COLLAPSED}) { $collapsed = "ul.$class > li > ul {display: none}" ; } my $style = < .$class li {list-style-type:none ; margin:0 ; padding:0 ; line-height: 1em ;} .$class ul {margin:0 ; padding:0 ;} ul.$class {font-family:monospace ; white-space: nowrap ;} $collapsed EOS if(defined $setup->{RENDERER}{STYLE}) { if('SCALAR' eq ref $setup->{RENDERER}{STYLE}) { ${$setup->{RENDERER}{STYLE}} = $style ; $style = '' ; } else { $style = $setup->{RENDERER}{STYLE} ; } } $style = '' if(exists $setup->{RENDERER}{NO_STYLE}) ; $perl_size = "<$perl_size>" if $perl_size ne '' ; my $header = <
  • $title [$td_address] $perl_size $perl_address EOH $setup->{RENDERER}{TABULATION} = 2 , push @{$setup->{RENDERER}{NODES}{A_IDS}}, "\"a_${uuuid}_ROOT\""; push @{$setup->{RENDERER}{NODES}{COLLAPSABLE_IDS}}, "\"c_${uuuid}_ROOT\"" ; $setup->{RENDERER}{PREVIOUS_ADDRESS} = "c_${uuuid}_ROOT" ; $uuuid++ ; return($style . $button_container . $header) ; } #------------------------------------------------------------------------------------------- sub RenderDhtmlNode { my ( $element , $level , $is_terminal , $previous_level_separator , $separator , $element_name , $element_value , $td_address , $address_link , $perl_size , $perl_address , $setup ) = @_ ; # HTMLify args my $glyph = '' ; unless ($setup->{RENDERER}{NO_GLYPH}) { $glyph = $previous_level_separator. $separator ; $glyph =~ s/ / /g ; } $perl_size = "<$perl_size>" if $perl_size ne '' ; if($element_value ne '') { $element_value =~ s/(<|>|&|\'|\"|\ )/$ascii_to_html{$1}/eg ; $element_value = " = $element_value" ; } #setup my $tabulation = $setup->{RENDERER}{TABULATION} ; my $class = $setup->{RENDERER}{CLASS} || DHTML_CLASS ; my $node = '' ; # HTML list formating if($level > $setup->{RENDERER}{PREVIOUS_LEVEL}) { $node = ' ' x $tabulation . "
      \n" ; $tabulation++ ; } else { if($level < $setup->{RENDERER}{PREVIOUS_LEVEL}) { for (my $i = 0 ; $i < $setup->{RENDERER}{PREVIOUS_LEVEL} - $level ; $i++) { $tabulation-- ; $node .= ' ' x $tabulation . "
    \n" ; $tabulation-- ; $node .= ' ' x $tabulation . "
  • \n" ; } } } # keep nodes id for search push @{$setup->{RENDERER}{NODES}{A_IDS}}, "\"a_${uuuid}_$td_address\""; if($is_terminal) { my $list_format_head = ' ' x $tabulation . "
  • " ; my $id = "" ; my $name_value = " $glyph$element_name$element_value" ; my $address = '' ; if($setup->{DISPLAY_ADDRESS}) { $address .= " [$td_address" ; $address .= " -> $address_link" if(defined $address_link) ; $address .= " ]" ; } my $perl_data = " $perl_size $perl_address" ; my $list_format_foot = "
  • " ; $node .= $list_format_head . $id . $name_value . $address . $perl_data . $list_format_foot . "\n" ; } else { if($setup->{RENDERER}{BUTTON}{COLLAPSE_EXPAND}) { push @{$setup->{RENDERER}{NODES}{COLLAPSABLE_IDS}}, "\"c_${uuuid}_$td_address\"" ; } my $list_format_head = (' ' x $tabulation) . "
  • \n" ; $tabulation++ ; my $alignment = ' ' x $tabulation ; my $id_and_click_head = "" ; my $name_value = "$glyph$element_name$element_value" ; my $id_and_click_foot = "" ; my $address = $setup->{DISPLAY_ADDRESS} ? "[$td_address] " : ''; my $perl_data = " $perl_size $perl_address" ; $node .= $list_format_head . $alignment . $id_and_click_head . $name_value . $id_and_click_foot . $address . $perl_data . "\n"; } # setup $setup->{RENDERER}{TABULATION} = $tabulation ; $setup->{RENDERER}{PREVIOUS_LEVEL} = $level ; $setup->{RENDERER}{PREVIOUS_ADDRESS} = "c_${uuuid}_$td_address" ; $uuuid++ ; return($node) ; } #------------------------------------------------------------------------------------------- sub RenderDhtmlEnd { my $setup = shift ; unless(exists $setup->{RENDERER}{BUTTON}) { "
  • \n\n" ; } else { my $a_ids = join "\n\t\t, ", @{$setup->{RENDERER}{NODES}{A_IDS}} ; my $collapsable_ids = join "\n\t\t\t\t, ", @{$setup->{RENDERER}{NODES}{COLLAPSABLE_IDS}} ; my $collapsed = 0 ; $collapsed++ if($setup->{RENDERER}{COLLAPSED}) ; my $class = $setup->{RENDERER}{CLASS} || DHTML_CLASS ; < EOS } } #------------------------------------------------------------------------------------------- 1 ; __END__ =head1 NAME Data::TreeDumper::Renderer::DHTML - DHTML renderer for B =head1 SYNOPSIS use Data::TreeDumper ; #------------------------------------------------------------------------------- my $style ; my $body = DumpTree ( GetData(), 'Data' , DISPLAY_ROOT_ADDRESS => 1 , DISPLAY_PERL_ADDRESS => 1 , DISPLAY_PERL_SIZE => 1 , RENDERER => { NAME => 'DHTML' , STYLE => \$style , BUTTON => { COLLAPSE_EXPAND => 1 , SEARCH => 1 } } ) ; print < Data $style $body

    Valid HTML 4.01!

    EOT =head1 DESCRIPTION Simple DHTML renderer for B. Thanks to Stevan Little author of Tree::Simple::View for giving me the idea and providing some code I could snatch. =head1 EXAMPLE Check B for a complete example of two structure dumps within the same HTML file. =head1 OPTIONS =head2 Style CSS style is dumped to $setup->{RENDERER}{STYLE} (a ref to a scalar) if it exists. This allows you to collect all the CSS then output it at the top of the HTML code. my $style ; my $body = DumpTree ( ... , RENDERER => { NAME => 'DHTML' , STYLE => \$style } ) ; {RENDERER}{NO_STYLE} removes style section generation. This is usefull when you defined your styles by hand. my $style ; my $body = DumpTree ( ... , RENDERER => { NAME => 'DHTML' , NO_STYLE => 1 } ) ; =head2 Class The output will use class 'data_tree_dumper_dhtml' for
  • and
      . The class can be renamed with the help of {RENDERER}{CLASS}. This allows you to dump multiple data structures and display them with a diffrent styles. my $style ; my $body = DumpTree ( ... , RENDERER => { NAME => 'DHTML' , CLASS => 'my_class_name' } ) ; =head2 Glyphs B outputs the tree lines as ASCII text by default. If {RENDERER}{NO_GLYPH} and RENDERER}{NO_STYLE} are defined, no lines are output and the indentation will be the default
    • style. If you would like to specify a specific style for your tree dump, defined you own CSS and set the appropriate class through {RENDERER}{CLASS}. =head2 Expand/Collapse Setting {RENDERER}{COLLAPSED} to a true value will display the tree collapsed. this is false by default. $setup->{RENDERER}{COLLAPSED}++ ; If {RENDERER}{BUTTON}{COLLAPSE_EXPAND} is set, the rendered will add a button to allow the user to collapse and expand the tree. $setup->{RENDERER}{BUTTON}{COLLAPSE_EXPAND} =head2 Search If {RENDERER}{BUTTON}{SEARCH} is set, the rendered will add a button to allow the user to search the tree. This is a primitive search and has no other value than for test. =head1 Bugs I'll hapilly hand this module over to someone who knows what he does :-) Check the TODO file. =head1 EXPORT None =head1 AUTHORS Khemir Nadim ibn Hamouda. Staffan Maahlén. Copyright (c) 2003 Nadim Ibn Hamouda el Khemir and Staffan Maahlén. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perlitself. If you find any value in this module, mail me! All hints, tips, flames and wishes are welcome at . =head1 SEE ALSO B. =cut