############################################################################### # $Id$ # # Description : Module for building tabbed menus for HTML pages. # # SBEAMS is Copyright (C) 2000-2005 Institute for Systems Biology # This program is governed by the terms of the GNU General Public License (GPL) # version 2 as published by the Free Software Foundation. It is provided # WITHOUT ANY WARRANTY. See the full description of GPL terms in the # LICENSE file distributed with this software. # ############################################################################### package SBEAMS::Connection::TabMenu; use strict; use SBEAMS::Connection::DataTable; use SBEAMS::Connection::Log; use SBEAMS::Connection::Settings qw($HTML_BASE_DIR); use overload ( '""', \&asHTML ); my $log = SBEAMS::Connection::Log->new(); ##### Public Methods ########################################################### # # Module provides interface to build a set of tabbed menus for a web page. # If you create and optionally add content to a TabMenu object, and then refer # to it in double quotes, it will stringify using the asHTML method. This # makes it easy to print within any sort of printing block # # For further information, please try perldoc `TabMenu.pm` #+ # Constructor method. # # narg cgi $q/$cgi object from page, needed to extract tab/url. (REQ) # narg activeColor bgcolor for active tab, defaults to gray # narg inactiveColor bgcolor for inactive tab, defaults to light gray # narg atextColor Color of text in active tab, default black # narg itextColor Color of text in inactive tab, default black # narg isSticky If true, pass thru cgi params, else delete # narg boxContent If true, draw box around content (if any) # narg maSkin If true, reoverload stringify to point at &asMA_skin #- sub new { my $class = shift; my $this = { activeColor => 'BBBBBB', # dark grey inactiveColor => 'DEDEDE', # light grey hoverColor => 'BBCCBB', # greenish atextColor => '000000', # black itextColor => '000000', # black isSticky => 0, boxContent => 1, maSkin => 0, paramName => '_tab', @_, _tabIndex => 0, _tabs => [ 'placeholder' ] }; for ( qw(cgi) ) { die ( "Required argument $_ missing" ) unless defined $this->{$_}; } my $cgi = $this->{cgi}; # cache tab number (if any). Delete from url to avoid collisions. $this->{_currentTab} = $cgi->param( $this->{paramName} ); $cgi->delete( $this->{paramName} ) if $this->{_currentTab}; # Cache abs and abs_query urls my @urlparams = ( -absolute => 1 ); $this->{_absURL} = $cgi->url( @urlparams ); # Do we want to include current params? push @urlparams, -query => 1; $this->{_absQueryURL} = $cgi->url( @urlparams ); #### If there's a password in here, strip it out! $this->{_absQueryURL} =~ s/password=.*?;/password=xxx;/; if ( $this->{maSkin} ) { eval 'use overload \'""\' => \&asMA_HTML'; } # Objectification. bless $this, $class; # Allow constructor to define some tabs if ( ref( $this->{labels} ) eq 'ARRAY' ) { foreach( @{$this->{labels}} ) { $this->addTab( label => $_ ); } } return $this; } #+ # settor method for current tab. Required if user allows cgi parameters to be # processed before creating tab object. # - sub setCurrentTab { my $this = shift; my %args = @_; if ( $args{currtab} ) { $this->{_currentTab} = $args{currtab}; } } #+ # accessor method for tabnum # - sub getCurrentTab { my $this = shift; return ( $this->{_currentTab} ); } #+ # stub method, for one day allowing the corners of the tabs to be rounded # - sub setRounded { my $this = shift; $this->{_rounded} = 1; } #+ # returns number of defined tabs # - sub getNumTabs { my $this = shift; # Due to starting the tabs at 1, there is an offset. return ( scalar( @{$this->{_tabs}} ) - 1 ); } #+ # Set active tab background color # narg color Color for background #- sub setActiveColor { my $this = shift; my %args = @_; if ( $args{color} ) { $this->{activeColor} = $args{color}; } } #+ # Set inactive tab background color # narg color Color for background #- sub setInactiveColor { my $this = shift; my %args = @_; if ( $args{color} ) { $this->{inactiveColor} = $args{color}; } } #+ # Set hover color for mouseover on CSS tabs # narg color Color for hover #- sub setHoverColor { my $this = shift; my %args = @_; if ( $args{color} ) { $this->{hoverColor} = $args{color}; } } #+ # Set active tab text color # narg color Color for text #- # sub setAtextColor { my $this = shift; my %args = @_; if ( $args{color} ) { $this->{atextColor} = $args{color}; } } #+ # Set inactive tab text color # narg color Color for text #- sub setItextColor { my $this = shift; my %args = @_; if ( $args{color} ) { $this->{itextColor} = $args{color}; } } #+ # creates new DataTable for menu display. #- sub getTable { my $this = shift; # tab menu is at its heart just a table... $this->{_table} = SBEAMS::Connection::DataTable->new( WIDTH => '100%', BORDER => 0, CELLSPACING => 0, CELLPADDING => 0 ); } #+ # I'm sure this is useful somehow! #- sub setBoxContent { my $this = shift; $this->{boxContent} = shift; } #+ # get tab content, allows for a crude 'append' #- sub getContent { my $this = shift; return $this->{_content}; } #+ # Add content to tab, useful only if a border is desired. #- sub addContent { my $this = shift; $this->{_content} = shift; } #+ # Set tab to load by default #- sub setDefaultTab { my $this = shift; $this->{_defaultTab} = shift; } #+ # Add a new tab to menuset # @narg label Name to put on tab itself REQ # @narg helptext Optional text to put in 'mouseover' info window. # @narg url Optional URL for this tab, overrides self URL if provided. #- sub addTab { my $this = shift; my %args = @_; for ( qw(label) ) { die ("Missing parameter $_") unless $args{label}; } # Increment the tabIndex $this->{_tabIndex}++; # Which param behaviour do we want. my $url = ( $args{URL} ) ? $args{URL} : # Look for user-defined URL ( $this->{isSticky} ) ? $this->{_absQueryURL} : $this->{_absURL}; $args{url} ||= $url; my $del = ( $args{url} =~ /\?/ ) ? '&' : '?'; $args{url} .= "${del}$this->{paramName}=$this->{_tabIndex}"; push ( @{$this->{_tabs}}, { url => $args{url}, label => $args{label}, helptext => $args{helptext} } ); # return tab number in case it is useful to the caller. return $this->{_tabIndex}; } sub addHRule { my $this = shift; $this->{hrule} = 1; } #+ # returns numeric index of active tab #- sub getActiveTab { my $this = shift; if ( $this->{_currentTab} && $this->{_currentTab} <= $this->{_tabIndex} ) { # If a tab was already selected, it is active return ( $this->{_currentTab} ) } elsif ( $this->{_defaultTab} ) { # Else use default return ( $this->{_defaultTab} ); } else { # Else use first tab return 1; } } #+ # returns name of active tab #- sub getActiveTabName { my $this = shift; my $act = $this->getActiveTab(); return( $this->{_tabs}->[$act]->{label} ) } #+ # Rendering method, returns HTML rendition of tabmenu #- sub asHTML { my $this = shift; return $this->asCSSHTML(); } #+ # Rendering method, returns CSS derived menu look 'n feel. #- sub asCSSHTML { my $this = shift; # Get table for rendering stuff... $this->getTable(); my @tabs = @{$this->{_tabs}}; my @row; my $dtab ||= $this->getActiveTab(); my $list = "\n"; $this->{_table}->addRow ( [ $list] ); $this->{_table}->setCellAttr( ROW => 1, COL => 1, ALIGN => 'CENTER' ); if ( $this->{_content} ) { $this->{_table}->addRow ( [ "
$this->{_content}
" ] ); my $color = ( $this->{boxContent} ) ? $this->{activeColor} : 'WHITE'; $this->{_table}->setCellAttr ( COL => 1, ROW => 2, BGCOLOR => $color ); } $this->setRule() if $this->{hrule}; return ( <<" END" ); $this->{_table} END #return "$this->{_table}"; } sub setRule { my $this = shift; my $cnt = ( $this->getNumTabs() ) * 2 + 5 ; $this->{_table}->addRow ( [ "" ] ); $this->{_table}->setCellAttr ( COL => 1, ROW => 2, COLSPAN => $cnt, BGCOLOR => $this->{activeColor} ); } #+ # Rendering method, returns HTML rendition of tabmenu #- sub asSimpleHTML { my $this = shift; # Get table for rendering stuff... $this->getTable(); my @tabs = @{$this->{_tabs}}; my @row; my $dtab ||= $this->getActiveTab(); for( my $i = 1; $i <= $#tabs; $i++ ) { my $spc = " "; my $color = ( $dtab == $i ) ? $this->{activeColor} : $this->{inactiveColor}; my $htext = ( $tabs[$i]->{helptext} ) ? "TITLE='$tabs[$i]->{helptext}'" : ''; my $link =<<" END"; {textColor}> $tabs[$i]->{label} END unless( $i == 1 ) { push( @row, $spc ); my $col = $#row + 1; $this->{_table}->setCellAttr( COL => $col, ROW => 1, BGCOLOR => $this->{activeColor} ); } push( @row, $link ); my $col = $#row + 1; $this->{_table}->setCellAttr( COL => $col, ROW => 1, BGCOLOR => $color, ALIGN => 'CENTER', NOWRAP => 1 ); } my $cnt = ( $this->getNumTabs() ) * 2 + 5 ; push @row, ' ' x 50; $this->{_table}->addRow ( \@row ); # Add horizontal rule... $this->{_table}->addRow ( [ "" ] ); $this->{_table}->setCellAttr ( COL => 1, ROW => 2, COLSPAN => $cnt, BGCOLOR => $this->{activeColor} ); if ( $this->{_content} ) { $this->{_table}->addRow ( [ "
$this->{_content}
" ] ); my $color = ( $this->{boxContent} ) ? $this->{activeColor} : 'white'; $this->{_table}->setCellAttr ( COL => 1, ROW => 3, COLSPAN => $cnt, BGCOLOR => $color ); } return " $this->{_table} "; #return "$this->{_table}"; } sub asMA_HTML { my $this = shift; # Get table for rendering stuff... $this->getTable(); $this->setActiveColor( color => '#FFCC33' ); $this->setInactiveColor( color => '#224499' ); $this->setAtextColor( color => '#000000' ); $this->setItextColor( color => '#FFFFFF' ); $this->setBoxContent( 0 ); my @tabs = @{$this->{_tabs}}; my @row; my $dtab = $this->getActiveTab(); for( my $i = 1; $i <= $#tabs; $i++ ) { my $spc = " "; my $color = ( $dtab == $i ) ? $this->{activeColor} : $this->{inactiveColor}; my $tcolor = ( $dtab == $i ) ? $this->{atextColor} : $this->{itextColor}; my $htext = ( $tabs[$i]->{helptext} ) ? "TITLE='$tabs[$i]->{helptext}'" : ''; my $link =<<" END"; $tabs[$i]->{label} END unless( $i == 1 ) { push( @row, $spc ); my $col = $#row + 1; $this->{_table}->setCellAttr( COL => $col, ROW => 1, WIDTH => '15' ); } push( @row, $link ); my $col = $#row + 1; $this->{_table}->setCellAttr( COL => $col, ROW => 1, BGCOLOR => $color, ALIGN => 'CENTER', NOWRAP => 1, WIDTH => 95 ); } my $cnt = ( $this->getNumTabs() ) * 2; $this->{_table}->addRow ( \@row ); # Add horizontal rule... $this->{_table}->addRow ( [ "" ] ); $this->{_table}->setCellAttr ( COL => 1, ROW => 2, COLSPAN => $cnt, BGCOLOR => $this->{activeColor} ); if ( $this->{_content} ) { $this->{_table}->addRow ( [ "
$this->{_content}
" ] ); my $color = ( $this->{boxContent} ) ? $this->{activeColor} : 'white'; $this->{_table}->setCellAttr ( COL => 1, ROW => 3, COLSPAN => $cnt, BGCOLOR => $color ); } return " $this->{_table} "; } #+ # Rendering method, returns table as tab-de #- sub asHTMLts { my $this = shift; # Get table for rendering stuff... $this->getTable(); my @tabs = @{$this->{_tabs}}; my @row; my $dtab = $this->getActiveTab(); for( my $i = 1; $i <= $#tabs; $i++ ) { my $color = ( $dtab == $i ) ? $this->{activeColor} : $this->{inactiveColor}; my $link =<<" END";
{textColor}> $tabs[$i]->{label}
END push( @row, $link ); #$this->{_table}->setCellAttr( COL => $i, ROW => 1, BGCOLOR => $color, ALIGN => 'CENTER' ); } my $cnt = $this->getNumTabs() + 1; push @row, ' ' x 50; $this->{_table}->addRow ( \@row ); # Add horizontal rule... $this->{_table}->addRow ( [ "" ] ); $this->{_table}->addRow ( [ "" ] ); $this->{_table}->addRow ( [ "" ] ); $this->{_table}->setCellAttr ( COL => 1, ROW => 2, COLSPAN => $cnt, BGCOLOR => '#FFFF99' ); $this->{_table}->setCellAttr ( COL => 1, ROW => 3, COLSPAN => $cnt, BGCOLOR => $this->{activeColor} ); $this->{_table}->setCellAttr ( COL => 1, ROW => 4, COLSPAN => $cnt, BGCOLOR => '#993399' ); if ( $this->{_content} ) { $this->{_table}->addRow ( [ $this->{_content} ] ); $this->{_table}->setCellAttr ( COL => 1, ROW => 5, COLSPAN => $cnt ); } return "$this->{_table}"; } ##### Private Methods ######################################################### sub _getURL { my $this = shift; # Default my @urlparams = ( -absolute => 1 ); # Do we want to include current params? push @urlparams, -query => 1 if $this->{isSticky}; # This will give an absolute internal url w/ query string. $this->{_url} = $this->{cgi}->url( -absolute => 1, -query => 1 ); $this->{_url} = $this->{cgi}->url( @urlparams ); my $comment =<<' ENDITALL'; my $full_url = $cgi->url(); my $query_url = $cgi->url(-query=>1); #alternative syntax my $relative_url = $cgi->url(-relative=>1); my $absolute_url = $cgi->url(-absolute=>1); my $url_with_path = $cgi->url(-path_info=>1); my $url_with_path_and_query = $cgi->url(-absolute=>1,-query=>1); my $netloc = $cgi->url(-base => 1); # cache invocation url FULL: $full_url QUER: $query_url REL: $relative_url ABS: $absolute_url URL_P: $url_with_path URL_PQ: $url_with_path_and_query BASE: $netloc SELF: $this->{_self_url}; END ENDITALL } 1; __END__ =head1 NAME: SBEAMS::Connection::TabMenu, sbeams HTML page tabbed menus widget =head1 SYNOPSIS Module provides interface to build a set of tabbed menus for a web page. If you create and optionally add content to a TabMenu object, and then refer to it in double quotes, it will stringify using the asHTML method. This makes it easy to print within any sort of printing block =head1 USAGE use SBEAMS::Connection::TabMenu; my $tabmenu = SBEAMS::Connection::TabMenu->new( cgi => $q, ); $tabmenu->addTab( label => 'Current Project', helptext => 'View details of current Project' ); $tabmenu->addTab( label => 'My Projects', helptext => 'View all projects owned by me' ); $tabmenu->addTab( label => 'Recent Resultsets', helptext => 'View recent SBEAMS resultsets' ); $tabmenu->addTab( label => 'Accessible Projects', helptext => 'View projects I have access to' ); my $content; if ( $tabmenu->getActiveTabName() eq 'Recent Resultsets' ){ $content = $sbeams->getRecentResultsets() ; } elsif ( $tabmenu->getActiveTabName() eq 'Current Project' ){ $content = $sbeams->getProjectDetailsTable( project_id => $project_id ); } elsif ( $tabmenu->getActiveTab() == 2 ){ $content = $sbeams->getProjectsYouOwn(); } elsif ( $tabmenu->getActiveTab() == 4 ){ $content = $sbeams->getProjectsYouHaveAccessTo(); } # Add content to tabmenu (if desired). $tabmenu->addContent( $content ); # The stringify method is overloaded to call the $tabmenu->asHTML method. This simplifies printing the object in a print block. print "$tabmenu"; # This is completely equivalent: # print $tabmenu->asHTML(); =head2 Constructor arguements =head3 cgi $q/$cgi object from page, needed to extract tab/url. (REQ) =head3 activeColor bgcolor for active tab, defaults to gray =head3 inactiveColor bgcolor for inactive tab, defaults to light gray =head3 atextColor Color of text in active tab, default black =head3 itextColor Color of text in inactive tab, default black =head3 isSticky If true, pass thru cgi params, else delete =head3 boxContent If true, draw box around content (if any) =head3 maSkin If true, reoverload stringify to point at &asMA_skin =cut