<?php
################################################################################
##              -= YOU MAY NOT REMOVE OR CHANGE THIS NOTICE =-                 #
## --------------------------------------------------------------------------- #
##  ApPHP TreeMenu Pro version 3.1.0 (26.11.2015)     		                   #
##  Developed by:  ApPHP <info@apphp.com>                                      #
##  License:       GNU LGPL v.3                                                #
##  Site:          http://www.apphp.com/php-tree-menu/                         #
##  Copyright:     ApPHP TreeMenu (c) 2010 - 2015. All rights reserved.        #
##                                                                             #
##  Additional modules (embedded):                                             #
##  -- jQuery v1.8.3 (JavaScript Library)                                      #
##                                                                             #
################################################################################

class TreeMenu
{
    // PUBLIC                                     PRIVATE
    // -------                                    --------
    // constructor                                ShowPlusMinus
    // AddNode                                    ShowIcon
    // AddNodeWithInnerHTML                       ShowNode
    // AddNodeAction                              LoadFiles
    // ShowTree
    // ShowNodes                                  STATIC
    // ShowContent                                --------
    // BuildFromFolder                            Simplify
    // BuildNodeFromFolder                        CheckInput
    // SetHttpVars
    // UseDefaultFolderIcons
    // UseDefaultFileIcons
    // ShowNumSubNodes
    // ShowExpandCollapseAllButtons

    // IsRefreshSelectedNodesAllowed
    // AllowRefreshSelectedNodes
    // IsSortedByName
    // SortByName
    // SortByNameAction
    // SetDefaultNode
    // GetDefaultNodeId
    // SetPostBackMethod
    // SetId
    // GetId
    // SetCaption
    // GetPath
    // SetStyle
    // GetStyle
    // GetSecondaryPath
    // SetSecondaryPath
    // GetNumChildren
    // Debug
    // GetDebug
    // Version
    // SetAnimationEffect
	// GetParent
	// GetExtraData
	// SetExtraData
	// SetParentNode
	// GetParentNode
	// GetPathCaption
	// GetPathID
	// GetNode
	// GetNodePathID

    // ShowDebugInfo
    // SetDirection
    // UseAnchor
    // CreateAnchorAuto
	// GetNodePathCaption

    // STATIC
    // --------
    // GetFormattedMicrotime
    // SimplifyPath
    // GetParam

    //--- PRIVATE DATA MEMBERS --------------------------------------------------
    private $nodes;
    private $numChildren;
    private $caption;
    private $id;
    private $isDebug;
    private $postBackMethod;
    private $style;
    private $path;
    private $width;
    private $height;
    private $httpVars;
    private $useDefaultFolderIcons;
    private $useDefaultFileIcons;
    private $nodesWithInnerHTML;
    private $showNumSubNodes=false;
    private $secondaryPath;
    private $refreshSelectedNodesAllowed=true;
    private $anchor='treemenu';
    private $useAnchor=false;
    private $createAnchorAuto=true;
    private $direction='ltr';
    private $nl="\n";
    private $showExpandCollapseAllButtons=false;
    private $isSortedByName=false;
    private $allowedEffects = array('none','slow','normal','fast');
    private $animationEffect = "none";
    private $defaultNodeId;
    private $token;


    //--- CONSTANTS -------------------------------------------------------------
    const version='3.1.0';
    const defaultEmptyMessage='No nodes defined';

    //--- PRIVATE STATIC DATA MEMBERS -------------------------------------------
    private static $bad_chars='><|?*:,\'';

    /**
    *	Creates a tree menu
    */
    public function __construct()
    {
    	$this->nodes=array();
    	$this->numChildren=0;
    	$this->caption='';
    	$this->id=1;
    	$this->defaultNodeId='';
    	$this->isDebug=false;
    	$this->postBackMethod='ajax';
    	$this->style='default';
    	if(defined('TREEMENU_DIR')) $this->path = TREEMENU_DIR;
        else $this->path = '';
        $this->secondaryPath=$this->path;
    	$this->width='auto';
    	$this->height='auto';
    	$this->httpVars=array();
    	$this->nodesWithInnerHTML=array();
    }

    /**
	*	Adds a new child node to this menu (calculates new node's id and calls function AddNodeAction)
	      @param $caption - node's caption
	      @param $file - file associated with this node
	      @param $icon - icon associated with this node
	      @param $isFolder - is true when this node corresponds to a folder
	*/
    public function AddNode($caption,$file='',$icon='undefined',$isFolder=false)
    {
         $id=$this->GetId().'_'.++$this->numChildren;

         return $this->AddNodeAction($caption,$id,$file,$icon,$isFolder);
    }

    /**
	*   Adds a node to list of nodes with inner HTML content
	     @param $node
	*/
    public function AddNodeWithInnerHTML($node)
    {
    	if($node->HasInnerHTML())
       	   $this->nodesWithInnerHTML[count($this->nodesWithInnerHTML)]=$node;
    }

    /**
	*   Returns whether sorting by name is applied to this menu's nodes
	*/
    public function IsSortedByName()
    {
    	return $this->isSortedByName;
    }
    /**
	*	Adds a new child node to this menu
	      @param $caption - node's caption
	      @param $id - node's id
	      @param $file - file associated with this node
	      @param $icon - icon associated with this node
	      @param $isFolder - is true when this node corresponds to a folder
	*/
    public function AddNodeAction($caption,$id,$file,$icon,$isFolder)
    {
       if(preg_match('/^[a-z|A-Z|0-9|_]+$/',$id)==0)
          return;
       if($pos=strrpos($file,'?'))
          $filename=substr($file,0,$pos);
       else $filename=$file;
       if(strpbrk($filename,self::$bad_chars))
          $file='';
       if(strpbrk($icon,self::$bad_chars))
          $icon='';
       $this->nodes[$id]=new Node($caption,$id,$file,$this,$icon,$isFolder);
       return $this->nodes[$id];
    }

