NAME
   CGI::Widget::Tabs - Create tab widgets in HTML

SYNOPSIS
       use CGI::Widget::Tabs;
       my $tab = CGI::Widget::Tabs->new;

       use CGI;
       my $cgi = CGI->new;            # interface to the query params

       $tab->headings(@titles);       # e.g. qw/Drivers Cars Courses/
       $tab->default("Courses");      # the default active tab
       $tab->force_active("Courses"); # forceably make this the active tab
       $tab->active;                  # the currently active tab
       $tab->class("my_tab");         # the CSS class to use for markup
       $tab->cgi_object($cgi);        # the object holding the query params
       $tab->cgi_param("t");          # the CGI query parameter to use
       $tab->drop_params("ays");      # do NOT pass on "Are You Sure?" answers
       $tab->wrap(4);                 # wrap after 4 headings...
       $tab->indent(1);               # ...and add indentation
       $tab->render;                  # the resulting HTML code
       $tab->display;                 # same as `print $tab->render'


       $h = $tab->heading;               # new OO heading for this tab
       $h->text("TV Listings");          # heading text
       $h->key("tv");                    # key identifying this heading
       $h->raw(1);                       # switch off HTML encoding
       $h->url("whatsontonight.com");    # redirect URL for this heading
       $h->class("red");                 # this heading has it's own class

       # See the EXAMPLE section for a complete example

DESCRIPTION
 Introduction
   CGI::Widget::Tabs lets you simulate tab widgets in HTML. You could
   benefit from a tab widget if you want to serve only one page. Depending
   on the tab selected you fetch and display the underlying data. There are
   three main reasons for taking this approach:

   1. For the end user not to be directed to YAL or YAP (yet another link /
   yet another page), but keep it all together: The single point of entry
   paradigm.

   2. As a consequence the end user deals with a more consistent and
   integrated GUI. This will give a better "situational awareness" within
   the application.

   3. For the Perl hacker to handle multiple related data sources within
   the same script environment.

   As an example the following tabs could be used on a web page for
   someone's spotting hobby:

         __________      __________      __________
        /  Planes  \    /  Trains  \    / Classics \
   ------------------------------------------------------
            _________
           /  Bikes  \
   ------------------------

   As you can see, the headings wrap at three and a small indentation is
   added to the start of the next row. The nice thing about
   CGI::Widget::Tabs is that the tabs know their internal state. So you can
   ask a tab for instance which heading has been clicked by the user. This
   way you get instant feedback.

 "Hey Gorgeous!"
   Of course tabs are useless if you can't "see" them. Without proper make
   up they print as ordinary text. So you really need to fancy them up with
   some eye candy. The designed way is that you provide a CSS style sheet
   and have CGI::Widget::Tabs use that. See the class() method for how to
   do this.

