tree menus - urgent help required

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

Post Reply
User avatar
gurjit
Forum Contributor
Posts: 314
Joined: Thu May 15, 2003 11:53 am
Location: UK

tree menus - urgent help required

Post by gurjit »

hi all,

i need to create a tree menu like this:

http://www.hitsoft.ro/treeview/treeview.htm

which is dynamically created from a database which holds tables

country
>location
>school

the above url example does not show how to submit a form with the menu created. i need to capture all the values in that are checked. i want it to work so that:

if a user checks box 'Category 11' all the sub menus 'Category 111' and 'Category 112' auto check.
or
you could check each value seprately.

then i want the user to hit a submit button and capture all the checked values on the next page where i can do a query depending on the values checked.

does anyone have an idea on how i could achieve this?

i need help urgently and desperately. i've been stuck on this for days...searched the web and could'nt find anything. if someone has done this please help.

THANKS IN ADVANCE.
User avatar
liljester
Forum Contributor
Posts: 400
Joined: Tue May 20, 2003 4:49 pm

Post by liljester »

do you have some code you could post?
User avatar
gurjit
Forum Contributor
Posts: 314
Joined: Thu May 15, 2003 11:53 am
Location: UK

Post by gurjit »

i have the following code, at the moment the menu is static i will make it dynamic after i get the static part working.

i need some help in

1. putting a checkbox next to 'location, 'schoola', 'schoolb', 'student names'.
2. when you click the link i want it to open a node rather than go to link test.php
3. i want to submit the values checked, so the entire menu needs to be in a form rather than the bottom bit.


copy and paste the following code in the relevant named file names and run example.php and you will see what i mean by the above.

if you have any better ideas please let me know.



example.php

<?php

require_once('TreeMenu.php');
//require_once('../TreeMenu.php');

$icon = 'php_menu/folder.gif';
$expandedIcon = 'php_menu/folder-expanded.gif';

$menu = new HTML_TreeMenu();

