NAME
Javascript::Menu - a NumberedTree that generates HTML and Javascript code for
a menu.
SYNOPSIS
use Javascript::Menu;
# Give it something to do (example changes the menu's caption):
my $action = sub {
my $self = shift;
my ($level, $unique) = @_;
my $value = $self->getValue;
return "getElementById(caption_$unique).innerHTML='$value'";
};
# Build the tree:
my $menu = Javascript::Menu->convert(tree => $otherTree, action => $action);
my $menu = Javascript::Menu->readDB(source_name => $table, source => $dbh,
action => $action);
my $menu = Javascript::Menu->new(value => 'Please select a parrot',
action => $action);
my $blue = $menu->append('Norwegian Blue');
$blue->append('Pushing up the daisies');
$menu->append('A Snail');
# Print it out as a right-to-left menu:
my $css = $menu->buildCSS($menu->reasonableCSS);
print $cgi->start_html(-script => $menu->baseJS('rtl'),
-style => $css); #CSS plays an important role.
print $tree->getHTML;
DESCRIPTION
Javascript::Menu is an object that helps in creating the HTML, Javascript, and some of the CSS required for a table-based menu. There are a few other modules that deal with menus, But as I browsed through them, I found that none of them exactly fitted my needs. So I designed this module, with the following goals in mind:
- Flexibility
-
The main feature of this module is the ability to supply all nodes or any specific node with a subroutine that is activated in time of the code generation to help decide what the item will do when it is clicked. This allows customisation far beyond associating a link with every item. Multy-level selection menus become very easy to do (and this is, in fact, what I needed when I started writing this).
- I18n
-
Working with i18n (internationalization) can be a big headache. Working with Hebrew (or Arabic) forces you not only to change your charachters, but also to change your direction of writing. I incorporated into this module the ability to produce right-to-left menus and tested it using a legacy ASCII-based encoding (iso-8859-8).
- Object Hierarchy
-
I designed the module to work with two other modules of mine, NumberedTree and NumberedTree::DBTree, which simplify the task of building the menu and allow for construction of a menu from database information.
The current version adds support for highlighting the item that's hovered over. You'll find that having made some preliminary steps, like tweaking the CSS to look the way you like it to, the rest is fairly easy.
So, how do we use this module?
What should you expect to see?
The generated menu will be visible as a div that shows the caption line for the menu. As you hover with the mouse over the caption, the main menu will appear under the caption. Hovering over any item with childs will open a sub-menu either to the right or left of the main menu, depending on the direction you chose for the menu. Clicking on any item will hide all menus, leaving only the caption, and fire the action you assigned to the item.
Some naming rules.
The following rules decide on the names (id attributes) of generated HTML elements:
Every generated menu recieves a unique suffix. Let's call this $unique. this is added to the name of every part of the same menu.
Every node has a number, unlike any other node on the same tree. Let's call that $number. For reasons why, see the documentation for NumberedTree or just read on.
The caption line is called caption_$unique, The main menu is called main_$unique, Every sub menu is called s_$number_$unique.
CSS classes
Every part of the menu is associated with a class name, that defines its style (the class attribute). By default the caption's class is 'caption', the main menu's class is 'Mmenu' and sub menus get the class 'Smenu'. If this does not suit you, you are welcome to create the menu with different style associations (see getHTML).
Setting up the supporting code
Javascript::Menu requires some supporting code to work. First, as implied by its name, certain Javascript functions must be available. This is, however, the easiest thing to set up. The code is returned in its entirety, as one gigant multiline string, by the class method baseJS (see below). use this in your head tag, or do like me and dump this to a .js file.
The second thing that needs to be set up is the CSS. except for a few settings, you are pretty free to style the menu as you see fit, but that also means some work for you. The class method baseCSS returns only the basic settings, those you can't change. You must tweak it some more to look good. The class method reasonableCSS returns some example CSS that doesn't look too bad. Again, you should tweak this as described below under buildCSS. Finally, I included a convenience class method called buildCSS that stringifies the data structure supplied by these two functions into valid CSS and also generates extra CSS to deal with the special oddities of Internet Explorer 6.
Building the tree
To get the tree that represents the structure of the menu, you have 3 ways:
- The hard way: Javascript::Menu->new
-
This builds the root node, with your desired value and action (which will be the default for all children of this node). You add nodes with $tree->append, and descend the hierarchy using methods found in the parent class - NumberedTree. For each element you supply the value (what is shown on the screen) and possibly an action.
- The easier way: Javascript::Menu->convert
-
This just takes an existing NumberedTree and blesses it as a Menu, 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..
- A nice shortcut: Javascript::Menu->readDB
-
If you have the module NumberedTree::DBTree (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 :)
But what are these actions and how do I generate them?
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:
A reference to the node being processed, so you can get information on the node via object methods.
The item's level in the hierarchy - the main menu is at level 0, the caption is at level -1.
The menu's unique suffix.
To make an item do nothing except for showing its submenu, use $item->setAction
I18n alert! 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 the USA or England..
Printing the HTML
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.
METHODS
This section only describes methods that are not the same as in NumberedTree. Obligatory arguments are marked.
Constructors
There are three of them:
- new (value => $value, action => $action)
-
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 append method.
- convert (tree => $tree, action => $action)
-
Converts a tree (given in the tree 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 NumberedTree.pm).
As in new, if action is not specified, one will be created for you.
- readDB (source_name => $table, source => $dbh, action => $action);
-
Creates a new menu from a table that contains tree data as specified in NumberedTree::DBTree. Arguments are the same as to new, except for the required source_name, which specifies the name of the table to be read, and source, which is a DBI database handle.
append ($value, $action)
Adds a new child with the value (caption) $value. An action is optional, as described in new. To use the parent node's action on the child node, pass '0' in $action.
getHTML (styles => $styles, caption => 'altCaption', no_ie => true)
This method returns the HTML for a menu whose caption is the node the method was invoked on. The menu's caption will be the root element's value unless the caption argument is given.
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:
$styles = { caption => 'mycap', Mmenu => 'myM', Smenu => 'myS' };
unless you specify the option no_ie as true, items of your menu will be wrapped with anchor tags so the :hover CSS pseudo-class will be aplicable to them even on Internet Explorer 6.
Accessors
Javascript::Menu adds to the methods of its base class the following accessors:
- getUniqueId
-
Returns the unique Id that the menu will recieve when built with this node as root.
- getAction / setAction ($action)
-
gets and sets the item's action. If no action is given to setAction, the default do-nothing action is used.
Class methods
The following class methods help you generate supporting code for your menus:
- baseJS ($rtl)
-
Returns the basic Javascript code for use with this module. If the optional $rtl is true, the code will generate right-to-left menus.
- baseCSS
-
Returns the minimum required CSS for the menu to work properly, as a reference to the data structure described below in buildCSS. It is up to you to add properties to this structure to make your menu look good.
- reasonableCSS
-
Returns the same data structure as in baseCSS, only with more properties. Using the properties provided by this function will result in a black-bordered, blue caption box with white text, and cyan menus with black text. Again, you can tweak this to your satisfaction.
- buildCSS ($css, $no_ie, $no_autolink)
-
Takes a data structure and returns a string with valid CSS you can incorporate into your document.
The data structure is as follows: A main hash with one key for each element of the menu (caption, main menu, sub menus). 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).
Unless $no_ie is true, buildCSS will generate IE6 compatible style for hover classes. This will also generate CSS for links inside the menu. To inhibit that, set $no_autolink to true.
BROWSER COMPATIBILITY
Tested on IE6 and Mozilla 1.4 and worked. If you test it on other browsers, please let me know what is the result.
BUGS
Please report through CPAN: <http://rt.cpan.org/NoAuth/Bugs.html?Dist=NumberedTree> or send mail to <bug-NumberedTree#rt.cpan.org>
SEE ALSO
NumberedTree, NumberedTree::DBTree. For an explanation of i18n and l10n in perl see perlunicode, bytes, Encode, perllocale.
AUTHOR
Yosef Meller, <mellerf@netvision.net.il>
COPYRIGHT AND LICENSE
Copyright 2003 by Yosef Meller
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.