EXAMPLE
   Before digging into the API and all accessor methods, this example will
   illustrate how to implement the spotting page from above. So you have
   something to start with. It will give you enough clues to get on the
   road quickly. The following code is a simple but complete example. Copy
   it and run it through the webservers CGI engine. (For a even more
   complete and useful demo with multiple tabs, see the file tabs-demo.pl
   in the CGI::Widget::Tabs installation directory.) To fully appreciate
   it, it would be best to run it in a performance environment, like
   mod_perl or SpeedyCGI.

       #! /usr/bin/perl -w

       use CGI::Widget::Tabs;
       use CGI;

       print <<EOT;
       Content-Type: text/html;

       <head>
       <style type="text/css">
       table.tab   { border-bottom: solid thin #C0D4E6; text-align: center }
       td.tab      { padding: 2 12 2 12; width: 80; background-color: #FAFAD2 }
       td.tab_actv { padding: 2 12 2 12; width: 80; background-color: #C0D4E6 }
       td.tab_spc  { width: 5 }
       td.tab_ind  { width: 15 }
       </style></head>
       <body>
       EOT

       my $cgi = CGI->new;
       my $tab = CGI::Widget::Tabs->new;
       $tab->cgi_object($cgi);
       $tab->headings( qw/Planes Traines Classics Bikes/ );
       $tab->wrap(3);
       # $tab->wrap(1);    # |uncomment to see the effect of
       # $tab->indent(0);  # |wrapping at 1 without indentation
       $tab->default("Traines");
       $tab->display;
       print "<br>We now should run some intelligent code ";
       print "to process <strong>", $tab->active, "</strong><br>";
       print "</body></html>";

PUBLIC INTERFACE
 Public Class Interface
  new
     new()

   Creates and returns a new CGI::Widget::Tabs object. new() does not take
   any arguments.

 Public Object Interface
  active
    active()

   Returns a string indicating the current active tab heading. This is (in
   order of precedence) the heading set by force_active(), the heading
   being clicked on, the default heading, or the first in the list. The
   string value will either be the heading key or the heading text,
   depending on if you chose to use keys. Example:

       if ( $tab->active() eq "Trains" ) {  # heading text only

       if ( $tab->active() eq "-t" ) {      # key value ISO heading text

  cgi_object
    cgi_object(OBJECT)

   Sets/returns the CGI or CGI::Minimal object. If the optional argument
   OBJECT is given, the CGI object is set, otherwise it is returned.
   CGI::Widget::Tabs uses this object internally to process the CGI query
   parameters. If you want you can use some other CGI object handler.
   However such an object handler must provide a param() method with
   corresponding behaviour as do CGI or CGI::Minimal. Note that currently
   only CGI and CGI::Minimal have been tested. Example:

       # set
       my $cgi = CGI::Minimal->new;
       $tab->cgi_object($cgi);

       # get
       my $cgi = $tab->cgi_object;

  cgi_param
     cgi_param(STRING)

   Sets/returns the CGI query parameter. This parameter identifies the tab
   in the CGI query string (the funny part of the URL with the ? = & #
   characters). If the optional argument STRING is given, the query
   parameter is set. Otherwise it is returned. Usually you can leave this
   untouched. In that case the default parameter "tab" is used. You will
   need to set this if you have more CGI query parameters on the URL with
   "tab" already being taken. Another situation is if you use multiple tab
   widgets on one page. They both would use "tab" by default causing
   conflicts. Example:

      # Lets paint a fruit tab and a vegetable tab
      my $fruits_tab = CGI::Widget::Tabs->new;
      my $vegies_tab = CGI::Widget::Tabs->new;

      # this is our link with the outside world
      my $cgi = CGI::Minimal->new;
      $fruits_tab->cgi_object($cgi);
      $vegies_tab->cgi_object($cgi);

      # In the CGI params collection the first is
      # identified by 'ft' and the second by 'vt'
      $fruits_tab->cgi_param("ft");
      $vegies_tab->cgi_param("vt");

  drop_params
     drop_params(LIST)

   Sets/retrieves the list of CGI parameters to be dropped from the
   parameter list. If the optional argument LIST is given the list is set,
   otherwise it is retrieved. Suppose you have clicked "Yes" to some "Are
   you sure?" question. You certainly want that question to be asked every
   time, right? Especially if the actions that go with it are destructive.
   If you did NOT specify the parameter to be dropped, "Yes" would have
   been silently passed on to the parameter list. That would effectively
   preset "Are you sure" with "Yes" causing disastrous results. Examples:

       $tab->drop_params("ays");  # drop the "Are you sure" param

  class
     class(STRING)

   Sets/returns the name of the CSS class used for the tabs markup. If the
   optional argument STRING is given the class is set, otherwise it is
   returned. If not set, the widget will be based on the class "tab". In
   the accompanying style sheet, there are five class elements you need to
   provide:

   1. A table element for containment of the entire tab widget
   2. A td element for a normal tab
   3. A td element for the active tab
   4. A td element for the spacers
   5. A td element for the indentation (if needed)

   The class names of these elements are directly borrowed from the class()
   method. The td elements for the active tab, the spacers and the
   indentations are suffixed with "_actv", "_spc" and "_ind" respectively.
   For instance, if you'd run

       $tab->class("my_tab");

   then the elements look like:

       <table class="my_tab">    # the entire table
       <td class="my_tab">       # normal tab
       <td class="my_tab_actv">  # active tab
       <td class="my_tab_spc">   # spacer
       <td class="my_tab_ind">   # indentation

   If you don't wrap headings, then ofcourse you won't need to specify the
   indentation td's. By the way, the indentation will usually look most
   natural if it has the same width as the spacers or a multiple thereof.
   Look at the example in the EXAMPLE section to see how this all works
   out.

  default
    default(STRING)

   Overrides which heading is the default. Normally CGI::Widget::Tabs will
   make the first heading active. Use the default() method if you want to
   deviate from this. The optional argument STRING must either be the
   heading key or the heading text, depending on how you chose to
   initialize the headings. Example:

       # Make the "Trains" heading the default active one.
       $tab->default("Trains");

       # ...or perhaps...
       $tab->default("-t");

  display
     display()

   Renders the tab widget and prints the resulting HTML to the default
   output handle (usually STDOUT). Example:

       $tab->display;       # this is the same as...

       print $tab->render;  # ...but saves a few keystrokes

   See also the render() method.

  force_active
    force_active(STRING)

   Forces the activation of a specific tab identified by it's heading text
   or key. This is useful if you have an application which must show a
   certain tab after doing someting. Or if you're paranoid and you've been
   given a CGI query string which you don't trust. In both cases you can
   make sure the tab of your preference is activated. Example:

       $tab->force_active("Trains");  # heading text only

       $tab->force_active("-t");      # key

       $tab->force_active(undef);     # forget all about it

  heading
     heading()

   Creates, appends and returns a new heading. The return value will always
   be an OO heading object. Example:

       my $h = $tab->heading();

   In general you will use OO headings if the headings() method is not
   flexible enough. For trivial applications the headings() method mostly
   suffices. Look at section PROPERTIES OF OO HEADINGS for more information
   on OO headings.

  headings
     headings(LIST)

   Sets/returns the tab headings. Without arguments the currently defined
   headings are returned. If no headings are defined, the empty list is
   returned. Any returned heading will always be an OO heading, regardless
   of if and how the initializing LIST argument is used. Look at section
   PROPERTIES OF OO HEADINGS for more info on how to deal with OO headings.

   The optional LIST argument is a short-cut to the OO headings interface.
   The elements of LIST can take various forms. Let's take a moment to take
   a close look at the headings of a tab. Tab headings are the things that
   --from human perspective-- identify a tab page. Observe the spotting
   example above. Here the different tab pages are identified by the
   strings "Planes", "Trains", "Classics" and "Bikes". They form the
   heading for each seperate tab. The LIST elements can be used to preset
   these tab headings.

   An element of LIST can be any one of:

   *   a string. E.g.:

           qw/Planes Trains Classics Bikes/

       This is the simplest initializer. In the spotting example the four
       tabs headings are easily created by feeding these words as a list to
       the headings() method. And then you are almost done: the headings
       can be displayed and each heading gets it's own self referencing
       URL.

   *   a key/value pair. E.g.:

           ( -p => "Planes",
             -t => "Trains",
             -c => "Classics,
             -b => "Bikes" )

       For trivial CGI::Widget::Tabs applications, the k/v pairs are the
       ones you will probably use the most. They come in handy because you
       don't need to check the value returned by active() against very long
       words. Even better, if you change the tab headings (upper/lower
       case, typo's) but use the same keys you don't need to change your
       code. So it is less error prone. As a pleasant side effect, the
       URL's get to be significantly shorter. Do notice that the keys want
       to be unique. Keys in a k/v list are not at all magical. You can
       choose any string you like with the provision that they start with
       the '-' (hyphen) sign. The starting '-' of a list entry is what
       triggers CGI::Widget::Tabs to decide this is a k/v entry. Single or
       dual character strings tend to be the most convenient keys.

   *   a hash

       This use of the headings() method will clutter up your code. The
       hash tries to mimic and encapsulate all OO accessor methods. If
       think you need an initializer hash, you probably want OO headings.
       Use it only if you must. If you can stick with the strings or k/v
       pairs. That said, the hash keys are the named equivalents of the OO
       heading properties. E.g.:

           ( { text  => "Planes",
               key   => "p",
               url   => "www.aviation-mag.com",
               class => "heavens_blue",
               raw   => 0 },

   You can mix these types in any way you like. The various types will be
   translated on the fly to OO headings and then processed. Thus you can
   safely say:

       $tab->headings( "Plaines",
                       -t => "Traines",
                       { text => "Classics",
                         key  => "c",
                         ... } )

   Just as the hash initializer, this use does clutter up your code. The
   reason is that different concepts of information are piled up on one big
   heep. You will need to scrutinize the code to understand what it is
   going on. Although it is supported you should refrain yourself from
   making use of these combinations.

   As a summary, here are a three examples of the headings() method for the
   spotting page.

       # Example 1: Set the headings with a list of strings
       my $tab = CGI::Widget::Tabs->new();
       $tab->headings( qw/Planes Trains Classics Bikes/ );

       # Example 2: Set the headings with a list of k/v pairs
       my $tab = CGI::Widget::Tabs->new();
       $tab->headings( -p => "Planes",
                       -t => "Trains",
                       -c => "Classics,
                       -b => "Bikes" );

       # Example 3: Isolate the "Classics" heading
       my $h = ($tab->headings)[2];

   Note that these few statements provide almost enough logic to generate
   the HTML for the tab widget!

  indent
     indent(BOOLEAN)

   Sets/returns the indentation setting. Without arguments the current
   setting is returned. indent() specifies if indentation should be added
   to the next row when the headings get wrapped. indent() is a toggle. By
   default indent() is set to TRUE. You must explicitely switch it off for
   the desired effect. The optional argument BOOLEAN can be any argument
   evaluating to a logical value.

   The purpose of swithing off indentation is to simulate a vertical menu.
   In the spotting example, running

       $tab->wrap(1);
       $tab->indent(0);

   would result in something like:

         __________
        |  Planes  |
       --------------
         __________
        |  Trains  |
       --------------
         __________
        | Classics |
       --------------
         __________
        |  Bikes   |
       --------------

   You probably need to tweak your style sheet to have it look nicely.

  render
     render()

   Renders the tab widget and returns the resulting HTML code. This is
   useful if you need to print the tab to a different file handle. Another
   use is if you want to manipulate the HTML. For instance to insert
   session id's or the like. See the class() method and the EXAMPLE section
   somewhere else in this document to see how you can influence the markup
   of the tab widget. Example:

       my $html = $tab->render;
       print HTML $html;  # there's a session id filter behind HTML

  wrap
     wrap(NUMBER)

   Sets or returns the wrap setting. Without arguments the current wrap
   setting is returned. If the argument NUMBER is given the headings will
   wrap to the next row after NUMBER headings. By default headings are not
   wrapped.

INTERNALS
 Private Class Methods
  _link
    link($text, $href)

   Returns a HTML 'a' tag pair linking to $href with text $text

INSTALLATION
   This module uses Module::Build for its installation. To install this
   module type the following:

     perl Build.PL
     ./Build
     ./Build test
     ./Build install

   If you do not have Module::Build type:

     perl Makefile.PL

   to fetch it. Or use CPAN or CPANPLUS and fetch it "manually".

DEPENDENCIES
   This module requires these other modules and libraries:

    Carp
    CGI  or  CGI::Minimal or another CGI   "object broker" with   a similar param()
    method
    HTML::Entities
    Test::More
    URI::Escape

   Test::More is only required for testing purposes.

   This module has these optional dependencies:

    File::Find::Rule
    Pod::Coverage
    Test::Pod (0.95 or higher)
    Test::Signature

   These are both just requried for testing purposes.

   Also required, a CSS stylesheet for the tabs markup

TODO
   Just because these items are in the todo list, does not mean they will
   actually be done. If you think one of these would be helpful say so -
   and it will then move up on my priority list.

   *   Re work the way Headings work. Do not assume that a heading wants to
       be wrapped into an a href tag. It might be javascript instead

   *   Provide a hash lookup as a replacement mechanism for $cgi->params()
       for those who don't use CGI or CGI::Minimal

   *   Add support for heading images instead of text

   *   Consider replacing some/all of the hand crafted get set methods with
       use of Class::MethodMaker

   *   Consider using Test::More in 003_main.t

   Patches always welcome.

BUGS
   As a side effect, the CGI query parameter to identify the tab (see the
   cgi_param() method) is always moved to the end of the query string.

   To report a bug or request an enhancement use CPAN's excellent Request
   Tracker.

CONTRIBUTIONS
   I would appreciate receiving your CSS style sheets used for the tabs
   markup. Especially if you happened to be professionally concerned with
   markup and layout. For techies like us it is not always easy to see what
   goes and what doesn't. If you send in a nice one, I will gladly bundle
   it with the next release.

ACKNOWLEDGEMENTS
   Bodo Eing <[email protected]>
   Bernie Ledwick <[email protected]>
   Bernhard Schmalhofer <[email protected]>

AUTHOR
   Koos Pol <[email protected]>

MAINTAINER
   Sagar R. Shah

SEE ALSO
   CGI, CGI::Minimal, CSS specs: <http://www.w3.org/TR/REC-CSS1>,
   <http://www.w3.org/TR/REC-CSS2>

COPYRIGHT
   Copyright 2003, Koos Pol, All rights reserved

   Copyright 2003, Sagar R. Shah, All rights reserved

   This program is free software; you can redistribute it and/or modify it
   under the same terms as Perl itself.

   The full text of the license can be found in the LICENSE file included
   with this module.