$node1 = new HTML_TreeNode(array('text' => "Country", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon), array('onclick' => "alert('foo'); return false", 'onexpand' => "alert('Expanded')"));
$node1_1 = &$node1->addItem(new HTML_TreeNode(array('text' => "Location", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1 = &$node1_1->addItem(new HTML_TreeNode(array('text' => "Schoola", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1_1 = &$node1_1_1->addItem(new HTML_TreeNode(array('text' => "Micheal Thomas", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1_1 = &$node1_1_1->addItem(new HTML_TreeNode(array('text' => "Kevin Hale", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1 = &$node1_1->addItem(new HTML_TreeNode(array('text' => "Schoolb", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1_1 = &$node1_1_1->addItem(new HTML_TreeNode(array('text' => "Pamela Anderson", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));
$node1_1_1_1 = &$node1_1_1->addItem(new HTML_TreeNode(array('text' => "Kerry Long", 'link' => "test.php", 'icon' => $icon, 'expandedIcon' => $expandedIcon)));


$menu->addItem($node1);


// Create the presentation class
$treeMenu = &new HTML_TreeMenu_DHTML($menu, array('images' => '', 'defaultClass' => 'treeMenuDefault'));
$listBox = &new HTML_TreeMenu_Listbox($menu, array('linkTarget' => '_self'));


?>

<html>
<head>
<style type="text/css">
body {
font-family: Georgia;
font-size: 11pt;
}

.treeMenuDefault {
font-style: italic;
}

.treeMenuBold {
font-style: italic;
font-weight: bold;
}
</style>
<script src="TreeMenu.js" language="JavaScript" type="text/javascript"></script>
</head>
<body>

<script language="JavaScript" type="text/javascript">
<!--
a = new Date();
a = a.getTime();
//-->
</script>

<?$treeMenu->printMenu()?>
<?$listBox->printMenu()?>

<script language="JavaScript" type="text/javascript">
<!--
b = new Date();
b = b.getTime();

document.write("Time to render tree: " + ((b - a) / 1000) + "s");
//-->
</script>
</body>
</html>







treemenu.php


<?php
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003, Richard Heyes, Harald Radi |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@phpguru.org> |
// | Harald Radi <harald.radi@nme.at> |
// +-----------------------------------------------------------------------+
//
// $Id: TreeMenu.php,v 1.15 2003/02/21 18:19:39 richard Exp $

/**
* HTML_TreeMenu Class
*
* A simple couple of PHP classes and some not so simple
* Jabbascript which produces a tree menu. In IE this menu
* is dynamic, with branches being collapsable. In IE5+ the
* status of the collapsed/open branches persists across page
* refreshes.In any other browser the tree is static. Code is
* based on work of Harald Radi.
*
* Usage.
*
* After installing the package, copy the example php script to
* your servers document root. Also place the TreeMenu.js and the
* images folder in the same place. Running the script should
* then produce the tree.
*
* Thanks go to Chip Chapin (http://www.chipchapin.com) for many
* excellent ideas and improvements.
*
* @author Richard Heyes <richard@php.net>
* @author Harald Radi <harald.radi@nme.at>
* @access public
* @package HTML_TreeMenu
*/

class HTML_TreeMenu
{
/**
* Indexed array of subnodes
* @var array
*/
var $items;

/**
* Constructor
*
* @access public
*/
function HTML_TreeMenu()
{
// Not much to do here :(
}

/**
* This function adds an item to the the tree.
*
* @access public
* @param object $node The node to add. This object should be
* a HTML_TreeNode object.
* @return object Returns a reference to the new node inside
* the tree.
*/
function &addItem(&$node)
{
$this->items[] = &$node;
return $this->items[count($this->items) - 1];
}

/**
* Import method for creating HTML_TreeMenu objects/structures
* out of existing tree objects/structures. Currently supported
* are Wolfram Kriesings' PEAR Tree class, and Richard Heyes' (me!)
* Tree class (available here: http://www.phpguru.org/). This
* method is intended to be used statically, eg:
* $treeMenu = &HTML_TreeMenu::import($myTreeStructureObj);
*
* @param array $params An array of parameters that determine
* how the import happens. This can consist of:
* structure => The tree structure
* type => The type of the structure, currently
* can be either 'heyes' or 'kriesing'
* nodeOptions => Default options for each node
*
* @return object The resulting HTML_TreeMenu object
*/
function createFromStructure($params)
{
if (!isset($params['nodeOptions'])) {
$params['nodeOptions'] = array();
}

switch (@$params['type']) {

/**
* Wolfram Kriesings' PEAR Tree class
*/
case 'kriesing':
$className = strtolower(get_class($params['structure']->dataSourceClass));
$isXMLStruct = strpos($className,'_xml') !== false ? true : false;

// Get the entire tree, the $nodes are sorted like in the tree view
// from top to bottom, so we can easily put them in the nodes
$nodes = $params['structure']->getNode();

// Make a new menu and fill it with the values from the tree
$treeMenu = new HTML_TreeMenu();
$curNode[0] = &$treeMenu; // we need the current node as the reference to the

foreach ( $nodes as $aNode ) {
$events = array();
$data = array();

// In an XML, all the attributes are saved in an array, but since they might be
// used as the parameters, we simply extract them here if we handle an XML-structure
if ( $isXMLStruct && sizeof($aNode['attributes']) ){
foreach ( $aNode['attributes'] as $key=>$val ) {
if ( !$aNode[$key] ) { // dont overwrite existing values
$aNode[$key] = $val;
}
}
}

// Process all the data that are saved in $aNode and put them in the data and/or events array
foreach ( $aNode as $key=>$val ) {
if ( !is_array($val) ) {
// Dont get the recursive data in here! they are always arrays
if ( substr($key,0,2) == 'on' ){ // get the events
$events[$key] = $val;
}

// I put it in data too, so in case an options starts with 'on' its also passed to the node ... not too cool i know
$data[$key] = $val;
}
}

// Normally the text is in 'name' in the Tree class, so we check both but 'text' is used if found
$data['text'] = $aNode['text'] ? $aNode['text'] : $aNode['name'];

// Add the item to the proper node
$thisNode = &$curNode[$aNode['level']]->addItem( new HTML_TreeNode( $data , $events ) );
$curNode[$aNode['level']+1] = &$thisNode;
}
break;

/**
* Richard Heyes' (me!) Tree class
*/
case 'heyes':
default:
// Need to create a HTML_TreeMenu object ?
if (!isset($params['treeMenu'])) {
$treeMenu = &new HTML_TreeMenu();
} else {
$treeMenu = &$params['treeMenu'];
}

// Loop thru the trees nodes
foreach ($params['structure']->nodes->nodes as $node) {
$tag = $node->getTag();
$parentNode = &$treeMenu->addItem(new HTML_TreeNode(array_merge($params['nodeOptions'], $tag)));

// Recurse ?
if (!empty($node->nodes->nodes)) {
$recurseParams['structure'] = $node;
$recurseParams['nodeOptions'] = $params['nodeOptions'];
$recurseParams['treeMenu'] = &$parentNode;
HTML_TreeMenu::createFromStructure($recurseParams);
}
}
break;

}

return $treeMenu;
}

/**
* Creates a treeMenu from XML. The structure of your XML should be
* like so:
*
* <treemenu>
* <node text="First node" icon="folder.gif" expandedIcon="folder-expanded.gif" />
* <node text="Second node" icon="folder.gif" expandedIcon="folder-expanded.gif">
* <node text="Sub node" icon="folder.gif" expandedIcon="folder-expanded.gif" />
* </node>
* <node text="Third node" icon="folder.gif" expandedIcon="folder-expanded.gif">
* </treemenu>
*
* Any of the options you can supply to the HTML_TreeNode constructor can be supplied as
* attributes to the <node> tag. If there are no subnodes for a particular node, you can
* use the XML shortcut <node ... /> instead of <node ... ></node>. The $xml argument can
* be either the XML as a string, or an pre-created XML_Tree object. Also, this method
* REQUIRES my own Tree class to work (http://phpguru.org/tree.html). If this has not
* been include()ed or require()ed this method will die().
*
* @param mixed $xml This can be either a string containing the XML, or an XML_Tree object
* (the PEAR::XML_Tree package).
* @return object The HTML_TreeMenu object
*/
function createFromXML($xml)
{
if (!class_exists('Tree')) {
die('Could not find Tree class');
}

// Supplied $xml is a string
if (is_string($xml)) {
require_once('XML/Tree.php');
$xmlTree = &new XML_Tree();
$xmlTree->getTreeFromString($xml);

// Supplied $xml is an XML_Tree object
} else {
$xmlTree = $xml;
}

// Now process the XML_Tree object, setting the XML attributes
// to be the tag data (with out the XML tag name or contents).
$treeStructure = Tree::createFromXMLTree($xmlTree, true);
$treeStructure->nodes->traverse(create_function('&$node', '$tagData = $node->getTag(); $node->setTag($tagData["attributes"]);'));


return HTML_TreeMenu::createFromStructure(array('structure' => $treeStructure));
}
} // HTML_TreeMenu


/**
* HTML_TreeNode class
*
* This class is supplementary to the above and provides a way to
* add nodes to the tree. A node can have other nodes added to it.
*
* @author Richard Heyes <richard@php.net>
* @author Harald Radi <harald.radi@nme.at>
* @access public
* @package HTML_TreeMenu
*/
class HTML_TreeNode
{
/**
* The text for this node.
* @var string
*/
var $text;

/**
* The link for this node.
* @var string
*/
var $link;

/**
* The icon for this node.
* @var string
*/
var $icon;

/**
* The icon to show when expanded for this node.
* @var string
*/
var $expandedIcon;

/**
* The css class for this node
* @var string
*/
var $cssClass;

/**
* The link target for this node
* @var string
*/
var $linkTarget;

/**
* Indexed array of subnodes
* @var array
*/
var $items;

/**
* Whether this node is expanded or not
* @var bool
*/
var $expanded;

/**
* Whether this node is dynamic or not
* @var bool
*/
var $isDynamic;

/**
* Should this node be made visible?
* @var bool
*/
var $ensureVisible;

/**
* The parent node. Null if top level
* @var object
*/
var $parent;

/**
* Javascript event handlers;
* @var array
*/
var $events;

/**
* Constructor
*
* @access public
* @param array $options An array of options which you can pass to change
* the way this node looks/acts. This can consist of:
* o text The title of the node, defaults to blank
* o link The link for the node, defaults to blank
* o icon The icon for the node, defaults to blank
* o expandedIcon The icon to show when the node is expanded
* o class The CSS class for this node, defaults to blank
* o expanded The default expanded status of this node, defaults to false
* This doesn't affect non dynamic presentation types
* o linkTarget Target for the links. Defaults to linkTarget of the
* HTML_TreeMenu_Presentation.
* o isDynamic If this node is dynamic or not. Only affects
* certain presentation types.
* o ensureVisible If true this node will be made visible despite the expanded
* settings, and client side persistence. Will not affect
* some presentation styles, such as Listbox. Default is false
* @param array $events An array of javascript events and the corresponding event handlers.
* Additionally to the standard javascript events you can specify handlers
* for the 'onexpand', 'oncollapse' and 'ontoggle' events which will be fired
* whenever a node is collapsed and/or expanded.
*/
function HTML_TreeNode($options = array(), $events = array())
{
$this->text = '';
$this->link = '';
$this->icon = '';
$this->expandedIcon = '';
$this->cssClass = '';
$this->expanded = false;
$this->isDynamic = true;
$this->ensureVisible = false;
$this->linkTarget = null;

$this->parent = null;
$this->events = $events;

foreach ($options as $option => $value) {
$this->$option = $value;
}
}

/**
* Allows setting of various parameters after the initial
* constructor call. Possible options you can set are:
* o text
* o link
* o icon
* o cssClass
* o expanded
* o isDynamic
* o ensureVisible
* ie The same options as in the constructor
*
* @access public
* @param string $option Option to set
* @param string $value Value to set the option to
*/
function setOption($option, $value)
{
$this->$option = $value;
}

/**
* Adds a new subnode to this node.
*
* @access public
* @param object $node The new node
*/
function &addItem(&$node)
{
$node->parent = &$this;
$this->items[] = &$node;

/**
* If the subnode has ensureVisible set it needs
* to be handled, and all parents set accordingly.
*/
if ($node->ensureVisible) {
$this->_ensureVisible();
}

return $this->items[count($this->items) - 1];
}

/**
* Private function to handle ensureVisible stuff
*
* @access private
*/
function _ensureVisible()
{
$this->ensureVisible = true;
$this->expanded = true;

if (!is_null($this->parent)) {
$this->parent->_ensureVisible();
}
}
} // HTML_TreeNode


/**
* HTML_TreeMenu_Presentation class
*
* Base class for other presentation classes to
* inherit from.
*/
class HTML_TreeMenu_Presentation
{
/**
* The TreeMenu structure
* @var object
*/
var $menu;

/**
* Base constructor simply sets the menu object
*
* @param object $structure The menu structure
*/
function HTML_TreeMenu_Presentation(&$structure)
{
$this->menu = &$structure;
}

/**
* Prints the HTML generated by the toHTML() method.
* toHTML() must therefore be defined by the derived
* class.
*
* @access public
* @param array Options to set. Any options taken by
* the presentation class can be specified
* here.
*/
function printMenu($options = array())
{
foreach ($options as $option => $value) {
$this->$option = $value;
}

echo $this->toHTML();
}
}


/**
* HTML_TreeMenu_DHTML class
*
* This class is a presentation class for the tree structure
* created using the TreeMenu/TreeNode. It presents the
* traditional tree, static for browsers that can't handle
* the DHTML.
*/
class HTML_TreeMenu_DHTML extends HTML_TreeMenu_Presentation
{
/**
* Dynamic status of the treemenu. If true (default) this has no effect. If
* false it will override all dynamic status vars and set the menu to be
* fully expanded an non-dynamic.
*/
var $isDynamic;

/**
* Path to the images
* @var string
*/
var $images;

/**
* Target for the links generated
* @var string
*/
var $linkTarget;

/**
* Whether to use clientside persistence or not
* @var bool
*/
var $userPersistence;

/**
* The default CSS class for the nodes
*/
var $defaultClass;

/**
* Whether to skip first level branch images
* @var bool
*/
var $noTopLevelImages;

/**
* Constructor, takes the tree structure as
* an argument and an array of options which
* can consist of:
* o images - The path to the images folder. Defaults to "images"
* o linkTarget - The target for the link. Defaults to "_self"
* o defaultClass - The default CSS class to apply to a node. Default is none.
* o usePersistence - Whether to use clientside persistence. This persistence
* is achieved using cookies. Default is true.
* o noTopLevelImages - Whether to skip displaying the first level of images if
* there is multiple top level branches.
*
* And also a boolean for whether the entire tree is dynamic or not.
* This overrides any perNode dynamic settings.
*
* @param object $structure The menu structure
* @param array $options Array of options
* @param bool $isDynamic Whether the tree is dynamic or not
*/
function HTML_TreeMenu_DHTML(&$structure, $options = array(), $isDynamic = true)
{
$this->HTML_TreeMenu_Presentation($structure);
$this->isDynamic = $isDynamic;

// Defaults
$this->images = 'images';
$this->linkTarget = '_self';
$this->defaultClass = '';
$this->usePersistence = true;
$this->noTopLevelImages = false;

foreach ($options as $option => $value) {
$this->$option = $value;
}
}

/**
* Returns the HTML for the menu. This method can be
* used instead of printMenu() to use the menu system
* with a template system.
*
* @access public
* @return string The HTML for the menu
*/
function toHTML()
{
static $count = 0;
$menuObj = 'objTreeMenu_' . ++$count;

$html = "\n";
$html .= '<script language="javascript" type="text/javascript">' . "\n\t";
$html .= sprintf('%s = new TreeMenu("%s", "%s", "%s", "%s", %s, %s);',
$menuObj,
$this->images,
$menuObj,
$this->linkTarget,
$this->defaultClass,
$this->usePersistence ? 'true' : 'false',
$this->noTopLevelImages ? 'true' : 'false');

$html .= "\n";

/**
* Loop through subnodes
*/
if (isset($this->menu->items)) {
for ($i=0; $i<count($this->menu->items); $i++) {
$html .= $this->_nodeToHTML($this->menu->items[$i], $menuObj);
}
}

$html .= sprintf("\n\t%s.drawMenu();", $menuObj);
if ($this->usePersistence && $this->isDynamic) {
$html .= sprintf("\n\t%s.resetBranches();", $menuObj);
}
$html .= "\n</script>";

return $html;
}

/**
* Prints a node of the menu
*
* @access private
*/
function _nodeToHTML($nodeObj, $prefix, $return = 'newNode')
{
$expanded = $this->isDynamic ? ($nodeObj->expanded ? 'true' : 'false') : 'true';
$isDynamic = $this->isDynamic ? ($nodeObj->isDynamic ? 'true' : 'false') : 'false';
$html = sprintf("\t %s = %s.addItem(new TreeNode('%s', %s, %s, %s, %s, '%s', '%s', %s));\n",
$return,
$prefix,
$nodeObj->text,
!empty($nodeObj->icon) ? "'" . $nodeObj->icon . "'" : 'null',
!empty($nodeObj->link) ? "'" . $nodeObj->link . "'" : 'null',
$expanded,
$isDynamic,
$nodeObj->cssClass,
$nodeObj->linkTarget,
!empty($nodeObj->expandedIcon) ? "'" . $nodeObj->expandedIcon . "'" : 'null');

foreach ($nodeObj->events as $event => $handler) {
$html .= sprintf("\t %s.setEvent('%s', '%s');\n",
$return,
$event,
str_replace(array("\r", "\n", "'"), array('\r', '\n', "''"), $handler));
}

/**
* Loop through subnodes
*/
if (!empty($nodeObj->items)) {
for ($i=0; $i<count($nodeObj->items); $i++) {
$html .= $this->_nodeToHTML($nodeObj->items[$i], $return, $return . '_' . ($i + 1));
}
}

return $html;
}
} // End class HTML_TreeMenu_DHTML


/**
* HTML_TreeMenu_Listbox class
*
* This class presents the menu as a listbox
*/
class HTML_TreeMenu_Listbox extends HTML_TreeMenu_Presentation
{
/**
* The text that is displayed in the first option
* @var string
*/
var $promoText;

/**
* The character used for indentation
* @var string
*/
var $indentChar;

/**
* How many of the indent chars to use
* per indentation level
* @var integer
*/
var $indentNum;

/**
* Target for the links generated
* @var string
*/
var $linkTarget;

/**
* Constructor
*
* @param object $structure The menu structure
* @param array $options Options whic affect the display of the listbox.
* These can consist of:
* o promoText The text that appears at the the top of the listbox
* Defaults to "Select..."
* o indentChar The character to use for indenting the nodes
* Defaults to "&nbsp;"
* o indentNum How many of the indentChars to use per indentation level
* Defaults to 2
* o linkTarget Target for the links. Defaults to "_self"
* o submitText Text for the submit button. Defaults to "Go"
*/
function HTML_TreeMenu_Listbox($structure, $options = array())
{
$this->HTML_TreeMenu_Presentation($structure);

$this->promoText = 'Select...';
$this->indentChar = '&nbsp;';
$this->indentNum = 2;
$this->linkTarget = '_self';
$this->submitText = 'Go';

foreach ($options as $option => $value) {
$this->$option = $value;
}
}

/**
* Returns the HTML generated
*/
function toHTML()
{
static $count = 0;
$nodeHTML = '';

/**
* Loop through subnodes
*/
if (isset($this->menu->items)) {
for ($i=0; $i<count($this->menu->items); $i++) {
$nodeHTML .= $this->_nodeToHTML($this->menu->items[$i]);
}
}

return sprintf('<form target="%s" action="" onsubmit="var link = this.%s.options[this.%s.selectedIndex].value; if (link) {this.action = link; return true} else return false"><select name="%s"><option value="">%s</option>%s</select> <input type="submit" value="%s" /></form>',
$this->linkTarget,
'HTML_TreeMenu_Listbox_' . ++$count,
'HTML_TreeMenu_Listbox_' . $count,
'HTML_TreeMenu_Listbox_' . $count,
$this->promoText,
$nodeHTML,
$this->submitText);
}

/**
* Returns HTML for a single node
*
* @access private
*/
function _nodeToHTML($node, $prefix = '')
{
$html = sprintf('<option value="%s">%s%s</option>', $node->link, $prefix, $node->text);

/**
* Loop through subnodes
*/
if (isset($node->items)) {
for ($i=0; $i<count($node->items); $i++) {
$html .= $this->_nodeToHTML($node->items[$i], $prefix . str_repeat($this->indentChar, $this->indentNum));
}
}

return $html;
}
} // End class HTML_TreeMenu_Listbox
?>







map_fs.php

<?php
/**
Map a filesystem with HTML TreeMenu
@author Tomas V.V.Cox <cox@idecnet.com>
*/
require_once 'TreeMenu.php';
$map_dir = '.';
$menu = new HTML_TreeMenu('menuLayer', 'images', '_self');
$menu->addItem(recurseDir($map_dir));

function &recurseDir($path) {
if (!$dir = opendir($path)) {
return false;
}
$files = array();
$node = &new HTML_TreeNode(basename($path), basename($path), 'php_menu/folder.gif');
while (($file = readdir($dir)) !== false) {
if ($file != '.' && $file != '..') {
if (@is_dir("$path/$file")) {
$addnode = &recurseDir("$path/$file");
} else {
$addnode = &new HTML_TreeNode($file, $file, 'php_menu/document2.png');
}
$node->addItem($addnode);
}
}
closedir($dir);
return $node;
}
?>
<html>
<head>
<script src="sniffer.js" language="JavaScript" type="text/javascript"></script>
<script src="TreeMenu.js" language="JavaScript" type="text/javascript"></script>
</head>
<body>

<div id="menuLayer"></div>
<?$menu->printMenu()?>

</body>
</html>





TreeMenu.js

// +-----------------------------------------------------------------------+
// | Copyright (c) 2002-2003, Richard Heyes, Harald Radi |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | o Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | o Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution.|
// | o The names of the authors may not be used to endorse or promote |
// | products derived from this software without specific prior written |
// | permission. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// | |
// +-----------------------------------------------------------------------+
// | Author: Richard Heyes <richard@phpguru.org> |
// | Harald Radi <harald.radi@nme.at> |
// +-----------------------------------------------------------------------+
//
// $Id: TreeMenu.js,v 1.14 2003/04/02 21:08:54 richard Exp $


/**
* TreeMenu class
*/
function TreeMenu(iconpath, myname, linkTarget, defaultClass, usePersistence, noTopLevelImages)
{
// Properties
this.iconpath = iconpath;
this.myname = myname;
this.linkTarget = linkTarget;
this.defaultClass = defaultClass;
this.usePersistence = usePersistence;
this.noTopLevelImages = noTopLevelImages;
this.n = new Array();

this.nodeRefs = new Array();
this.branches = new Array();
this.branchStatus = new Array();
this.layerRelations = new Array();
this.childParents = new Array();
this.cookieStatuses = new Array();

this.preloadImages();
}

/**
* Adds a node to the tree
*/
TreeMenu.prototype.addItem = function (newNode)
{
newIndex = this.n.length;
this.n[newIndex] = newNode;

return this.n[newIndex];
}

/**
* Preload images hack for Mozilla
*/
TreeMenu.prototype.preloadImages = function ()
{
var plustop = new Image; plustop.src = this.iconpath + '/php_menu/plustop.gif';
var plusbottom = new Image; plusbottom.src = this.iconpath + '/php_menu/plusbottom.gif';
var plus = new Image; plus.src = this.iconpath + '/php_menu/plus.gif';

var minustop = new Image; minustop.src = this.iconpath + '/php_menu/minustop.gif';
var minusbottom = new Image; minusbottom.src = this.iconpath + '/php_menu/minusbottom.gif';
var minus = new Image; minus.src = this.iconpath + '/php_menu/minus.gif';

var branchtop = new Image; branchtop.src = this.iconpath + '/php_menu/branchtop.gif';
var branchbottom = new Image; branchbottom.src = this.iconpath + '/php_menu/branchbottom.gif';
var branch = new Image; branch.src = this.iconpath + '/php_menu/branch.gif';

var linebottom = new Image; linebottom.src = this.iconpath + '/php_menu/linebottom.gif';
var line = new Image; line.src = this.iconpath + '/php_menu/line.gif';
}

/**
* Main function that draws the menu and assigns it
* to the layer (or document.write()s it)
*/
TreeMenu.prototype.drawMenu = function ()// OPTIONAL ARGS: nodes = [], level = [], prepend = '', expanded = false, visbility = 'inline', parentLayerID = null
{
/**
* Necessary variables
*/
var output = '';
var modifier = '';
var layerID = '';
var parentLayerID = '';

/**
* Parse any optional arguments
*/
var nodes = arguments[0] ? arguments[0] : this.n
var level = arguments[1] ? arguments[1] : [];
var prepend = arguments[2] ? arguments[2] : '';
var expanded = arguments[3] ? arguments[3] : false;
var visibility = arguments[4] ? arguments[4] : 'inline';
var parentLayerID = arguments[5] ? arguments[5] : null;

var currentlevel = level.length;

for (var i=0; i<nodes.length; i++) {

level[currentlevel] = i+1;
layerID = this.myname + '_' + 'node_' + this.implode('_', level);

/**
* Store this object in the nodeRefs array
*/
this.nodeRefs[layerID] = nodes;

/**
* Store the child/parent relationship
*/
this.childParents[layerID] = parentLayerID;

/**
* Gif modifier
*/
if (i == 0 && parentLayerID == null) {
modifier = nodes.length > 1 ? "top" : 'single';
} else if(i == (nodes.length-1)) {
modifier = "bottom";
} else {
modifier = "";
}

/**
* Single root branch is always expanded
*/
if (!this.doesMenu() || (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages))) {
expanded = true;

} else if (nodes.expanded) {
expanded = true;

} else {
expanded = false;
}

/**
* Make sure visibility is correct based on parent status
*/
visibility = this.checkParentVisibility(layerID) ? visibility : 'none';

/**
* Setup branch status and build an indexed array
* of branch layer ids
*/
if (nodes.n.length > 0) {
this.branchStatus[layerID] = expanded;
this.branches[this.branches.length] = layerID;
}

/**
* Setup toggle relationship
*/
if (!this.layerRelations[parentLayerID]) {
this.layerRelations[parentLayerID] = new Array();
}
this.layerRelations[parentLayerID][this.layerRelations[parentLayerID].length] = layerID;

/**
* Branch images
*/
var gifname = nodes.n.length && this.doesMenu() && nodes.isDynamic ? (expanded ? 'minus' : 'plus') : 'branch';
var iconimg = nodes.icon ? this.stringFormat('<img src="{0}/{1}" width="20" height="20" align="top" id="icon_{2}">', this.iconpath, nodes.icon, layerID) : '';

/**
* Add event handlers
*/
var eventHandlers = "";
for (j in nodes.events) {
eventHandlers += this.stringFormat('{0}="{1}" ', j, nodes.events[j]);
}

/**
* Build the html to write to the document
* IMPORTANT:
* document.write()ing the string: '<div style="display:...' will screw up nn4.x
*/
var layerTag = this.doesMenu() ? this.stringFormat('<div id="{0}" style="display: {1}" class="{2}">', layerID, visibility, (nodes.cssClass ? nodes[i].cssClass : this.defaultClass)) : this.stringFormat('<div class="{0}">', nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass);
var onMDown = this.doesMenu() && nodes[i].n.length && nodes[i].isDynamic ? this.stringFormat('onmousedown="{0}.toggleBranch(''{1}'', true)" style="cursor: pointer; cursor: hand"', this.myname, layerID) : '';
var imgTag = this.stringFormat('<img src="../php_menu/{0}/{1}{2}.gif" width="20" height="20" align="top" border="0" name="img_{3}" {4}>', this.iconpath, gifname, modifier, layerID, onMDown);
var linkTarget= nodes[i].linkTarget ? nodes[i].linkTarget : this.linkTarget;
var linkStart = nodes[i].link ? this.stringFormat('<a href="{0}" target="{1}">', nodes[i].link, linkTarget) : '';
var linkEnd = nodes[i].link ? '</a>' : '';

output = this.stringFormat('{0}<nobr>{1}{2}{3}{4}<span {5}>{6}</span>{7}</nobr><br></div>',
layerTag,
prepend,
parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages) ? '' : imgTag,
iconimg,
linkStart,
eventHandlers,
nodes[i].title,
linkEnd);

/**
* Write out the HTML. Uses document.write for speed over layers and
* innerHTML. This however means no dynamic adding/removing nodes on
* the client side. This could be conditional I guess if dynamic
* adding/removing is required.
*/
document.write(output + "\r\n");

/**
* Traverse sub nodes ?
*/
if (nodes[i].n.length) {
/**
* Determine what to prepend. If there is only one root
* node then the prepend to pass to children is nothing.
* Otherwise it depends on where we are in the tree.
*/
if (parentLayerID == null && (nodes.length == 1 || this.noTopLevelImages)) {
var newPrepend = '';

} else if (i < (nodes.length - 1)) {
var newPrepend = prepend + this.stringFormat('<img src="{0}/php_menu/line.gif" width="20" height="20" align="top">', this.iconpath);

} else {
var newPrepend = prepend + this.stringFormat('<img src="{0}/php_menu/linebottom.gif" width="20" height="20" align="top">', this.iconpath);
}

this.drawMenu(nodes[i].n,
level,
newPrepend,
nodes[i].expanded,
expanded ? 'inline' : 'none',
layerID);
}
}
}

/**
* Toggles a branches visible status. Called from resetBranches()
* and also when a +/- graphic is clicked.
*/
TreeMenu.prototype.toggleBranch = function (layerID, updateStatus) // OPTIONAL ARGS: fireEvents = true
{
var currentDisplay = this.getLayer(layerID).style.display;
var newDisplay = (this.branchStatus[layerID] && currentDisplay == 'inline') ? 'none' : 'inline';
var fireEvents = arguments[2] != null ? arguments[2] : true;

for (var i=0; i<this.layerRelations[layerID].length; i++) {

if (this.branchStatus[this.layerRelations[layerID][i]]) {
this.toggleBranch(this.layerRelations[layerID][i], false);
}

this.getLayer(this.layerRelations[layerID][i]).style.display = newDisplay;
}

if (updateStatus) {
this.branchStatus[layerID] = !this.branchStatus[layerID];

/**
* Persistence
*/
if (this.doesPersistence() && !arguments[2] && this.usePersistence) {
this.setExpandedStatusForCookie(layerID, this.branchStatus[layerID]);
}

/**
* Fire custom events
*/
if (fireEvents) {
nodeObject = this.nodeRefs[layerID];

if (nodeObject.ontoggle != null) {
eval(nodeObject.ontoggle);
}

if (newDisplay == 'none' && nodeObject.oncollapse != null) {
eval(nodeObject.oncollapse);
} else if (newDisplay == 'inline' && nodeObject.onexpand != null){
eval(nodeObject.onexpand);
}
}

// Swap image
this.swapImage(layerID);
}

// Swap icon
this.swapIcon(layerID);
}

/**
* Swaps the plus/minus branch images
*/
TreeMenu.prototype.swapImage = function (layerID)
{
var imgSrc = document.images['img_' + layerID].src;

var re = /^(.*)(plus|minus)(bottom|top|single)?.gif$/
if (matches = imgSrc.match(re)) {

document.images['img_' + layerID].src = this.stringFormat('{0}{1}{2}{3}',
matches[1],
matches[2] == 'plus' ? 'minus' : 'plus',
matches[3] ? matches[3] : '',
'.gif');
}
}

/**
* Swaps the icon for the expanded icon if one
* has been supplied.
*/
TreeMenu.prototype.swapIcon = function (layerID)
{
if (document.images['icon_' + layerID]) {
var imgSrc = document.images['icon_' + layerID].src;

if (this.nodeRefs[layerID].icon && this.nodeRefs[layerID].expandedIcon) {
//alert(imgSrc.indexOf(this.nodeRefs[layerID].expandedIcon));
var newSrc = (imgSrc.indexOf(this.nodeRefs[layerID].expandedIcon) == -1 ? this.nodeRefs[layerID].expandedIcon : this.nodeRefs[layerID].icon);

document.images['icon_' + layerID].src = this.iconpath + '/' + newSrc;
}
}
}

/**
* Can the browser handle the dynamic menu?
*/
TreeMenu.prototype.doesMenu = function ()
{
return (is_ie4up || is_nav6up || is_gecko || is_opera7);
}

/**
* Can the browser handle save the branch status
*/
TreeMenu.prototype.doesPersistence = function ()
{
return (is_ie4up || is_gecko || is_nav6up || is_opera7);
}

/**
* Returns the appropriate layer accessor
*/
TreeMenu.prototype.getLayer = function (layerID)
{
if (is_ie4) {
return document.all(layerID);

} else if (document.getElementById(layerID)) {
return document.getElementById(layerID);

} else if (document.all(layerID)) {
return document.all(layerID);
}
}

/**
* Save the status of the layer
*/
TreeMenu.prototype.setExpandedStatusForCookie = function (layerID, expanded)
{
this.cookieStatuses[layerID] = expanded;
this.saveCookie();
}

/**
* Load the status of the layer
*/
TreeMenu.prototype.getExpandedStatusFromCookie = function (layerID)
{
if (this.cookieStatuses[layerID]) {
return this.cookieStatuses[layerID];
}

return false;
}

/**
* Saves the cookie that holds which branches are expanded.
* Only saves the details of the branches which are expanded.
*/
TreeMenu.prototype.saveCookie = function ()
{
var cookieString = new Array();

for (var i in this.cookieStatuses) {
if (this.cookieStatuses[i] == true) {
cookieString[cookieString.length] = i;
}
}

document.cookie = 'TreeMenuBranchStatus=' + cookieString.join(':');
}

/**
* Reads cookie parses it for status info and
* stores that info in the class member.
*/
TreeMenu.prototype.loadCookie = function ()
{
var cookie = document.cookie.split('; ');

for (var i=0; i < cookie.length; i++) {
var crumb = cookie[i].split('=');
if ('TreeMenuBranchStatus' == crumb[0] && crumb[1]) {
var expandedBranches = crumb[1].split(':');
for (var j=0; j<expandedBranches.length; j++) {
this.cookieStatuses[expandedBranches[j]] = true;
}
}
}
}

/**
* Reset branch status
*/
TreeMenu.prototype.resetBranches = function ()
{
if (!this.doesPersistence()) {
return false;
}

this.loadCookie();

for (var i=0; i<this.branches.length; i++) {
var status = this.getExpandedStatusFromCookie(this.branches[i]);
// Only update if it's supposed to be expanded and it's not already
if (status == true && this.branchStatus[this.branches[i]] != true) {
if (this.checkParentVisibility(this.branches[i])) {
this.toggleBranch(this.branches[i], true, false);
} else {
this.branchStatus[this.branches[i]] = true;
this.swapImage(this.branches[i]);
}
}
}
}

/**
* Checks whether a branch should be open
* or not based on its parents' status
*/
TreeMenu.prototype.checkParentVisibility = function (layerID)
{
if (this.in_array(this.childParents[layerID], this.branches)
&& this.branchStatus[this.childParents[layerID]]
&& this.checkParentVisibility(this.childParents[layerID]) ) {

return true;

} else if (this.childParents[layerID] == null) {
return true;
}

return false;
}

/**
* New C# style string formatter
*/
TreeMenu.prototype.stringFormat = function (strInput)
{
var idx = 0;

for (var i=1; i<arguments.length; i++) {
while ((idx = strInput.indexOf('{' + (i - 1) + '}', idx)) != -1) {
strInput = strInput.substring(0, idx) + arguments[i] + strInput.substr(idx + 3);
}
}

return strInput;
}

/**
* Also much adored, the PHP implode() function
*/
TreeMenu.prototype.implode = function (seperator, input)
{
var output = '';

for (var i=0; i<input.length; i++) {
if (i == 0) {
output += input[i];
} else {
output += seperator + input[i];
}
}

return output;
}

/**
* Aah, all the old favourites are coming out...
*/
TreeMenu.prototype.in_array = function (item, arr)
{
for (var i=0; i<arr.length; i++) {
if (arr[i] == item) {
return true;
}
}

return false;
}

/**
* TreeNode Class
*/
function TreeNode(title, icon, link, expanded, isDynamic, cssClass, linkTarget, expandedIcon)
{
this.title = title;
this.icon = icon;
this.expandedIcon = expandedIcon;
this.link = link;
this.expanded = expanded;
this.isDynamic = isDynamic;
this.cssClass = cssClass;
this.linkTarget = linkTarget;
this.n = new Array();
this.events = new Array();
this.handlers = null;
this.oncollapse = null;
this.onexpand = null;
this.ontoggle = null;
}

/**
* Adds a node to an already existing node
*/
TreeNode.prototype.addItem = function (newNode)
{
newIndex = this.n.length;
this.n[newIndex] = newNode;

return this.n[newIndex];
}

/**
* Sets an event for this particular node
*/
TreeNode.prototype.setEvent = function (eventName, eventHandler)
{
switch (eventName.toLowerCase()) {
case 'onexpand':
this.onexpand = eventHandler;
break;

case 'oncollapse':
this.oncollapse = eventHandler;
break;

case 'ontoggle':
this.ontoggle = eventHandler;
break;

default:
this.events[eventName] = eventHandler;
}
}

/**
* That's the end of the tree classes. What follows is
* the browser detection code.
*/


//<!--
// Ultimate client-side JavaScript client sniff. Version 3.03
// (C) Netscape Communications 1999-2001. Permission granted to reuse and distribute.
// Revised 17 May 99 to add is_nav5up and is_ie5up (see below).
// Revised 20 Dec 00 to add is_gecko and change is_nav5up to is_nav6up
// also added support for IE5.5 Opera4&5 HotJava3 AOLTV
// Revised 22 Feb 01 to correct Javascript Detection for IE 5.x, Opera 4,
// correct Opera 5 detection
// add support for winME and win2k
// synch with browser-type-oo.js
// Revised 26 Mar 01 to correct Opera detection
// Revised 02 Oct 01 to add IE6 detection

// Everything you always wanted to know about your JavaScript client
// but were afraid to ask. Creates "is_" variables indicating:
// (1) browser vendor:
// is_nav, is_ie, is_opera, is_hotjava, is_webtv, is_TVNavigator, is_AOLTV
// (2) browser version number:
// is_major (integer indicating major version number: 2, 3, 4 ...)
// is_minor (float indicating full version number: 2.02, 3.01, 4.04 ...)
// (3) browser vendor AND major version number
// is_nav2, is_nav3, is_nav4, is_nav4up, is_nav6, is_nav6up, is_gecko, is_ie3,
// is_ie4, is_ie4up, is_ie5, is_ie5up, is_ie5_5, is_ie5_5up, is_ie6, is_ie6up, is_hotjava3, is_hotjava3up,
// is_opera2, is_opera3, is_opera4, is_opera5, is_opera5up
// (4) JavaScript version number:
// is_js (float indicating full JavaScript version number: 1, 1.1, 1.2 ...)
// (5) OS platform and version:
// is_win, is_win16, is_win32, is_win31, is_win95, is_winnt, is_win98, is_winme, is_win2k
// is_os2
// is_mac, is_mac68k, is_macppc
// is_unix
// is_sun, is_sun4, is_sun5, is_suni86
// is_irix, is_irix5, is_irix6
// is_hpux, is_hpux9, is_hpux10
// is_aix, is_aix1, is_aix2, is_aix3, is_aix4
// is_linux, is_sco, is_unixware, is_mpras, is_reliant
// is_dec, is_sinix, is_freebsd, is_bsd
// is_vms
//
// See http://www.it97.de/JavaScript/JS_tutori ... avobj.html and
// http://www.it97.de/JavaScript/JS_tutori ... eraol.html
// for detailed lists of userAgent strings.
//
// Note: you don't want your Nav4 or IE4 code to "turn off" or
// stop working when new versions of browsers are released, so
// in conditional code forks, use is_ie5up ("IE 5.0 or greater")
// is_opera5up ("Opera 5.0 or greater") instead of is_ie5 or is_opera5
// to check version in code which you want to work on future
// versions.

/**
* Severly curtailed all this as only certain elements
* are required by TreeMenu, specifically:
* o is_ie4up
* o is_nav6up
* o is_gecko
*/

// convert all characters to lowercase to simplify testing
var agt=navigator.userAgent.toLowerCase();

// *** BROWSER VERSION ***
// Note: On IE5, these return 4, so use is_ie5up to detect IE5.
var is_major = parseInt(navigator.appVersion);
var is_minor = parseFloat(navigator.appVersion);

// Note: Opera and WebTV spoof Navigator. We do strict client detection.
// If you want to allow spoofing, take out the tests for opera and webtv.
var is_nav = ((agt.indexOf('mozilla')!=-1) && (agt.indexOf('spoofer')==-1)
&& (agt.indexOf('compatible') == -1) && (agt.indexOf('opera')==-1)
&& (agt.indexOf('webtv')==-1) && (agt.indexOf('hotjava')==-1));
var is_nav6up = (is_nav && (is_major >= 5));
var is_gecko = (agt.indexOf('gecko') != -1);


var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1));
var is_ie4 = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) );
var is_ie4up = (is_ie && (is_major >= 4));

var is_opera = (agt.indexOf("opera") != -1);
var is_opera7 = is_opera && (agt.indexOf("opera 7") != -1);
//--> end hide JavaScript
Post Reply