    /**
	*	Shows tree structure on the screen
	    (is used when this is an independent menu)
	*/
    public function ShowTree()
    {
    	$startTime = $this->GetFormattedMicrotime();
    	if(self::GetParam("treemenu_token".$this->id, "post") != ""
        	&& isset($_SESSION['treemenu_token'])
        	&& self::GetParam('treemenu_token'.$this->id, "post") != $_SESSION['treemenu_token'])
        {
        	if($this->isDebug){
            	$output.= $this->nl."Token validation was not successful";
            	$output.= $this->ShowDebugInformation($startTime);
            }
            echo $output;
        	return;
     	}
       if($_SERVER["REQUEST_METHOD"] == "POST")
       {
       		if(!empty($_SERVER['HTTP_REFERER']))
       		{
       			if(!preg_match('/'.$_SERVER['HTTP_HOST'].'/', $_SERVER['HTTP_REFERER'])){
       				@header("HTTP/1.1 403 Forbidden");
       				exit("Access forbbiden");
       			}
       		}
       }
       $output = '';
       $output.= $this->nl.'<div class="tm_'.htmlentities($this->style).'">';
       if($this->showExpandCollapseAllButtons)
       {
       		$output.= $this->nl.'<span class="hyperlink" onclick="__tmExpandAll(\''.$this->GetId().'\')">Expand all</span>';
       		$output.= $this->nl.'<span class="link_separator">|</span>';
       		$output.= $this->nl.'<span class="hyperlink" onclick="__tmCollapseAll()">Collapse all</span>';
       		$output.= $this->nl.'<br>';
       }
       $output.= $this->nl.'<p>';
       if($this->useAnchor && $this->createAnchorAuto)
            $output.= '<a name="'.$this->anchor.'"></a>';
       if($this->caption!='')
            echo '<span id="caption" class="tmCaption">'.$this->caption.'</span><br /><br />';
       if($this->numChildren==0)
       {
            echo self::defaultEmptyMessage;
            return;
       }
       if($this->isDebug)
          $startTime = self::GetFormattedMicrotime();
       $output.= $this->LoadFiles();
       if($this->postBackMethod!='ajax')
       {
          $params='';
          if($this->postBackMethod=='post')
          {
             foreach($this->httpVars as $key)
                if(self::GetParam($key, 'get')!= '')
                   $params .= '&amp;'.$key.'='.self::GetParam($key, 'get');
             if($params!='')
             {
                $params = '?'.substr($params, 5);
             }
          }
          //if(self::CheckInput($_SERVER['SCRIPT_NAME']))
          //{
              if($this->useAnchor) $action=$_SERVER['SCRIPT_NAME'].$params.'#'.$this->anchor;
              else $action=$_SERVER['SCRIPT_NAME'].$params;
          //}
          $output.= $this->nl.'<form name="frmnodes'.$this->GetId().'" id="frmnodes'.$this->GetId().'" action="'.$action.'" method="'.$this->postBackMethod.'">';
       }
       $output.= '<div class="nodes">';
       if($this->postBackMethod!='ajax')
       {
          if($this->postBackMethod=='get')
             foreach($this->httpVars as $key)
                if(self::GetParam($key, 'get')!= '')
                   $output.= '<input type="hidden" id="'.$key.'" name="'.$key.'" value="'.self::GetParam($key, 'get').'" />';
       }
       else
       {
       	  $output.= $this->nl.'<input type="hidden" id="debug'.$this->GetId().'" value="'.$this->GetDebug().'" />';
       	  $output.= $this->nl.'<input type="hidden" id="folderIcons'.$this->GetId().'" value="'.$this->useDefaultFolderIcons.'" />';
       	  $output.= $this->nl.'<input type="hidden" id="fileIcons'.$this->GetId().'" value="'.$this->useDefaultFileIcons.'" />';
       	  $output.= $this->nl.'<input type="hidden" id="showNumFiles'.$this->GetId().'" value="'.$this->showNumSubNodes.'" />';
       }
       $output.= $this->nl.'<input type="hidden" id="path'.$this->GetId().'" value="'.$this->GetPath().'" />';
       $output.= $this->nl.'<input type="hidden" id="refresh'.$this->GetId().'" value="'.$this->refreshSelectedNodesAllowed.'" />';
       $output.= $this->nl.'<input type="hidden" id="style'.$this->GetId().'" name="style" value="'.$this->GetStyle().'" />';
       $output.= $this->nl.'<input type="hidden" id="animationEffect'.$this->id.'" value="'.$this->animationEffect.'" />';
       $output.= $this->nl.'<input type="hidden" id="direction'.$this->GetId().'" name="direction" value="'.$this->direction.'" />';
       if(self::GetParam('nodeid'.$this->GetId(),'request') != '')
          $selectedNodeId = self::GetParam('nodeid'.$this->GetId(),'request');
       else $selectedNodeId = $this->defaultNodeId;
       $paneled = ($this->style == 'paneled');
       $output.= $this->nl."<input type='hidden' id='treemenu_token".$this->GetId()."' name='treemenu_token".$this->GetId()."' value='".$this->token."' />";
       $output.= $this->nl.'<input type="hidden" id="nodeid'.$this->GetId().'" name="nodeid'.$this->GetId().'" value="'.$selectedNodeId.'" />';
       if(!$paneled)
          $output.= $this->nl.'<ul class="tmTree">';
       else $output.= $this->nl.'<table cellpadding="0" cellspacing="0" style="width:100%;">';
       for($i=1;$i<=$this->getNumChildren();$i++)
       		$childNodes[$this->GetId().'_'.$i] = $this->nodes[$this->GetId().'_'.$i]->GetCaption();
       if ($this->IsSortedByName())
       		asort($childNodes);
       $i=1;
       for (reset($childNodes); ($k=key($childNodes)); next($childNodes))
       {
       		if($paneled)
       		    $output.= '<tr class="tmRow"><td><ul class="tmTree">';
          	$output.= $this->ShowNode($k,$i==1,$i==$this->getNumChildren(),true,false);
          	$i++;
          	if($paneled)
            	$output.= '</ul></td></tr>';
       }
       if(!$paneled)
          $output.= '</ul>';
       else $output.= '</table>';

       $output.= '</div>';
       if($this->postBackMethod != 'ajax')
          $output.= '</form>';

       $output.= $this->nl.'<!-- END This code was generated by treemenu.class.php v.'.self::version.'(http://www.apphp.com/php-tree-menu/index.php) END -->';
       $output.= $this->nl;
       if($this->isDebug)
       		$output.= $this->ShowDebugInformation($startTime);
       $output.= $this->nl.'</p>';
       $output.= $this->nl."</div>";
       echo $output;
    }

    /**
    *   Shows tree structure on the screen
        (is used when this menu is a part of some bigger menu)
    */
    public function ShowNodes()
    {
       $output = '';
       $output.= $this->nl.'<ul class="tmSubTree">';
       for($i=1;$i<=$this->numChildren;$i++)
       {
          $output.= $this->ShowNode($this->id.'_'.$i,$i==1,$i==$this->numChildren,false);
       }
       $output.= $this->nl.'</ul>'.$this->nl;
       return $output;
   	}

