home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2001 August
/
PCWorld_2001-08_cd.bin
/
Komunikace
/
phptriad
/
phptriadsetup2-11.exe
/
php
/
pear
/
Experimental
/
HTML
/
Menu.php
Wrap
PHP Script
|
2001-03-13
|
22KB
|
653 lines
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Ulf Wendel <ulf.wendel@phpdoc.de> |
// +----------------------------------------------------------------------+
//
// $Id: Menu.php,v 1.6 2001/03/13 00:18:38 uw Exp $
/**
* Generates a HTML menu from a multidimensional hash.
*
* Special thanks to the original author: Alex Vorobiev <sasha@mathforum.com>
*
* @version $id: $
* @author Ulf Wendel <ulf.wendel@phpdoc.de>
* @access public
* @package HTML
*/
class menu {
/**
* Menu structure as a multidimensional hash.
*
* @var array
* @see setMenu(), Menu()
*/
var $menu = array();
/**
* Mapping from URL to menu path.
*
* @var array
* @see getPath()
*/
var $urlmap = array();
/**
* Menu type: tree, rows, you-are-here.
*
* @var array
* @see setMenuType()
*/
var $menu_type = "tree";
/**
* Path to a certain menu item.
*
* Internal class variable introduced to save some recursion overhead.
*
* @var array
* @see get(), getPath()
*/
var $path = array();
/**
* Generated HTML menu.
*
* @var string
* @see get()
*/
var $html = "";
/**
* URL of the current page.
*
* This can be the URL of the current page but it must not be exactly the
* return value of getCurrentURL(). If there's no entry for the return value
* in the menu hash getPath() tries to find the menu item that fits best
* by shortening the URL sign by sign until it find an entry that fits.
*
* @see getCurrentURL(), getPath()
*/
var $current_url = "";
/**
* Initializes the menu, sets the type and menu structure.
*
* @param array
* @param string
* @see setMenuType(), setMenu()
*/
function menu($menu = "", $type = "tree") {
if (is_array($menu))
$this->setMenu($menu);
$this->setMenuType($type);
} // end constructor
/**
* Sets the menu structure.
*
* The menu structure is defined by a multidimensional hash. This is
* quite "dirty" but simple and easy to traverse. An example
* show the structure. To get the following menu:
*
* 1 - Projects
* 11 - Projects => PHPDoc
* 12 - Projects => Forms
* 2 - Stuff
*
* you need the array:
*
* $menu = array(
* 1 => array(
* "title" => "Projects",
* "url" => "/projects/index.php",
* "sub" => array(
* 11 => array(
* "title" => "PHPDoc",
* ...
* ),
* 12 => array( ... ),
* )
* ),
* 2 => array( "title" => "Stuff", "url" => "/stuff/index.php" )
* )
*
* Note the index "sub" and the nesting. Note also that 1, 11, 12, 2
* must be unique. The class uses them as ID's.
*
* @param array
* @access public
* @see append(), update(), delete()
*/
function setMenu($menu) {
$this->menu = $menu;
$this->urlmap = array();
} // end func setMenu
/**
* Sets the type / format of the menu: tree, rows or urhere.
*
* @param string "tree", "rows", "urhere", "prevnext"
* @access public
*/
function setMenuType($menu_type) {
$this->menu_type = strtolower($menu_type);
} // end func setMenuType
/**
* Returns the HTML menu.
*
* @param string Menu type: tree, urhere, rows, prevnext
* @return string HTML of the menu
* @access public
*/
function get($menu_type = "") {
if ("" != $menu_type)
$this->setMenuType($menu_type);
$this->html = "";
// buildMenu for rows cares on this itself
if ("rows" != $this->menu_type)
$this->html .= $this->getStart();
// storing to a class variable saves some recursion overhead
$this->path = $this->getPath();
$this->buildMenu($this->menu);
if ("rows" != $this->menu_typ)
$this->html .= $this->getEnd();
return $this->html;
} // end func get
/**
* Prints the HTML menu.
*
* @brother get()
* @return void
*/
function show($menu_type = "") {
print $this->get($menu_type);
} // end func show
/**
* Returns a sitemap.
*
* @return string HTML code
* @access public
*/
function getSitemap() {
$oldtype = $this->menu_type;
$this->setMenuType("tree");
$this->html = $this->getStart();
$this->path = $this->getPath();
$this->buildSitemap($this->menu);
$this->setMenuType($oldtype);
return $this->html . $this->getEnd();
} // end func getSitemap
/**
* Returns the prefix of the HTML menu items.
*
* @return string HTML menu prefix
* @see getEnd(), get()
*/
function getStart() {
$html = "";
switch ($this->menu_type) {
case "rows":
case "prevnext":
$html .= "<table border><tr>";
break;
case "tree":
case "urhere":
$html .= "<table border>";
break;
}
return $html;
} // end func getStart
/**
* Returns the postfix of the HTML menu items.
*
* @return string HTML menu postfix
* @see getStart(), get()
*/
function getEnd() {
$html = "";
switch ($this->menu_type) {
case "rows":
case "prevnext":
$html .="</tr></table>";
break;
case "tree":
case "urhere":
$html = "</table>";
break;
}
return $html;
} // end func getEnd
/**
* Build the menu recursively.
*
* @param array first call: $this->menu, recursion: submenu
* @param integer level of recursion, current depth in the tree structure
* @param integer prevnext flag
*/
function buildMenu($menu, $level = 0, $flag_stop_level = -1) {
static $last_node = array(), $up_node = array();
// the recursion goes slightly different for every menu type
switch ($this->menu_type) {
case "tree":
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
if ($this->current_url == $node["url"]) {
// menu item that fits to this url - "active" menu item
$type = 1;
} else if (isset($this->path[$level]) && $this->path[$level] == $node_id) {
// processed menu item is part of the path to the active menu item
$type = 2;
} else {
// not selected, not a part of the path to the active menu item
$type = 0;
}
$this->html .= $this->getEntry($node, $level, $type);
// follow the subtree if the active menu item is in it
if ($type && isset($node["sub"]))
$this->buildMenu($node["sub"], $level + 1);
}
break;
case "rows":
// every (sub)menu has it's own table
$this->html .= $this->getStart();
$submenu = false;
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
if ($this->current_url == $node["url"]) {
// menu item that fits to this url - "active" menu item
$type = 1;
} else if (isset($this->path[$level]) && $this->path[$level] == $node_id) {
// processed menu item is part of the path to the active menu item
$type = 2;
} else {
// not selected, not a part of the path to the active menu item
$type = 0;
}
$this->html .= $this->getEntry($node, $level, $type);
// remember the subtree
if ($type && isset($node["sub"]))
$submenu = $node["sub"];
}
// close the table for this level
$this->html .= $this->getEnd();
// go deeper if neccessary
if ($submenu)
$this->buildMenu($submenu, $level + 1);
break;
case "urhere":
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
if ($this->current_url == $node["url"]) {
// menu item that fits to this url - "active" menu item
$type = 1;
} else if (isset($this->path[$level]) && $this->path[$level] == $node_id) {
// processed menu item is part of the path to the active menu item
$type = 2;
} else {
// not selected, not a part of the path to the active menu item
$type = 0;
}
// follow the subtree if the active menu item is in it
if ($type) {
$this->html .= $this->getEntry($node, $level, $type);
if (isset($node["sub"])) {
$this->buildMenu($node["sub"], $level + 1);
continue;
}
}
}
break;
case "prevnext":
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
if (-1 != $flag_stop_level) {
// add this item to the menu and stop recursion - (next >>) node
if ($flag_stop_level == $level) {
$this->html .= $this->getEntry($node, $level, 4);
$flag_stop_level = -1;
}
break;
} else if ($this->current_url == $node["url"]) {
// menu item that fits to this url - "active" menu item
$type = 1;
$flag_stop_level = $level;
if (0 != count($last_node)) {
$this->html .= $this->getEntry($last_node, $level, 3);
} else {
// WARNING: if there's no previous take the first menu entry - you might not like this rule!
reset($this->menu);
list($node_id, $first_node) = each($this->menu);
$this->html .= $this->getEntry($first_node, $level, 3);
}
if (0 != count($up_node)) {
$this->html .= $this->getEntry($up_node, $level, 5);
} else {
// WARNING: if there's no up take the first menu entry - you might not like this rule!
reset($this->menu);
list($node_id, $first_node) = each($this->menu);
$this->html .= $this->getEntry($first_node, $level, 5);
}
} else if (isset($this->path[$level]) && $this->path[$level] == $node_id) {
// processed menu item is part of the path to the active menu item
$type = 2;
} else {
$type = 0;
}
// remember the last (<< prev) node
$last_node = $node;
// follow the subtree if the active menu item is in it
if ($type && isset($node["sub"])) {
$up_node = $node;
$flag_stop_level = $this->buildMenu($node["sub"], $level + 1, (-1 != $flag_stop_level) ? $flag_stop_level + 1 : -1);
}
}
break;
} // end switch menu_type
return ($flag_stop_level) ? $flag_stop_level - 1 : -1;
} // end func buildMenu
/**
* Build the menu recursively.
*
* @param array first call: $this->menu, recursion: submenu
* @param int level of recursion, current depth in the tree structure
*/
function buildSitemap($menu, $level = 0) {
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
if ($this->current_url == $node["url"]) {
// menu item that fits to this url - "active" menu item
$type = 1;
} else if (isset($this->path[$level]) && $this->path[$level] == $node_id) {
// processed menu item is part of the path to the active menu item
$type = 2;
} else {
// not selected, not a part of the path to the active menu item
$type = 0;
}
$this->html .= $this->getEntry($node, $level, $type);
// follow the subtree if the active menu item is in it
if (isset($node["sub"]))
$this->buildSitemap($node["sub"], $level + 1);
}
} // end func buildSitemap
/**
* Returns the HTML of one menu item.
*
* @param array menu item data (node data)
* @param integer level in the menu tree
* @param string menu item type: 0, 1, 2. 0 => plain menu item,
* 1 => selected (active) menu item, 2 => item
* is a member of the path to the selected (active) menu item
* 3 => previous entry, 4 => next entry
* @return string HTML
* @see buildMenu()
*/
function getEntry(&$node, $level, $item_type) {
$html = "";
if ("tree" == $this->menu_type) {
// tree menu
$html .= "<tr>";
$indent = "";
if ($level)
for ($i = 0; $i < $level; $i++)
$indent .= " ";
}
// draw the <td></td> cell depending on the type of the menu item
switch ($item_type) {
case 0:
// plain menu item
$html .= sprintf('<td>%s<a href="%s">%s</a></td>',
$indent,
$node["url"],
$node["title"]
);
break;
case 1:
// selected (active) menu item
$html .= sprintf('<td>%s<b>%s</b></td>',
$indent,
$node["title"]
);
break;
case 2:
// part of the path to the selected (active) menu item
$html .= sprintf('<td>%s<b><a href="%s">%s</a></b>%s</td>',
$indent,
$node["url"],
$node["title"],
("urhere" == $this->menu_type) ? " >> " : ""
);
break;
case 3:
// << previous url
$html .= sprintf('<td>%s<a href="%s"><< %s</a></td>',
$indent,
$node["url"],
$node["title"]
);
break;
case 4:
// next url >>
$html .= sprintf('<td>%s<a href="%s">%s >></a></td>',
$indent,
$node["url"],
$node["title"]
);
break;
case 5:
// up url ^^
$html .= sprintf('<td>%s<a href="%s">^ %s ^</a></td>',
$indent,
$node["url"],
$node["title"]
);
break;
}
if ("tree" == $this->menu_type)
$html .= "</tr>\n";
return $html;
} // end func getEnty
/*
* Returns the path of the current page in the menu "tree".
*
* @return array path to the selected menu item
* @see buildPath(), $urlmap
*/
function getPath() {
$this->current_url = $this->getCurrentURL();
$this->buildPath($this->menu, array());
while ($this->current_url && !isset($this->urlmap[$this->current_url]))
$this->current_url = substr($this->current_url, 0, -1);
return $this->urlmap[$this->current_url];
} // end func getPath
/**
* Computes the path of the current page in the menu "tree".
*
* @param array first call: menu hash / recursion: submenu hash
* @param array first call: array() / recursion: path
* @return boolean true if the path to the current page was found,
* otherwise false. Only meaningful for the recursion.
* @global $PHP_SELF
* @see getPath(), $urlmap
*/
function buildPath($menu, $path) {
// loop through the (sub)menu
foreach ($menu as $node_id => $node) {
// save the path of the current node in the urlmap
$this->urlmap[$node["url"]] = $path;
// we got'em - stop the search by returning true
// KLUDGE: getPath() works with the best alternative for a URL if there's
// no entry for a URL in the menu hash, buildPath() does not perform this test
// and might do some unneccessary recursive runs.
if ($node["url"] == $this->current_url)
return true;
// current node has a submenu
if ($node["sub"]) {
// submenu path = current path + current node
$subpath = $path;
$subpath[] = $node_id;
// continue search recursivly - return is the inner loop finds the
// node that belongs to the current url
if ($this->buildPath($node["sub"], $subpath))
return true;
}
}
// not found
return false;
} // end func buildPath
/**
* Returns the URL of the currently selected page.
*
* The returned string is used for all test against the URL's
* in the menu structure hash.
*
* @return string
*/
function getCurrentURL() {
return $GLOBALS["PHP_SELF"];
} // end func getCurrentURL
} // end class menu
?>