<?php
// TreeMenuXL.php
// Chip Chapin <hide@address.com>
// Defines classes HTML_TreeMenuXL and HTML_TreeNodeXL
// An extension of Richard Heyes' HTML_TreeMenu
// 2002-12-02 cc Restore lost bug fix in static printNode
// 2002-11-14 cc Additional tweaks.
// 2002-11-12 cc Major update for release 1.1.0/XL2.0
// 2002-11-10 cc Updated
// +-----------------------------------------------------------------------+
// | Copyright (c) 2002 Chip Chapin <hide@address.com> |
// | http://www.chipchapin.com |
// | 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: Chip Chapin <hide@address.com> |
// +-----------------------------------------------------------------------+
// Check local directory first, then check system directory.
// This facilitates local mods and testing.
$PHPLIBPATH='/bin/'; // Where do you keep your PHP libraries?
if (file_exists( 'menu/TreeMenu.php' ))
include_once( 'menu/TreeMenu.php' );
else include_once( $_SERVER['DOCUMENT_ROOT'] . $PHPLIBPATH . 'TreeMenu.php' );
/* Not-quite Obsolete as of HTML_TreeMenu 1.1 */
if (file_exists( 'menu/ccBrowserInfo.php' ))
include_once( 'menu/ccBrowserInfo.php' );
else include_once( $_SERVER['DOCUMENT_ROOT'] . $PHPLIBPATH . 'ccBrowserInfo.php' );
// Browser detection determines whether we generate DHTML menus
static $tmsDoesMenus = 'maybe';
/**/
////////////////////////////////////////////////////////////////////////////////////
// class HTML_TreeMenuXL extends HTML_TreeMenu
// As of 1.1, the only remaining changes in this class are
// 1. Support setProperties( array ) and unsetProperties( array ) member functions
// These can probably be removed now.
////////////////////////////////////////////////////////////////////////////////////
class HTML_TreeMenuXL extends HTML_TreeMenu
{
// setProperties -- update the property list for this object.
function setProperties( $pl=null )
{
if (empty($pl) || !is_array($pl)) return false;
foreach($pl as $pname => $pval) {
//$this->properties[$pname] = $pval;
$this->$pname = $pval;
}
return true;
} // setProperties
// unsetProperties -- remove property list entries for this object.
function unsetProperties( $pl=null )
{
if (empty($pl) || !is_array($pl)) return false;
foreach($pl as $pname) {
// unset($this->properties[$pname]);
unset($this->$pname);
// Hack
if ($pname == 'expanded' || $pname == 'selected') {
// Propagate removal to all child nodes.
for ($i=0; $i<count($this->items); $i++) $this->items[$i]->unsetChildProperties( array($pname) );
}
}
return true;
} // unsetProperties
} // class HTML_TreeMenuXL
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// class HTML_TreeNodeXL extends HTML_TreeNode
// Provides the following extensions over TreeNode 1.1
// 1. Flexible interface (mix of positional and property list args)
// 2. Some additional property setting functions
////////////////////////////////////////////////////////////////////////////////////
class HTML_TreeNodeXL extends HTML_TreeNode
{
// Constructor
// Supports a flexible interface:
// HTML_TreeNodeXL( [$text [, $link [, $icon [, $expanded [, $isDynamic [, $cssClass]]]]]] [array] )
function HTML_TreeNodeXL( )
{
HTML_TreeNode::HTML_TreeNode();
$numargs = func_num_args();
$numargs = min(6, $numargs);
$arglist = array('text', 'link', 'icon', 'expanded', 'isDynamic', 'cssClass');
for ($i=0; $i<$numargs; $i++) {
$a =& func_get_arg($i);
if (is_array( $a )) {
// Array is always the last argument. Ignore anything else.
$this->setProperties( $a );
break;
}
$this->$arglist[$i] = $a;
}
$this->_checkProperties();
} // HTML_TreeNodeXL constructor
// setProperties -- update the property list for this object.
function setProperties( $pl=null )
{
if (empty($pl) || !is_array($pl)) return false;
foreach ($pl as $pname => $pval) $this->$pname = $pval;
return true;
} // setProperties
// setChildProperties -- update the property list for this object and its children
function setChildProperties( $pl=null )
{
if (!$this->setProperties( $pl )) return false;
for ($i=0; $i<count($this->items); $i++) $this->items[$i]->setChildProperties( $pl );
return true;
} // setChildProperties
// unsetChildProperties -- remove property list entries for this object and its children
// 2002-11-12 Probably no longer necessary
function unsetChildProperties( $pl=null )
{
if (empty($pl) || !is_array($pl)) return false;
foreach($pl as $pname) {
//unset($this->properties[$pname]);
unset($this->$pname);
}
for ($i=0; $i<count($this->items); $i++) $this->items[$i]->unsetChildProperties( $pl );
return true;
} // unsetChildProperties
///////////////////////////////////
// HTML_TreeNodeXL Helper Functions
///////////////////////////////////
// Check object for required properties. Set them to defaults if not present.
function _checkProperties()
{
$pdefaults = array('text'=>'[item]',
'icon'=>null,
'link'=>'#',
'expanded'=>false,
'isDynamic'=>true );
foreach($pdefaults as $pname => $pval) {
if (!isset($this->$pname)) $this->$pname = $pval;
}
return true;
} // _checkProperties
} // class HTML_TreeNodeXL
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// class HTML_TreeMenu_DHTMLXL extends HTML_TreeMenu_DHTML
// Presentation class new for 1.1. Most of the XL functionality no longer in the
// base classes is here. Browser detection for static menus has been removed.
// 1. Alternate implementation of "expanded" (TODO: check this against 1.1 changes,
// may no longer be necessary).
// 2. Implements auto-expansion and highlighting of selected nodes.
////////////////////////////////////////////////////////////////////////////////////
class HTML_TreeMenu_DHTMLXL extends HTML_TreeMenu_DHTML
{
// Constructor
function HTML_TreeMenu_DHTMLXL($structure, $options = array())
{
$this->defaultProperties['selectedStyle'] = 'tmenuSelected';
$this->defaultProperties['brOK'] = null;
HTML_TreeMenu_DHTML::HTML_TreeMenu_DHTML($structure, $options);
} // HTML_TreeMenu_DHTMLXL
// toHTML -- Returns HTML string for the menu
function toHTML()
{
// I'm Still having erratic problems with NN4
// Hence this check.
if ($GLOBALS['tmsDoesMenus'] == 'maybe') {
$tmb = new ccBrowserInfo();
$GLOBALS['tmsDoesMenus'] = ($tmb->is_ie5up() || $tmb->is_moz5up());
}
if (!$GLOBALS['tmsDoesMenus']) {
$this_sucks = &new HTML_TreeMenu_RigidXL( $this->menu,
array('images'=>$this->images,
'defaultClass'=>$this->defaultClass,
'autostyles'=>$this->autostyles,
'linkSelectKey'=>$this->linkSelectKey,
'lineImageWidth'=>$this->lineImageWidth,
'lineImageHeight'=>$this->lineImageHeight,
'iconImageWidth'=>$this->iconImageWidth,
'iconImageHeight'=>$this->iconImageHeight,
'linkTarget'=>$this->linkTarget,
'selectedStyle'=>$this->selectedStyle,
'brOK'=>$this->brOK));
return $this_sucks->toHTML();
}
// Expand the branch(es), if any, that contain nodes with matching links.
if (!empty($this->linkSelectKey)) {
$keys = $this->linkSelectKey;
if (!is_array($keys)) $keys = array($keys);
foreach ($keys as $key) $this->_expandSelected( $this->menu, $key );
}
// Is the entire menu "expanded"? Then make it so...
if (isset($this->expanded) && $this->expanded) {
for ($i=0; $i<count($this->menu->items); $i++)
$this->menu->items[$i]->setChildProperties( array('expanded'=>true) );
}
return HTML_TreeMenu_DHTML::toHTML();
} // toHTML
/////////////////////////////////////////
// HTML_TreeMenu_DHTMLXL Helper Functions
/////////////////////////////////////////
// _expandSelected -- Expand the branch(es), if any that contain nodes
// with matching links.
// Note: when called the first time $node is the menu object
function _expandSelected( &$node, $key )
{
if ($key == (!empty($node->link) ? $node->link : null)) {
$node->_ensureVisible();
$node->selected = true;
}
for ($i=0; $i<count($node->items); $i++) $this->_expandSelected( $node->items[$i], $key );
} // _expandSelected
// _nodeToHTML -- Generate JavaScript for this node.
function _nodeToHTML($nodeObj, $prefix, $return = 'newNode', $treelevel = 0)
{
$html = HTML_TreeMenu_DHTML::_nodeToHTML($nodeObj, $prefix, $return, $treelevel);
// Set javascript values for image sizes for this node
$nodeprops = array('iconImageWidth', 'iconImageHeight', 'lineImageWidth', 'lineImageHeight');
foreach ($nodeprops as $nprop) {
// Image sizes: set from node or presentation object.
if (isset($nodeObj->$nprop)) $$nprop = $nodeObj->$nprop;
else $$nprop = $this->$nprop;
$html .= "\t$return." . $nprop . '=' . $$nprop . ";\n";
}
// Node may be selected
if (!empty($nodeObj->selected)) {
$html .= "\t$return.selected = '" . $this->selectedStyle . "';\n";
}
return $html;
} // _nodeToHTML
} // class HTML_TreeMenu_DHTMLXL
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// class HTML_TreeMenu_RigidXL
// Statically generate a "rigid" tree menu
////////////////////////////////////////////////////////////////////////////////////
class HTML_TreeMenu_RigidXL extends HTML_TreeMenu_Presentation
{
// toHTML -- Returns HTML string for the menu
function toHTML()
{
// Generate Static Menu
$html = '';
for ($i=0; $i < count($this->menu->items); $i++)
$html .= $this->_printStaticMenu($this->menu, $i);
return $html;
} // toHTML
/////////////////////////////////////////////
// Helper Functions for HTML_TreeMenu_RigidXL
/////////////////////////////////////////////
// _printStaticMenu -- Print current node and all subnodes
// $np: Node parent
// $ni: Index in parent of current node
// $treelevel: Level in tree of current node
// $prepend: string of HTML to prepend to current node
function _printStaticMenu($np, $ni, $treelevel=0, $prepend='')
{
$n = $np->items[$ni];
$cntSubnodes = empty($n->items) ? 0 : count($n->items);
$cntSiblings = empty($np->items) ? 0 : count($np->items);
// Gif modifier
if ($ni == 0 && $treelevel == 0) $modifier = $cntSubnodes > 1 ? 'top' : 'single';
elseif ($ni == ($cntSiblings - 1)) $modifier = 'bottom';
else $modifier = '';
$html = $this->_printNode($n, $treelevel, $prepend, $modifier);
// Print any subnodes
for ($i=0; $i<$cntSubnodes; $i++) {
// Determine what to prepend to child subnode entries.
$newPrepend = $prepend;
if (empty($this->images)) {
// No images. Use spaces instead
$newPrepend .= ' ';
}
else {
// Prepend images to each menu entry
//if ($treelevel == 0 && $cntSiblings == 1) $nppimg = '';
//^^^ This special case does not apply to static menus.
// Why? Bwaaa Ha Ha Ha Haaaaaaa!! Try it and see. Happy Halloween...
if ($ni < ($cntSiblings - 1)) $nppimg = 'line.gif';
else $nppimg = 'linebottom.gif';
$lineImageWidth = empty($n->lineImageWidth) ? $this->lineImageWidth : $n->lineImageWidth;
$lineImageHeight = empty($n->lineImageHeight) ? $this->lineImageHeight : $n->lineImageHeight;
$newPrepend .= sprintf('<img src="%s/%s" width="%d" height="%d" align="top">',
$this->images, $nppimg, $lineImageWidth, $lineImageHeight);
}
// Now print the menu entry for this node
$html .= $this->_printStaticMenu($n, $i, $treelevel+1, $newPrepend);
}
return $html;
} // _printStaticMenu
// Return static HTML for a single menu node
// This code is based on the JavaScript function drawMenu().
function _printNode($n, $treelevel, $prepend='', $modifier='')
{
if (empty($this->images)) {
// No images in output
$iconimg = '';
$imgTag = '';
}
else {
$lineImageWidth = empty($n->lineImageWidth) ? $this->lineImageWidth : $n->lineImageWidth;
$lineImageHeight = empty($n->lineImageHeight) ? $this->lineImageHeight : $n->lineImageHeight;
$iconImageWidth = empty($n->iconImageWidth) ? $this->iconImageWidth : $n->iconImageWidth;
$iconImageHeight = empty($n->iconImageHeight) ? $this->iconImageHeight : $n->iconImageHeight;
$gifname = 'branch';
$iconimg = $n->icon ? sprintf('<img src="%s/%s" width="%d" height="%d" align="top">',
$this->images, $n->icon, $iconImageWidth, $iconImageHeight) : '';
$imgTag = sprintf('<img src="%s/%s%s.gif" width="%d" height="%d" align="top" border="0" />',
$this->images, $gifname, $modifier, $lineImageWidth, $lineImageHeight);
}
$linkStart = $n->link ? sprintf('<a href="%s" target="%s">',
$n->link, $this->linkTarget) : '';
$linkEnd = $n->link ? '</a>' : '';
$selectedStart = '';
$selectedEnd = '';
if (!empty($n->selected)) {
$selectedStart = "<span class='" . $this->selectedStyle . "'>";
$selectedEnd = "</span>";
}
// cc 2002-11-12, this depends on operation of _checkproperties()
$cssClass = empty($n->cssClass) ? $this->defaultClass : $n->cssClass;
if ($cssClass == 'auto') $cssClass = $this->autostyles[min($treelevel, count($this->autostyles)-1)];
$cssStart = empty($cssClass) ? '' : '<span class="' . $cssClass . '">';
$cssEnd = empty($cssClass) ? '' : '</span>';
return '<nobr>'. $cssStart . $prepend .
($treelevel == 0 && count($n->items) == 1 ? '' : $imgTag) . // cc 2002-12-02 may need fixing...
$iconimg .
$selectedStart . $linkStart . $n->text . $linkEnd . $selectedEnd .
$cssEnd . "</nobr><br>\n";
} // _printNode
} // HTML_TreeMenu_RigidXL
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
// class HTML_TreeMenu_ListboxXL extends HTML_TreeMenu_Presentation
//
// This is NOT an extension of the base Listbox class, rather it's a hybrid
// of my original implementation and Richard's version. I've rewritten mine
// to match his as closely as possible, so that the essential differences are apparent.
////////////////////////////////////////////////////////////////////////////////////
class HTML_TreeMenu_ListboxXL extends HTML_TreeMenu_Presentation
{
/**
* The default presentation properties list -
* By replacing the entire list, there is no need to subclass the constructor.
* @var array
*/
var $defaultProperties = array( //'promoText' => 'Select...',
'indentChar' => ' ',
'indentNum' => 2,
'cssClass' => 'tmlistbox',
'bulletStyles' => array('', '• ', '-- ', ' - ', ' '),
'useBullets' => false );
// toHTML -- Generate HTML for a listbox from current menu object.
// We print the entire <form><select>...</select></form>
function toHTML()
{
$cssString = empty($this->cssClass) ? '' : sprintf(' class="%s"', $this->cssClass);
$promoString = empty($this->promoText) ? '' : sprintf('<option value="">%s</option>', $this->promoText);
// Select the branch(es), if any that contain nodes with matching links.
if (!empty($this->linkSelectKey)) {
$keys = $this->linkSelectKey;
if (!is_array($keys)) $keys = array($keys);
foreach ($keys as $key) $this->_expandSelected( $this->menu, $key );
}
// Loop through child nodes and gather list of <option> elements.
$nodeHTML = '';
if (isset($this->menu->items)) {
for ($i=0; $i<count($this->menu->items); $i++) {
$nodeHTML .= $this->_nodeToHTML($this->menu->items[$i]);
}
}
return sprintf('<form onsubmit="var link = this.sl.options[this.sl.selectedIndex].value; if (link) location.href = link; return false" style="margin-bottom:0px"%s><select name="sl"%s>%s%s</select> <input type="submit" value="Go"%s /></form>',
$cssString, $cssString, $promoString, $nodeHTML, $cssString);
} // toHTML
// _nodeToHTML -- Helper routine for _printListbox
// Print the <option> tags for the menu subtree rooted at $node
// Note: $node could be either a Menu object or a Node object.
function _nodeToHTML($node, $treelevel=0)
{
// Is this item selected?
$selectString = empty($node->selected) ? '' : ' selected';
// Leading for node string
$prefix = str_repeat($this->indentChar, $this->indentNum * $treelevel);
if (!empty($this->useBullets) && is_array($this->bulletStyles)) {
$prefix .= $this->bulletStyles[min($treelevel, count($this->bulletStyles)-1)];
}
// Print node string and </option>
$html = sprintf('<option value="%s"%s>%s%s</option>'."\n", $node->link, $selectString, $prefix, $node->text);
// Proceed through child nodes
for ($i=0; $i < count($node->items); $i++) {
$html .= $this->_nodeToHTML($node->items[$i], $treelevel+1);
}
return $html;
} // _nodeToHTML
// _expandSelected -- Set the 'selected' property for nodes with matching links.
// Note: when called the first time $node is the menu object
function _expandSelected( &$node, $key )
{
if ($key == (!empty($node->link) ? $node->link : null)) {
$node->selected = true;
}
for ($i=0; $i<count($node->items); $i++) $this->_expandSelected( $node->items[$i], $key );
} // _expandSelected
} // class HTML_TreeMenu_ListboxXL
////////////////////////////////////////////////////////////////////////////////////
?>