   	/**
     *	Sets default node
     *      @param $defaultNode - new default node
    */
    public function SetDefaultNode($defaultNode)
    {
        if(!is_a($defaultNode,"Node")){
            if($this->isDebug) echo '<span style="color:#ff0000">Error: "SetDefaultNode" function parameter is not a valid Node object</span>';
            return;
        }
        $this->defaultNodeId=$defaultNode->GetId();
    }

    /**
	*	Shows the loaded content
	*/
    public function ShowContent()
    {
       $output = '';
       $output.= $this->nl.'<div class="tmContainer" id="container'.$this->GetId().'">';
       if(self::GetParam('nodeid'.$this->GetId(),'request') != '')
          $selectedNodeId = self::GetParam('nodeid'.$this->GetId(),'request');
       else $selectedNodeId = $this->defaultNodeId;
       if(isset($this->nodes[$selectedNodeId]))
       	  $selectedNode = $this->nodes[$selectedNodeId];
       else $selectedNode = null;

       foreach($this->nodesWithInnerHTML as $nodeWithInnerHTML)
       {
           if($selectedNode!=null && $nodeWithInnerHTML==$selectedNode)
              $output.= $this->nl.'<div id="code'.$nodeWithInnerHTML->GetId().'">'.$nodeWithInnerHTML->GetInnerHTML().'</div>';
           else// if($this->postBackMethod=='ajax')
              $output.= $this->nl.'<div id="code'.$nodeWithInnerHTML->GetId().'" style="display:none">'.$nodeWithInnerHTML->GetInnerHTML().'</div>';
       }
       $output.= '<div id="innercontainer'.$this->GetId().'">';

       if($selectedNode !=null)
          $output.= $selectedNode->ShowContent($this->GetDebug());
       $output.= '</div>';
       $output.= $this->nl.'</div>';
       echo $output;
    }

    /**
    *   Applies sorting by name to a node's subnodes
	       @param $node
	       @param $isRecursive - whether the sorting should be applied recursively to the node's subnodes
    */
    public function SortByNameAction($node,$isRecursive = false)
	{
		if($isRecursive)
			for($i=1;$i<=$node->GetNumChildren();$i++)
          		$this->nodes[$node->GetId().'_'.$i]->SortByName($isRecursive);
	}

	/**
    *   Applies sorting by name to this menu
	       @param $isRecursive - whether the sorting should be applied recursively to the node's subnodes
    */
	public function SortByName($isRecursive = false)
	{
		$this->isSortedByName = true;
		$this->SortByNameAction($this,$isRecursive);
	}

	/**
    *   Shows plus or minus sign to the left of the text
	       @param $symbol - symbol to display (plus or minus)
	       @param $node - the symbol's node
	       @param $isIndependent - is true when this is a stand-alone menu
    */
    private function ShowPlusMinus($symbol,$node,$isIndependent)
    {
        $output = '';
        $output.= '<img id="imge'.$node->GetId().'" class="tmImage" alt="icon" name="tmImage"';
        if(!$isIndependent)
           $output.= ' onmouseover="__tmHighlight(this)" onmouseout="__tmLowlight(this)" ';
        if($this->direction == 'rtl')
	        $output.= ' src="'.$this->secondaryPath.'styles/'.htmlentities($this->style).'/images-right/'.$symbol.'.gif"';
        else $output.= ' src="'.$this->secondaryPath.'styles/'.htmlentities($this->style).'/images/'.$symbol.'.gif"';
        	if($node->GetFolder()!='')
        	{
        		//if(!self::CheckInput($_SERVER['SCRIPT_NAME'])) $pathToFile = '';
        		/*else*/ $pathToFile=substr($_SERVER['SCRIPT_NAME'],0,strrpos($_SERVER['SCRIPT_NAME'],'/')+1);
           		$output.= ' onclick=\'__tmSwitch("'.$node->GetId().'","'.self::SimplifyPath($pathToFile.$node->GetFolder()).'")\' />';
           	}
           	else $output.= ' onclick=\'__tmSwitch("'.$node->GetId().'","")\' />';
        return $output;
    }

    /**
     *	Sets treemenu token
    */
    public function SetToken()
    {
    	if(!isset($_SESSION['treemenu_token']))
    	{
    		$_SESSION['treemenu_token'] = md5(uniqid(rand(), true));
        }
        $this->token = $_SESSION['treemenu_token'];
    }

    /**
    *   Shows node's icon
	       @param $node
    */
    private function ShowIcon($node)
    {
    	$output = '';
    	if($node->GetIcon()!='undefined' && $node->GetIcon()!='')
        {
            $output.= '<img id="pic'.$node->GetId().'" class="tmIcon" src="'.$node->GetIcon().'" alt="icon" />';
        }
        return $output;
    }

