| // +----------------------------------------------------------------------+ // // $Id$ // // {{{ DB_NestedSet_TigraMenu:: class /** * This class can be used to generate the data to build javascript popup menu * from a DB_NestedSet node array. * The Javascript part is done using the free available TigraMenu * available at http://www.softcomplex.com/products/tigra_menu/. * Currently version 1.0 is supported. * Parts of this class where taken ftom the TreemMenu driver by Jason Rust * * @author Daniel Khan * @package DB_NestedSet * @version $Revision: 1.8 $ * @access public */ // }}} class DB_NestedSet_TigraMenu extends DB_NestedSet_Output { // {{{{ properties /** * @var integer The depth of the current menu. * @access private */ var $_levels = 1; /** * @var integer The level we started at * @access private */ var $_levelOffset = false; /** * @var array The current menu structure * @access private */ var $_structTigraMenu = false; /** * @var array The longest text for each level * @access private */ var $_strlenByLevel = array(); // }}} // {{{ DB_NestedSet_TigraMenu /** * Constructor * * @param array $params A hash with parameters needed by the class * @see _createFromStructure() * @return bool **/ function &DB_NestedSet_TigraMenu($params) { $this->_menu_id = $params['menu_id']; $this->_structTigraMenu = $this->_createFromStructure($params); return true; } // }}} // {{{ _createFromStructure() /** * Creates the JavaScript array for TigraMenu * Initially this method was introduced for the TreeMenu driver by Jason Rust * * o 'structure' => the result from $nestedSet->getAllNodes(true) * o 'textField' => the field in the table that has the text for node * o 'linkField' => the field in the table that has the link for the node * * @access private * @return string The TigraMenu JavaScript array */ function &_createFromStructure($params) { // Basically we go through the array of nodes checking to see // if each node has children and if so recursing. The reason this // works is because the data from getAllNodes() is ordered by level // so a root node will always be first, and sub children will always // be after them. static $rootlevel; // always start at level 1 if (!isset($params['currentLevel'])) { $params['currentLevel'] = 1; } if (!isset($rootlevel)) { $rootlevel = $params['currentLevel']; } if (isset($params['tigraMenu'])) { $tigraMenu = $tigraMenu.$params['tigraMenu']; } if(!$this->_levelOffset) { $this->_levelOffset = $params['currentLevel']; } if($this->_levels < ($params['currentLevel']- $this->_levelOffset)) { $this->_levels = $params['currentLevel'] - $this->_levelOffset; } // have to use a while loop here because foreach works on a copy of the array and // the child nodes are passed by reference during the recursion so that the parent // will know when they have been hit. reset($params['structure']); while(list($key, $node) = each($params['structure'])) { // see if we've already been here before if (isset($node['hit']) || $node['level'] < $params['currentLevel']) { continue; } // mark that we've hit this node $params['structure'][$key]['hit'] = $node['hit'] = true; $tag = array( isset($node[$params['textField']]) ? "'".$node[$params['textField']]."'" : 'null', isset($node[$params['linkField']]) ? "'".$node[$params['linkField']]."'" : 'null' ); if (!$this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] || strlen($node[$params['textField']]) > $this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset]) { $this->_strlenByLevel[$params['currentLevel'] - $this->_levelOffset] = strlen($node[$params['textField']]); }; $tigraMenu = $tigraMenu.$this->_openSubMenu($tag); // see if it has children if (($node['r'] - 1) != $node['l']) { $children = array(); // harvest all the children $tempStructure = $params['structure']; foreach ($tempStructure as $childKey => $childNode) { if (!isset($childNode['hit']) && $node['rootid'] == $childNode['rootid'] && $node['l'] < $childNode['l'] && $node['r'] > $childNode['r'] && $childNode['level'] > $params['currentLevel']) { // important that we assign it by reference here, so that when the child // marks itself 'hit' the parent loops will know $children[] =& $params['structure'][$childKey]; } } $recurseParams = $params; $recurseParams['structure'] = $children; $recurseParams['currentLevel']++; $tigraMenu = $tigraMenu.$this->_createFromStructure($recurseParams); } $tigraMenu = $tigraMenu.$this->_closeSubMenu(); } return $tigraMenu; } // }}} // {{{ _openMenu() /** * Returns the string which opens the JavaScript menu * * @access private * @param int $menu_id ID of the menu needed to use more than one menu on a page * @return string The JavaScript piece */ function _openMenu($menu_id=1) { $str = false; $str = $str."var MENU_ITEMS".$menu_id." = new Array();\n"; $str = $str."MENU_ITEMS".$menu_id." = [\n"; return $str; } // }}} // {{{ _openSubMenu() /** * Returns the string which opens a submenu within the JavaScript menu * * @access private * @param array $tag Contains the content of the current item (name, link) * @return string The JavaScript piece */ function _openSubMenu($tag) { $rtag = implode(', ', $tag); return "\n[".$rtag.','; } // }}} // {{{ _closeMenu() /** * Closes the JavaScript array * * @access private * @return string The JavaScript piece */ function _closeMenu() { return '];'; } // }}} // {{{ _closeSubMenu() /** * Closes the JavaScript array of a submenu * * @access private * @return string The JavaScript piece */ function _closeSubMenu() { return "\n],"; } // }}} // {{{ _addStyles() /** * Creates the JavaScript code which sets the styles for each level * * @access private * @param int $menu_id ID of the menu needed to use more than one menu on a page * @param array $rootStyles Array of style attributes for the top items * @param array $childStyles Array of style attributes for the sub items * @return string The JavaScript piece */ function _addStyles($menu_id, $rootStyles, $childStyles = false) { if (!$childStyles) { $childStyles = $rootStyles; } $styles = array(); foreach ($rootStyles as $key => $val) { foreach ($val as $skey => $sval) { $styles["'$key'"][$skey][] = "'$sval'"; } } foreach ($childStyles as $key => $val) { foreach ($val as $skey => $sval) { for ($i = 1; $i <= $this->_levels; $i++) { $styles["'$key'"][$skey][] = "'$sval'"; } } } $menustyles = false; $menustyles = $menustyles . 'var MENU_STYLES'.$menu_id." = new Array();\n"; foreach ($styles as $key => $val) { $menustyles = $menustyles.'MENU_STYLES'.$menu_id."[$key] = [\n"; foreach ($val as $skey => $sval) { $menustyles = $menustyles . "'$skey', [".implode(', ', $sval)."],\n"; } $menustyles = $menustyles."];\n"; } return $menustyles; } // }}} // {{{ _addGeometry() /** * Creates the JavaScript code which sets the position and geometry of the menu * * @access private * @param int $menu_id ID of the menu needed to use more than one menu on a page * @param array $rootGeometry Array of geometry attributes for the top items * @param array $childGeometry Array of geometry attributes for the sub items * @return string The JavaScript piece */ function _addGeometry($menu_id, $rootGeometry, $childGeometry = false) { if (!$childGeometry) { $childGeometry = $rootGeometry; } $params = array(); $geometry = array(); foreach ($rootGeometry as $key => $val) { $geometry["'$key'"][] = $val; $incr = false; if (strpos($val, ',') !== false) { list($start, $interval) = explode(',',$val); $incr = true; } $ratio = false; if ($key == 'width' && strpos($val, '*') !== false) { $ratio = trim(str_replace('*','', $val)); } if ($incr) { $val = trim($interval); if ($key == 'left' && preg_match('/[+-]/', $interval)) { $val = $params[0]['width'] + trim($val); } } elseif ($incr) { $val = trim($start); } elseif ($ratio) { $val = $ratio * $this->_strlenByLevel[0]; } $geometry["'$key'"][0] = $val; $params[0][$key] = $val; } foreach($childGeometry as $key => $val) { $incr = false; if (strpos($val, ',') !== false) { list($start, $interval) = explode(',', $val); $incr = true; } $ratio = false; if ($key == 'width' && strpos($val, '*') !== false) { $ratio = trim(str_replace('*', '', $val)); } for ($i = 1; $i <= $this->_levels; $i++) { if ($incr && isset($lastval[$key])) { $val = trim($interval); if($key == 'block_left' && preg_match('/[+-]/', $interval)) { $val = $params[$i - 1]['width'] + trim($val); } } elseif($incr) { $val = trim($start); } elseif ($ratio) { $val = $ratio * $this->_strlenByLevel[$i]; if($val < $params[0]['width']) { $val = $params[0]['width']; } } $lastval[$key] = $val; $geometry["'$key'"][] = $val; $params[$i][$key] = $val; } } $pos = false; $pos = $pos . 'var MENU_POS'.$menu_id." = new Array();\n"; foreach ($geometry as $key => $val) { $pos = $pos . 'MENU_POS' . $menu_id . "[$key] = [" . implode(', ', $val) . "];\n"; } return $pos; } // }}} // {{{ printTree() /** * Print's the current tree using the output driver * * @access public */ function printTree() { if (!$options = $this->_getOptions('printTree')) { return PEAR::raiseError("TigraMenu::printTree() needs options. See TigraMenu::setOptions()", NESEO_ERROR_NO_OPTIONS, PEAR_ERROR_TRIGGER, E_USER_ERROR); } echo $this->_openMenu($options['menu_id']) . $this->_structTigraMenu .$this->_closeMenu(); echo "\n\n"; echo $this->_addStyles($options['menu_id'], $options['rootStyles'], $options['childStyles']); echo "\n\n"; echo $this->_addGeometry($options['menu_id'], $options['rootGeometry'], $options['childGeometry']); } // }}} } ?>