    /**
	*	Shows a node
	      @param $id - node's id
	      @param $last - whether the node is the last one on this level
	      @param $isIndependent - is true when this is an independent menu
	      @param $isStandAlone - is true when this is a stand-alone menu
	*/
    private function ShowNode($id,$first,$last,$isIndependent=true,$isStandAlone=false)
    {
        $output = '';
        $node=$this->nodes[$id];
        if($this->useDefaultFolderIcons && $node->IsFolder() && ($node->GetIcon()==''||$node->GetIcon()=='undefined'))
           $node->ChooseIcon();
        if(!$node->IsFolder() && $node->GetIcon()=='' && $this->useDefaultFileIcons)
           $node->ChooseIcon();
        // defining if this node is selected
        if(self::GetParam('nodeid'.$this->GetId(),'request') != '')
           $selectedNodeId = self::GetParam('nodeid'.$this->GetId(),'request');
        else $selectedNodeId = $this->defaultNodeId;
        if($selectedNodeId == $node->GetId())
        {
           if($this->refreshSelectedNodesAllowed)
               $class = 'tmSelected';
           else $class = 'tmSelectedPass';
        }
        else $class = 'tmRegular';
        if(!$isStandAlone)
        {
        	if ($last)
        		$output.= '<li class="tmLast">';
	        else if($first && $isIndependent)
    	    	$output.= '<li class="tmFirst">';
        	else
        		$output.= '<li class="tmSingle">';
        }
       //if(!self::CheckInput($_SERVER['SCRIPT_NAME'])) $pathToFile = '';
       /*else*/ $pathToFile=substr($_SERVER['SCRIPT_NAME'],0,strrpos($_SERVER['SCRIPT_NAME'],'/')+1);
       $symbol = '';
        // if this node has subnodes
        if($node->getNumChildren()>0 || $node->Getfolder()!='')
        {
        	// if this node is expanded
        	if(self::GetParam('node'.$node->GetId(), 'request') == "e" || $class=='tmSelected' || $class=='tmSelectedPass'
        	   || !(strpos($selectedNodeId,$node->GetId())===false))
        	{
                $liclass = 'tmExpanded';
                if($this->style == 'paneled' && $node->GetLevel() == 1)
                    $symbol = 'minus-first';
                else $symbol = 'minus';
                $value = 'e';
            }
            // if this node is collapsed
            else{
                $liclass='tmCollapsed';
                if($this->style=='paneled' && $node->GetLevel() == 1) $symbol='plus-first';
                else $symbol = 'plus';
                $value = 'c';
            }
        	$output.= '<div class="'.$liclass.'" id="tm'.$node->GetId().'">';
        	// displaying plus or minus sign to the left of the text
            if($this->direction != 'rtl') $output.= $this->ShowPlusMinus($symbol,$node,$isIndependent);


        }
        // if node has no subnodes
        else
            $output.= '<div class="tmNoChildren" id="tm'.$node->GetId().'">';


        $output.= '<span id="span'.$node->GetId().'" class="tmNode" ';
        if($node->GetTooltip()!='')
           $output.= ' title="'.$node->GetTooltip().'" ';
        if(!$isIndependent)
           $output.= 'onmouseover="__tmHighlight(this)" onmouseout="__tmLowlight(this)"';
        $selectedNode = $this->nodes[$node->GetDefaultNodeId()];
        if($selectedNode->HasInnerHTML())
        {
	        $output.= 'onclick=\'__tmPostBackAjax("'.$selectedNode->GetId().'","'.$selectedNode->GetFile().'","code")\'>';
        }
        else if($selectedNode->IsOpenNewWindow())
        {
            if($this->postBackMethod == 'ajax')
	            $output.= ' onclick=\'window.location.href="'.$selectedNode->GetFile().'"\'>';
            else $output.= ' onclick=\'__tmPostBackNewWindow("'.$selectedNode->GetId().'","'.$this->id.'","'.$selectedNode->GetFile().'")\'>';
        }
        else if($this->postBackMethod == 'ajax')
        {
           if($selectedNode->GetFolder()=='')
           {
              if($selectedNode->IsPicture())
                 $output.= 'onclick=\'__tmPostBackAjax("'.$selectedNode->GetId().'","'.$selectedNode->GetFile().'","file")\'>';
              else $output.= 'onclick=\'__tmPostBackAjax("'.$selectedNode->GetId().'","'.self::SimplifyPath($pathToFile.$selectedNode->GetFile()).'","file")\'>';
           }
           else $output.= 'onclick=\'__tmPostBackAjax("'.$selectedNode->GetId().'","'.self::SimplifyPath($pathToFile.$selectedNode->GetFolder()).'","filesystem")\'>';
        }
        else $output.= 'onclick=\'__tmPostBack("'.$selectedNode->GetId().'")\'>';

        //displaying this node's icon
        if ($this->direction != 'rtl') $output.= $this->ShowIcon($node);
        // displaying this node's text
        $output.= '<span id="text'.$node->GetId().'" class="'.$class.'">'.$node->GetCaption();
        if($this->showNumSubNodes && $node->GetNumChildren()>0)
        	$output.= '&nbsp('.$node->GetNumChildren().')';
        $output.= '</span>';
        if ($this->direction == 'rtl') $output.= $this->ShowIcon($node);
        $output.= '</span>';
        if($node->getNumChildren()>0 || $node->Getfolder()!='')
        {
           if($this->direction == 'rtl')
	           $output.= $this->ShowPlusMinus($symbol,$node,$isIndependent);
           // hidden field which stores this node's state ('c' for collapsed and 'e' for expanded)
        	$output.= '<input type="hidden" id="node'.$node->GetId().'" name="node'.$node->GetId().'" value="'.$value.'" />';
        }
        //displaying this node's subnodes if there are any
        if($node->getNumChildren()>0)
        {
        	$output.= '<br />';
        	if($last)
        	   $output.= '<ul class="tmSubTree-last">';
         	else $output.= '<ul class="tmSubTree">';
         	$childNodes = array();
        	for($i=1;$i<=$node->getNumChildren();$i++)
        		$childNodes[$node->GetId().'_'.$i] = $this->nodes[$node->GetId().'_'.$i]->GetCaption();

        	if ($node->IsSortedByName())
        		asort($childNodes);
        	$i=1;
        	for (reset($childNodes); ($k=key($childNodes)); next($childNodes))
        	{
        		$output.= $this->ShowNode($k,false,$i==$node->getNumChildren());
        		$i++;
        	}

            $output.= '</ul>';
        }
           $output.= '</div>';
           if(!$isStandAlone)
        $output.= '</li>';

        return $output;
    }

    /**
	*	Builds a tree menu from contents of a folder
	       @param $folder - relative path to the folder
	       @param $isIndependent - is true when this is an independent menu
	*/
    public function BuildFromFolder($folder,$isIndependent=true,$expandSubfolders=false)
	{
		$this->BuildNodeFromFolder($this,$folder,$isIndependent,$expandSubfolders);
    }

    /**
	*	Builds a tree menu from contents of a folder
	      @param $root - the node
	      @param $folder - relative path to the folder
	      @param $isIndependent - is true when this is an independent menu
	*/
    public function BuildNodeFromFolder($root,$folder,$isIndependent,$expandSubfolders)
	{
        $folder=self::SimplifyPath($folder);
        $dirs=array();
        $files=array();
        if (is_dir($folder)) {
           if ($dir = opendir($folder)) {
           	   while (false !== ($file = readdir($dir))){
           	   	  if($file != '.' && $file != '..' && is_dir($folder.'/'.$file))
           	   	     $dirs[count($dirs)]=$file;
           	   	  else if($file != '.' && $file != '..')
           	   	     $files[count($files)]=$file;
               }
               closedir($dir);
    	    }
        }
        natcasesort($dirs);
        natcasesort($files);

        foreach($dirs as $dir)
        {
            $node=$root->AddNode($dir,'','',true);
            if($this->postBackMethod!='ajax' || $expandSubfolders)
               $this->BuildNodeFromFolder($node,$folder.'/'.$dir,$isIndependent,$expandSubfolders);
            else $node->SetFolder($folder.'/'.$dir);
        }
        foreach($files as $file)
        {
        	$node=$root->AddNode($file,$folder.'/'.$file,'',false);
            if($node->IsPicture()&&!$isIndependent)
            	$node->SetFile($this->secondaryPath.'inc/'.$node->GetFile());
        }
    }

    /**
	*	Loads CSS and JS files
	*/
	private function LoadFiles()
	{
		$output = '';
	    if(!file_exists($this->path.'styles/'.htmlentities($this->style).'/style.css')) $this->style='default';
        if($this->direction == 'rtl'){
	    	$output.= $this->nl.'<link href="'.$this->path.'styles/'.htmlentities($this->style).'/style-right.css" rel="stylesheet" type="text/css" />';
	        $output.= $this->nl.'<link href="'.$this->path.'styles/common-right.css" rel="stylesheet" type="text/css" />';
	   	}else{
	   		$output.= $this->nl.'<link href="'.$this->path.'styles/'.htmlentities($this->style).'/style.css" rel="stylesheet" type="text/css" />';
	   		$output.= $this->nl.'<link href="'.$this->path.'styles/common.css" rel="stylesheet" type="text/css" />';
	    }
       	if(file_exists($this->path.'js/script.js')){
       		$output.= $this->nl.'<script type="text/javascript" defer="defer" src="'.$this->path.'js/script.js"></script>';
       	}
       	$output.= $this->nl.'<!--[if IE]>';
       	$output.= '<link href="'.$this->path.'styles/commonIE.css" rel="stylesheet" type="text/css" />';
       	if (file_exists($this->path.'styles/'.htmlentities($this->style).'/styleIE.css')){
        	$output.= $this->nl.'<link href="'.$this->path.'styles/'.htmlentities($this->style).'/styleIE.css" rel="stylesheet" type="text/css" />';
       	}
       	$output.= $this->nl.'<![endif]-->';
       	if($this->postBackMethod=='ajax' || $this->animationEffect != 'none'){
       		///$output.= '<script src="'.$this->path.'js/jquery-1.8.3.min.js" type="text/javascript"></script>';
			$output.= '<script>window.jQuery || document.write(\'<script type="text/javascript" src="'.$this->path.'js/jquery-1.8.3.min.js">\x3C/script>\')</script>'."\r\n";
       	}
       	return $output;
    }

    /**
    *	Sets variables that used to get access to the page (like: my_page.php?act=34&id=56 etc.)
           @param $vars
	*/
    public function SetHttpVars($vars)
    {
    	$this->httpVars = $vars;
    }

    /**
	*	Sets whether or not default icons are used for folder nodes
			@param $use - false|true
	*/
	public function UseDefaultFolderIcons($use = false)
	{
		if($use === true || strtolower($use) == 'true') $this->useDefaultFolderIcons = true;
		if($use === false || strtolower($use) == 'false') $this->useDefaultFolderIcons = false;
	}

    /**
	*	Sets whether or not default icons are used for file nodes
			@param $use - false|true
	*/
	public function UseDefaultFileIcons($use = false)
	{
		if($use === true || strtolower($use) == 'true') $this->useDefaultFileIcons = true;
		if($use === false || strtolower($use) == 'false') $this->useDefaultFileIcons = false;
	}

    /**
	*	Sets if number of sub-nodes is displayed in brackets to the left of every node
			@param $show - false|true
	*/
	public function ShowNumSubNodes($show = false)
	{
		if($show === true || strtolower($show) == 'true') $this->showNumSubNodes = true;
	}

    /**
	*	Sets whether 'Expand all' and 'Collapse all' buttons are shown
			@param $show - false|true
	*/
	public function ShowExpandCollapseAllButtons($show = false)
	{
		if($show === true || strtolower($show) == 'true') $this->showExpandCollapseAllButtons = true;
	}
    /**
    *	Returns if refreshing selected Nodes is enabled
    */
    public function IsRefreshSelectedNodesAllowed()
    {
        return $this->refreshSelectedNodesAllowed;
    }

    /**
    *	Allows or prohibits to refresh selected Nodes
    *		@param $allow - true or false
    */
    public function AllowRefreshSelectedNodes($allow = false)
    {
        if($allow === true || strtolower($allow) == 'true') $this->refreshSelectedNodesAllowed = true;
        if($allow === false || strtolower($allow) == 'false') $this->refreshSelectedNodesAllowed = false;
    }

    /**
    *	Sets appear/disappear effect for the nodes
    *		@param $effect - none|slow|normal|fast|integer number
    */
    public function SetAnimationEffect($effect = "none")
    {
        if(in_array($effect,$this->allowedEffects) || is_int($effect))
        	$this->animationEffect = $effect;
    }

    /**
	*	Sets postback method
	*		@param $postback_method
	*/
	public function SetPostBackMethod($postback_method = 'post')
	{
		if(strtolower($postback_method) == 'get') $this->postBackMethod = 'get';
		else if(strtolower($postback_method) == 'ajax') $this->postBackMethod = 'ajax';
		else $this->postBackMethod = 'post';
	}
   /**
     *	Returns default node's id
     *
    */
    public function GetDefaultNodeId()
    {
        return $this->defaultNodeId;
    }
    /*
    * Sets menu's unique identifier
    *      @param $id
    */
    public function SetId($id)
    {
        $this->id=$id;
    }

    /**
	*	Returns menu's unique identifier
	*/
    public function GetId()
    {
       return $this->id;
    }

    /**
	*	Sets menu's caption
	*     @param $caption - new caption
	*/
    public function SetCaption($caption)
    {
    	$this->caption=$caption;
    }

    /**
	*	Returns menu's path
	*/
    public function GetPath()
    {
    	return $this->path;
    }

    /**
	*	Sets style
	*     @param $style - new style
	*/
    public function SetStyle($style)
    {
    	//if(file_exists($this->path.'styles/'.$style.'/style.css'))
        $this->style = is_array($style) ? 'default' : $style;
    }

    /**
	*	Returns menu's style
	*/
    public function GetStyle()
    {
    	return $this->style;
    }

    /**
	*	Returns menu's style
	*/
    public function GetSecondaryPath()
    {
    	return $this->secondaryPath;
    }

    /**
	*	Sets menu's secondary path
	*/
    public function SetSecondaryPath($secondaryPath)
    {
    	$this->secondaryPath=$secondaryPath;
    }

    /**
	*	Returns number of nodes on the first level
	*/
	public function GetNumChildren()
    {
       return $this->numChildren;
    }

    /**
    *	Sets debug mode
    *		@param $mode
	*/
	public function Debug($mode = false)
	{
		if($mode === true || strtolower($mode) == 'true') $this->isDebug = true;
	}

    /**
    *	Gets debug mode
	*/
	public function GetDebug()
	{
		return $this->isDebug;
	}

    /**
	*	Returns TreeMenu's version
	*/
    public function Version()
    {
       return self::version;
    }

    /**
    *	Gets formatted microtime
	*/
    public static function GetFormattedMicrotime()
    {
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }

    /**
	*	Shows debug information
	*      @param $startTime - time when the script started
	*/
    public function ShowDebugInformation($startTime)
    {
       $output = '';
       $endTime = self::GetFormattedMicrotime();
	   $output.= '<div id="tmDebug" style="margin: 10px auto; text-align:left; color:#000096;">';

	   $output.= 'Debug Info: (Total running time: <span id="tmTime'.$this->id.'">'.round((float)$endTime - (float)$startTime, 6).'</span> sec.) <br />========<br />';
	   $output.= '<br />GET: <br />--------<br />';
	   $output.= '<pre>';
	   $output.= print_r($_GET, true);
	   $output.= '</pre><br />';
	   $output.= 'POST: <br />--------<br />';
	   $output.= '<pre>';
	   $output.= print_r($_POST, true);
	   $output.= '</pre><br />';

	   $output.= 'NODES: <br />--------<br />';
	   $output.= '<pre><table style="color:#000096;">';
       foreach($this->nodes as $node)
       {
          $output.= '<tr>';
          $output.= '<td>'.$node->GetId().'</td>';
          $output.= '<td>'.$node->GetCaption().'</td>';
          $output.= '</tr>';
       }
       $output.= '</table></pre>';
       $output.= '</div>';
       return $output;
    }

    /**
	*	Simplifies dirs like /./dir ordir/../../dir/ and returns a simplified pathname
	*    	@param $path - path to be simplified
	*/
    public static function SimplifyPath($path)
    {
       if(strlen($path)>0 && $path[0]=='/')
          return '/'.self::SimplifyPath(substr($path,1));
       if($path=='')
          return $path;
       $dirs = explode("/",$path);
       for($i=0; $i<count($dirs);$i++)
       {
          if($dirs[$i]=='.' || $dirs[$i]=='')
          {
             array_splice($dirs,$i,1);
             $i--;
          }
          if($dirs[$i]=='..')
          {
            $c = count($dirs);
            $dirs=self::Simplify($dirs, $i);
            $i-= $c-count($dirs);
          }
       }
       return implode("/",$dirs);
    }

    /**
	*	Sets menu's direction (left-to-right or right-to-left)
	*		@param $dir
	*/
    public function SetDirection($dir = 'ltr')
    {
    	$this->direction = (strtolower($dir) == 'rtl') ? 'rtl' : 'ltr';
    }

    /**
	*	Auxiliary function
	*       @param $dirs
	*       @param $idx
	*/
    private static function Simplify($dirs, $idx)
    {
       if($idx==0) return $dirs;
       if($dirs[$idx-1]=='..') self::Simplify($dirs, $idx-1);
       else  array_splice($dirs,$idx-1,2);
       return $dirs;
    }

    /**
	*	Checks input string for suspicious code
	*       @param $input - input string
	*	    @param $level - security level
	*/
    private static function CheckInput($input, $level = 'medium')
    {
    	if($input == '') return true;
		if(is_array($input)) return false;
		
    	$error = 0;
    	$bad_string = array('%20union%20', ' /*', '*/ union /*', '+union+', 'load_file', 'outfile', 'document.cookie', 'onmouse', '<script', '<iframe', '<applet', '<meta', '<style', '<form', '<img', '<body', '<link', '_GLOBALS', '_REQUEST', '_GET', '_POST', 'include_path', 'prefix', 'http://', 'https://', 'ftp://', 'smb://' );
    	foreach($bad_string as $string_value)
    	{
    		if(strstr($input, $string_value)) $error = 1;
    	}
    	if((preg_match('/<[^>]*script*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*object*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*iframe*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*applet*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*meta*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*style*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*form*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*img*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*onmouseover*\'?[^>]*>/i', $input)) ||
        (preg_match('/<[^>]*body*\'?[^>]*>/i', $input)) ||
        (preg_match('/\([^>]*\'?[^)]*\)/i', $input)) ||
        (preg_match('/ftp:\/\//i', $input)) ||
        (preg_match('/https:\/\//i', $input)) ||
        (preg_match('/http:\/\//i', $input)) )
        {
        	$error = 1;
        }
        $ss = $_SERVER["HTTP_USER_AGENT"];
        if ((preg_match('/libwww/i',$ss)) ||
        (preg_match('/^lwp/i',$ss))  ||
        (preg_match('/^Jigsaw/i',$ss)) ||
        (preg_match('/^Wget/i',$ss)) ||
        (preg_match('/^Indy\ Library/i',$ss)) )
    	{
        	$error = 1;
    	}
    	if($error){
    		return false;
   		}
    	return true;
	}

    /**
    *	Gets a GET- or POST- parameter
    *      @param $key - parameter's key
    *	   @param $postbackMethod - postback method
    */
    public static function GetParam($key, $postbackMethod)
    {
        if(strtolower($postbackMethod) == 'get')
            return isset($_GET[$key]) && self::CheckInput($_GET[$key]) ? $_GET[$key] : '';
        if(strtolower($postbackMethod) == 'post')
            return isset($_POST[$key]) && self::CheckInput($_POST[$key]) ? $_POST[$key] : '';
        return isset($_REQUEST[$key]) && self::CheckInput($_REQUEST[$key]) ? $_REQUEST[$key] : '';
    }

    /**
    *	Use an anchor to navigate to treemenu object after a node is clicked
    *		@param $use - true or false
    */
    public function UseAnchor($use = false)
    {
        if($use === true || strtolower($use) == 'true') $this->useAnchor = true;
        if($use === false || strtolower($use) == 'false') $this->useAnchor = false;
    }

    /**
    *	Create the anchor automatically
    *		@param $create - true or false
    */
    public function CreateAnchorAuto($create = false)
    {
        if($create === true || strtolower($create) == 'true') $this->createAnchorAuto = true;
        if($create === false || strtolower($create) == 'false') $this->createAnchorAuto = false;
    }

	/**
	 * Gets node
	 */
	public function GetNode($id) { 
		return isset($this->nodes[$id]) ? $this->nodes[$id] : null; 
	} 
  
	/**
	 * Gets node path ID
	 */
	public function GetNodePathID($id) { 
		return isset($this->nodes[$id]) ? $this->nodes[$id]->GetPathID() : null; 
	} 
  
	/**
	 * Gets node path caption
	 */
	public function GetNodePathCaption($id) { 
		return isset($this->nodes[$id]) ? $this->nodes[$id]->GetPathCaption() : null;  
	} 
}

/**
*	Class Node
*      represents a separate node
*      last date modified: 10.07.2011
*
*/
class Node
{
    // PUBLIC
    // -------
    // constructor
    // AddNode
    // BuildFromFolder
    // ShowContent
    // GetCaption
    // GetLevel
    // GetFile
    // SetFile
    // GetIcon
    // SetIcon
    // GetTooltip
    // SetTooltip
    // GetId
    // GetNumChildren
    // IsPicture
    // IsHTML
    // IsPHP
    // IsText
    // ChooseIcon
    // SetFolder
    // GetFolder
    // SetInnerHTML
    // HasInnerHTML
    // GetInnerHTML
    // OpenNewWindow
    // IsOpenNewWindow
    // SortByName

    //--- PRIVATE DATA MEMBERS --------------------------------------------------
    private $caption;
    private $selected=false;
    private $id;
    private $parent;
    private $numChildren=0;
    private $file;
    private $icon;
    private $level;
    private $tooltip;
    private $innerHTML;
    private $openNewWindow;
    private $folder;
    public $isFolder=false;
    private $isSortedByName;
    private $nl="\n";
	private $extraData = null; 
	private $parentNode = null; 

    //--- PRIVATE STATIC DATA MEMBERS -------------------------------------------
    private static $bad_chars='><|?*:,\'';

    /**
    *	Creates a new node
	*        @param $caption - node's caption
    *        @param $id - node's id
    *        @param $file - name of the file associated with this node
    *        @param $parent - menu which contains this node
    *        @param $icon - icon associated with this node
    *        @param $isFolder - is true when this node corresponds to a folder
	*/
    function __construct($caption,$id,$file,$parent,$icon,$isFolder=false)
	{
    	if(preg_match('/^[a-z|A-Z|0-9|_]/',$id)==0)
        	$id=0;
        $this->id=$id;
        $this->defaultNodeId=$id;
        $this->caption=$caption;
        $this->parent=$parent;

        $this->level=substr_count($id,'_');
        if($pos=strrpos($file,'?'))
           $filename=substr($file,0,$pos);
        else $filename=$file;
        if(strpbrk($filename,self::$bad_chars))
           $this->file='';
        else $this->file=$file;
        $this->isFolder=$isFolder;
        if(strpbrk($icon,self::$bad_chars))
           $this->icon='';
        else $this->icon=$icon;
	}

    /**
	*	Adds a new child node to this node
	*      @param $caption - node's caption
	*      @param $file - file associated with this node
	*      @param $icon - icon associated with this node
	*      @param $isFolder - is true when this node corresponds to a folder
	*/
    public function AddNode($caption,$file='',$icon='undefined',$isFolder=false)
    {
        if(!is_a($this->parent,'TreeMenu')){
           echo '<span style="color:#ff0000">Error: node '.$this->caption.' has no valid parent object</span>';
           return;
        }
        
		$id = $this->GetId().'_'.++$this->numChildren;
		$n = $this->parent->AddNodeAction($caption, $id, $file, $icon, $isFolder);
		$n->SetParentNode($this); 
        return $n; 
    }

    /**
	*	Builds a tree menu from contents of a folder
	*      @param $folder - relative path to the folder
	*/
    public function BuildFromFolder($folder,$expandSubfolders=false)
    {
    	if(!is_a($this->parent,'TreeMenu'))
        {
           echo '<span style="color:#ff0000">Error: node '.$this->caption.' has no valid parent object</span>';
           return;
        }
        $this->parent->BuildNodeFromFolder($this,$folder,true,$expandSubfolders);
    }

    /**
	*	Shows contents of the file which is associated with this node
	*/
    public function ShowContent($debug)
    {
        $output = '<br />'.$this->nl.$this->nl;
        if($pos=strrpos($this->file,'?'))
        {
            $get_parameters=substr($this->file,$pos+1);
            $this->file=substr($this->file,0,$pos);
            $get_parameters_array = explode('&', $get_parameters);
            foreach($get_parameters_array as $get_parameter)
            {
            	$key_value=explode('=',$get_parameter);
            	$_GET[$key_value[0]]=$key_value[1];
            }
        }
        if($this->IsPicture())
        {
       	    $output.= '<img id="tmTree_image" src="'.$this->file.'" alt="icon" />';
   	    }
    	else
    	{
    	    if($this->file=='')
         	{
         		if(!$this->IsFolder())
         			$output.= 'No content associated with this node';
         	}
    	    else if(!file_exists($this->file)){
    	    	if($debug)
	                $output.= '<div style="color:#000096;">File "'.$this->file.'" not found</div>';
            }
            else if($this->IsPHP()){
                flush();
                ob_start();
                include_once($this->file);
                $contents = ob_get_clean();
                $output.= $contents;
            }
            else if($this->IsHTML()){
                $str = file_get_contents($this->file);
                if(preg_match('/<head.*?>(.+?)<\/head>/si',$str,$head) != 0){
                    if(preg_match_all('/<script.*?>(.*?)<\/script>/si',$head[1],$scripts) != 0)
                        foreach($scripts[0] as $script){
                            $output .= $script;
                        }
                    if(preg_match_all('/<style.*?>(.*?)<\/style>/si',$head[1],$styles) != 0){
                        foreach($styles[0] as $style){
                            $output .= $style;
                        }
                    }
                }
                if(preg_match('/<body.*?>(.+?)<\/body>/si',$str,$body) != 0){
                    $output.= print_r($body[1], true);
                }else{
                    $output.= print_r($str, true);
                }
            }
            else if($this->IsText()){
                $output.= htmlspecialchars(file_get_contents($this->file), ENT_QUOTES);
            }
        }
        return $output;
    }

    /**
    *	Returns the node's caption
    */
    public function GetCaption()
    {
    	 return $this->caption;
    }

    /**
    *	Returns the node's level
	*/
    public function GetLevel()
    {
    	return $this->level;
    }

    /**
	*	Returns the node's file
	*/
    public function GetFile()
    {
    	return $this->file;
    }

    /**
	*	Sets the node's file
	*/
    public function SetFile($file)
    {
        $this->file=$file;
    }

    /**
	*	Returns the node's icon
	*/
    public function GetIcon()
    {
       return $this->icon;
    }

    /**
    *	Sets node's icon
	*      @param $icon
	*/
    public function SetIcon($icon)
    {
		$this->icon=$icon;
    }

    /**
    *	Returns the node's tooltip
    */
    public function GetTooltip()
    {
        return $this->tooltip;
    }

    /**
    *	Sets node's tooltip
    *       @param $tooltip
    */
    public function SetTooltip($tooltip)
    {
        $this->tooltip= htmlspecialchars($tooltip, ENT_QUOTES);
    }

    /**
	*	Returns the node's id
	*/
    public function GetId()
    {
		return $this->id;
    }

    /**
	*	Returns amount of subnodes associated with this node
	*/
    public function GetNumChildren()
    {
        return $this->numChildren;
    }

    /**
    *	Checks if file associated with this node is a graphic file
	*/
 	public function IsPicture()
  	{
        $extension=strtolower(substr(strrchr($this->file,'.'),1));
        if($extension=='jpg'||$extension=='gif'||$extension=='bmp'||$extension=='tif'||$extension=='png'||$extension=='jpeg')
           return true;
        else return false;
    }

    /**
	*	Checks if file associated with this node is a hypertext file
	*/
    public function IsHTML()
    {
        if($pos=strrpos($this->file,'?'))
           $filename=substr($this->file,0,$pos);
        else $filename=$this->file;
        $extension=strtolower(substr(strrchr($filename,'.'),1));
        if($extension=='htm'||$extension=='xml'||$extension=='html')
           return true;
        else return false;
    }

    /**
	*	Checks if file associated with this node is a PHP file
	*/
    public function IsPHP()
    {
        if($pos=strrpos($this->file,'?'))
           $filename=substr($this->file,0,$pos);
        else $filename=$this->file;
        $extension=strtolower(substr(strrchr($filename,'.'),1));
        if($extension=='php')
           return true;
        else return false;
    }

    /**
	*	Checks if file associated with this node is a text file
	*/
    public function IsText()
    {
        $extension=strtolower(substr(strrchr($this->file,'.'),1));
        if($extension=='txt')
           return true;
        else return false;
    }

    /**
    * Chooses icon according to file's type if it wasn't defined by user
    */
    public function ChooseIcon()
    {
    	$this->icon=$this->parent->GetSecondaryPath().'styles/'.$this->parent->GetStyle().'/images/';
        if($this->IsFolder())
        {
            if(TreeMenu::GetParam('node'.$this->GetId(),'request') == "e")
               $this->icon = $this->icon.'folderopened';
            else $this->icon = $this->icon.'folder';
        }
        else if($this->IsPicture())
           $this->icon = $this->icon.'picture';
        else if($this->IsText())
           $this->icon = $this->icon.'text';
        else if($this->IsHTML())
           $this->icon = $this->icon.'html';
        else $this->icon=$this->icon.'file';
        if($this->parent->GetStyle() == 'default' || ($this->parent->GetStyle() == 'paneled' && $this->IsFolder()))
           $this->icon = $this->icon.'.gif';
        else $this->icon = $this->icon.'.jpg';
    }

    /**
	*	Sets folder which this node corresponds to
	*   	@param $folder
	*/
    public function SetFolder($folder)
    {
    	$this->folder = $folder;
    }

    /**
	*	Returns folder which this node corresponds to
	*/
    public function GetFolder()
    {
        return $this->folder;
    }

    /**
	*	Sets this node's inner HTML content
	*      @param $innerHTML
	*
	*/
    public function SetInnerHTML($innerHTML)
    {
    	$this->innerHTML=$innerHTML;
    	if($innerHTML!='')
    	   $this->parent->AddNodeWithInnerHTML($this);
    }

    /**
	*	Returns true if this node has inner HTML content (false otherwise)
	*/
    public function HasInnerHTML()
    {
       return $this->innerHTML!='';
    }

    /**
	*	Gets this node's inner HTML content
	*/
    public function GetInnerHTML()
    {
       return $this->innerHTML;
    }

    public function IsSortedByName()
    {
    	return $this->isSortedByName;
    }
    /**
    *   Applies sorting by name to this node
	       @param $isRecursive - whether the sorting should be applied recursively to the node's subnodes
    */
    public function SortByName($isRecursive = false)
	{
		$this->isSortedByName = true;
		if($isRecursive)
			$this->parent->SortByNameAction($this,true);
	}
    /**
	*	Sets if this node must be opened in a new window
	*       @param $open
	*/
    public function OpenNewWindow($open = false)
	{
		if($open === true || strtolower($open) == 'true')
		   $this->openNewWindow = true;
	}

    /**
	*	Returns if this node must be opened in a new window
	*/
    public function IsOpenNewWindow()
    {
        return $this->openNewWindow;
    }

    /**
    *  Returns true if this node corresponds to a folder or has sub-nodes
    */
    public function IsFolder()
    {
       return $this->numChildren > 0 || $this->isFolder;
    }

    /**
     *	Sets default node
     *      @param $defaultNode - new default node
    */
    public function SetDefaultNode($defaultNode)
    {
        if(!is_a($defaultNode,"Node")){
            if($this->isDebug) echo '<span style="color:#ff0000">Error: "SetDefaultNode" function parameter is not a valid Node object</span>';
            return;
        }
        $this->defaultNodeId=$defaultNode->GetId();
    }

    /**
     *	Returns default node's id
     */
    public function GetDefaultNodeId()
    {
        return $this->defaultNodeId;
    }
	
    /**
     *	Returns parent node
     */
	public function GetParent() { 
		return $this->parent; 
	} 

    /**
     *	Returns extra data 
     */
	public function GetExtraData() { 
		return $this->extraData; 
	} 

    /**
     *	Sets extra data 
     */
	public function SetExtraData($v) { 
		$oldextraData = $this->extraData; 
		$this->extraData = $v; 
		return $oldextraData; 
	} 

    /**
     *	Sets parent node
     */
	public function SetParentNode($v) { 
		$this->parentNode = $v; 
	} 

    /**
     *	Gets parent node
     */
	public function GetParentNode($v) { 
	  return $this->parentNode; 
	} 

    /**
     *	Gets path caption
     */
	public function GetPathCaption() { 
		$a = array(); 
		$n = $this; 
		while (!empty ($n)) { 
			$a[] = $n->GetCaption(); 
			$n = $n->GetParentNode(); 
		} 
		return array_reverse($a); 
	} 

    /**
     *	Gets path ID
     */
	public function GetPathID() { 
		$a = array(); 
		$n = $this; 
		while (!empty ($n)) { 
			$a[] = $n->GetID(); 
			$n = $n->GetParentNode(); 
		} 
		return array_reverse($a); 
	}
	
}
