<?php
class clsHtmlTag
{
  var $rawtext;
  var $parsed;
  var $prefix;
  var $name;
  var $attributes;
  var $inner_html;

  function clsHtmlTag($text=NULL)
  {
    $this->SetRawText($text);
    if(strlen($text))
      $this->parse();
  }

  function Clear()
  {
    $this->parsed = FALSE;
    $this->rawtext = NULL;
    $this->prefix = "";
    $this->name = "";
    $this->attributes = NULL;
    $this->inner_html = "";
  }

  function ValidTag()
  {
    return (strlen($this->prefix) && strlen($this->name));
  }

  function SetRawText($text)
  {
    $this->Clear();
    if($text != NULL)
      $text = trim($text);
    $this->rawtext = $text;
  }

  function GetAttributeByName($attr)
  {
    if(is_array($this->attributes))
    {
      $attr = strtolower($attr);
      if( array_key_exists($attr,$this->attributes))
      {
        return $this->attributes[$attr];
      }
      else
        return FALSE;
    }
    else
      return FALSE;
  }
  
  function SetAttributeByName($attr,$value)
  {
  	 if(!is_array($this->attributes))
  	   $this->attributes = array();
  	 $this->attributes[$attr] = $value;  
  }

  function GetAttributeByIndex($index)
  {
    if(is_array($this->attributes))
    {
      if($index < count($this->attributes))
      {
        return $this->attributes[$index];
      }
      else
        return FALSE;
    }
    else
      return FALSE;
  }
  function ParseAttributes($attributes)
  {
	unset($output);
	$attribute = "";
	$attributes = str_replace("\\>",">",$attributes);
	$attributes = str_replace("\\<","<",$attributes);
	$attributes = str_replace("\\\\","\\",$attributes);
		
	while(strpos($attributes,"=")>0)
	{
		$pos = strpos($attributes,"=");
		$attribute = trim(substr($attributes,0,$pos));
		$attributes = trim(substr($attributes,$pos+1));
		$pos2 = strpos($attributes,"\"");
		$pos3 = strpos($attributes,"'");
		if(!($pos3===false) and !($pos2===false) and ($pos3<$pos2)) $pos2 = $pos3;
		if(!($pos3===false) and ($pos2===false) and (($pos3<$pos) or ($pos==0))) $pos2 = $pos3;
			
		if(!($pos2===false) and (($pos2<$pos) or ($pos==0)))
		{
			if (substr($attributes,0,1) == "\"")
			{
				$pos = strpos($attributes,"\"",1);
				$val = substr($attributes,1,$pos-1);
			}
			elseif (substr($attributes,0,1) == "'")
			{
				$pos = strpos($attributes,"'",1);
				$val = substr($attributes,1,$pos-1);
			}
			else
			{
				$pos1 = strpos($attributes,"=",1);
				$val = substr($attributes,0,$pos1);
				$pos1a = strrpos($val," ");
				$pos = $pos1-(strlen($val)-$pos1a);
				$val = substr($val,0,$pos1a);
			}
				
			while (strpos($attribute," ")>0)
			{
				$pos1 = strpos($attribute," ");
				$attr1 = substr($attribute,0,$pos1);
				$output[$attr1] = null;
				$attribute = trim(substr($attribute,$pos1+1));
			}
				
			$output[strtolower($attribute)] = $val;
			$attributes = trim(substr($attributes,$pos+1));
		    
		}
		elseif ($pos>0)
		{
			if (strpos($attributes,"=")>0)
			{
				$pos = strpos($attributes,"=");
				$val = substr($attributes,0,$pos);
			}
			else
			{
				$val = $attributes;
			}
				
			$pos2 = strrpos($val," ");
			if($pos2>0)
			{
				$len = strlen($val);
				$val = substr($val,0,$pos2);
				$attributes = trim(substr($attributes,($pos-$len)+$pos2));
			}
			else
			{
				$len = strlen($val);
				$attributes = trim(substr($attributes,$len));
			}
				
			while (strpos($attribute," ")>0)
			{
				$pos1 = strpos($attribute," ");
				$attr1 = substr($attribute,0,$pos1);
				$output[$attr1] = null;
				$attribute = trim(substr($attribute,$pos1+1));
			}							
			$output[strtolower($attribute)] = $val;		    
		}
		else
		{
			while (strpos($attribute," ")>0)
			{
				$pos1 = strpos($attribute," ");
				$attr1 = substr($attribute,0,$pos1);
				$output[$attr1] = null;
				$attribute = trim(substr($attribute,$pos1+1));
			}
			$output[strtolower($attribute)] = $attributes;
		}
	}
	
	if(strlen(trim($attributes))>0)
	{
		while (strpos($attribute," ")>0)
		{
				$pos1 = strpos($attribute," ");
				$attr1 = substr($attribute,0,$pos1);
				$output[$attr1] = null;
				$attribute = trim(substr($attribute,$pos1+1));
		}		
		$output[strtolower($attributes)] = null;			
	}
		
	if (isset($output)) 
		return($output);
  }  

  function parse()
  { 
  	global $objSession;
  	
    $html = $this->rawtext;
    $html = substr($html,1,strlen($html)-2);
    if(substr($html,strlen($html)-1,1)=="/")
    {
      $html = substr($html,0,strlen($html)-1);
    }

    $tagparts = explode(" ",$html,2);
    $tagname = $tagparts[0];
    $attribs = array();
    if(count($tagparts)>0)
    	$attribs = $this->ParseAttributes( isset($tagparts[1]) ? $tagparts[1] : '');

    if(is_object($objSession) && is_array($attribs))
    {
    	$referer = $objSession->GetVariable("Template_Referer");
        foreach($attribs as $key=>$value)
        {
      	  if($value=="_referer_")
      	  	$attribs[$key] = $referer;	      	  
        }
    }
    $name = explode(":",$tagname);
    $this->prefix = strtolower($name[0]);
    $this->name = strtolower($name[1]);
    if(is_array($attribs))
    {
    	foreach($attribs as $key=>$value)
    	{
    		if(!strlen($value))
    		{
    			$attribs[$key]=1;
    		}
    	}
    	$this->attributes = $attribs;
    }
    else 
     	$this->attributes = array();
    $this->parsed=TRUE;
  }

  	function Execute()
  	{
    	$func = $this->name;     
		$override = "_".$func;
		
		if( function_exists($override) )
		{ 
	  		$ret = @$override($this->attributes);
		}
		else 
		{	
    		if(function_exists($func))
    		{ 
      			$ret = @$func($this->attributes);
    		}
    		else
    		{
      			//$ret = "{Unknown Tag:" .$this->name."}";
    		}
		}
    	return $ret;
  	}
}

class clsTemplate
{
  var $source;
  var $name;
  var $out;
  var $error;
  
  function clsTemplate($template=NULL)
  {
	$this->name=$template;
	$this->source=NULL;
    $this->out = NULL;
    $this->error = "";
  }

  function LoadByFileName($file,$SupressError=FALSE)
  {
      if(file_exists($file))
      {
        $fp = fopen ($file, "r");
        if($fp)
        {
          $this->source = fread($fp, filesize ($file));
          $this->source = trim($this->source);
          fclose($fp);
          return TRUE;
        }
        else
        {
          if(!$SupressError) 	
  		    $this->error = "lu_read_error";      	
          return FALSE;
        }
      }
      else
      {
      	if(!$SupressError)
      	 	$this->error = "lu_missing_error";
        return FALSE;
      }
  }

  function HasExtension()
  {
  	$t = strtolower($this->name);
  	
  	if(strlen($t))
  	{
  		return (substr($t,-4)==".tpl");
  	}
  	else
  	  return false;
  }

  function TemplateFilename($template_root)
  {  
  	if(!$this->HasExtension())
  	{
 	  $filename = $template_root.$this->name.".tpl";
  	}
  	else
  	  $filename = $template_root.$this->name;
  	return $filename;
  }  	
  
  function LoadFile($template_root="",$SupressError=FALSE)
  {
  	$filename = $this->TemplateFilename($template_root);
	if(file_exists($filename))
	{
        //echo "Loading $filename from Filesystem<br>\n";

	  $fp = @fopen ($filename, "r");
	  if($fp)
	  {
	    $this->source = fread($fp, filesize ($filename));
	    $this->source = trim($this->source);
	    fclose($fp);
	    return TRUE;
	  }
	  else
	  {
	  	if(!$SupressError)
	  		$this->error = "lu_read_error"; 
	    return FALSE;
	  }
	}
	else
	{
	  if(!$SupressError)
	  	$this->error = "lu_missing_error";	
	  return FALSE;
	}
  }

  function WriteFile($template_root="")
  {
      $filename = $this->TemplateFilename($template_root);
      $pos = strrpos($this->name,"/");

      if(!is_dir(substr($template_root,0,-1)))
          @mkdir(substr($tempalate_root0,-1));

      if($pos>0)
          $path=$template_root.substr($this->name,0,$pos);

      if(!is_dir($path))
      {
          @mkdir($path);
      }

      if(strlen($this->out))
      {      
        $fp = @fopen($filename, "w");
        if($fp)
        { 
          fwrite($fp,$this->out);
          fclose($fp);
          return TRUE;
        }
        else
            return FALSE;      
      }
      else
          return TRUE;
  }
}

class clsTemplateList
{
  var $templates;
  var $root_dir;
  var $ErrorStr;
  var $ErrorNo;
  var $stack;
  var $SkipIncludes = 0;

  function clsTemplateList($root_dir)
  {
    $this->templates = array();
    $this->root_dir = $root_dir;
    $this->ErrorStr = "";
    $this->ErrorNo = 0;
    $this->SkipIncludes = 0;
    $this->stack = array();
  }

  function InStack($template)
  {
    if(in_array($template,$this->stack))
    {
      return TRUE;
    }
    else
      return FALSE;
  }

  function GetTemplate($name,$SupressError=FALSE)
  {
  	if(!strlen($name))
  	{
  	  $ret = FALSE;
  	  if(!$SupressError)
  	  {
  	    $this->ErrorNo = -2;
  	    $this->ErrorStr=language("lu_template_error").":".language("lu_no_template_error");
  	  }
  	}
  	else
  	{ 
      $ret = FALSE;
      foreach($this->templates as $n => $t)
      {
        if($n == $name)
        {
            $ret=$t;
            break;
        }
      }
      if(!is_object($ret))
      {
        $ret = new clsTemplate($name);
        if($ret->LoadFile($this->root_dir,$SupressError))
        {
          $this->templates[$name]=$ret;
        }
        else
        {
          if(!$SupressError)
          {
            $this->ErrorNo = -1;
            $this->ErrorStr = language("lu_template_error").":".language($ret->error).":"."'$name'";
            LogEntry($this->ErrorStr);
          }
        }
      }
  	}  
    return $ret;
  }

  	function GetTemplateCache($template,$SupressError=FALSE)
  	{
      	global $CurrentTheme, $pathtoroot;
		$ret = '';
      	if( $CurrentTheme->Get("CacheTimeout") > 0)
      	{      
        	$id = $CurrentTheme->GetTemplateId($template);
        	if($id)
        	{
            	$exp = isset($CurrentTheme->ParseCacheDate[$id]) ? $CurrentTheme->ParseCacheDate[$id] : false;
            	if($exp)
            	{
              		//echo "$template Cache expires: ".date("m-d-Y h:m s",$exp)."<br>\n";
              		if( $exp > time() )
              		{
                		/* look for a cache file */
                		$t = new clsTemplate($template);
                		$dir = $CurrentTheme->ThemeDirectory();
                		if( $t->LoadFile($dir."/_cache/",$SupressError) ) $ret = $t->source;
              		}
            	}
        	}
      }
      return $ret;
  }

  function SaveTemplateCache($objTemplate)
  {
     global $CurrentTheme, $objThemes, $pathtoroot;

	if(!is_object($CurrentTheme))
      $CurrentTheme = $objThemes->GetItem($m_var_list["theme"]);

     if($CurrentTheme->Get("CacheTimeout")>0)
     {     
       $TemplateId = $CurrentTheme->GetTemplateId($objTemplate->name);
       if($TemplateId)
       {
         if(isset($CurrentTheme->ParseCacheDate[$TemplateId]))
         {                   
               //echo "Writing Template ".$objTemplate->name."<br>\n";
             $interval = $CurrentTheme->ParseCacheTimeout[$TemplateId];
             $CurrentTheme->UpdateFileCacheData($TemplateId,time()+$interval);
             $dir = $CurrentTheme->ThemeDirectory()."/_cache/";
             $objTemplate->WriteFile($dir);
         }
       }
     }
     
  }

  function IncludeTemplate($tag, $SupressError=FALSE)
  {
      global $LogLevel, $objSession,$objLanguages;

      $SupressError = ($SupressError || $tag->GetAttributeByName("_supresserror"));
      switch($tag->name)
      {
      case "perm_include":
          $perms = $tag->GetAttributeByName("_permission");
          if(strlen($perms))
          {
            $plist = explode(",",$perms);
            $value=0;
            $CheckSys = $tag->GetAttributeByName("_system");
            for($p=0;$p<count($plist);$p++)
            {
                if($objSession->HasCatPermission(trim($plist[$p])))
                {
                    $value = 1;
                    break;
                }
                else
                {
                    if($CheckSys)
                    {
                        if($objSession->HasSystemPermission(trim($plist[$p])))
                        {
                            $value = 1;
                            break;
                        }
                    }
                }
            }
            if($value)
            {
               $t = $tag->GetAttributeByName("_template");
            }
            else
                $t = $tag->GetAttributeByName("_noaccess");    
          }
          else
          {
              $module = $tag->GetAttributeByName("_module");
              if(strlen($module))
              {
                if(ModuleEnabled($module))
                {
                  $t = $tag->GetAttributeByName("_template");
                }
                else
                  $t = $tag->GetAttributeByName("_noaccess");
              }
          }
      break;
	  case "lang_include":
	    $lang = $tag->GetAttributeByName("_language");
	    if(strlen($lang))
	    {
	    	$LangId = $objSession->Get("Language");
	    	$l = $objLanguages->GetItem($LangId);
	    	if(strtolower($lang)==strtolower($l->Get("PackName")))
	    	{
	    		$t = $tag->GetAttributeByName("_template");
	    	}
	    }	    
	  break;
      case "include":
        $t = $tag->GetAttributeByName("_template");
      break; 
      }//switch

      LogEntry("Parsing $t\n");
      $LogLevel++;
      
      if($t)
	  {
	    if(!$this->InStack($t))
	    {
	      //return $this->ParseTemplate($t);
          //if(!$tag->GetAttributeByName("_nocache"));
          $ret = $this->GetTemplateCache($t,$SupressError);
          if(!strlen($ret))
          {          
            $req = $tag->GetAttributeByName("_dataexists");
            if($req)
            {
              global $content_set;
              $content_set=1;
              $temp = $this->ParseTemplate($t,0,0,$SupressError);
              if($content_set)
              {              	
                $ret = $temp;
              }
              else
              {
              	$t_nodata = $tag->GetAttributeByName("_nodatatemplate");
              	if(strlen($t_nodata))
              	{
              	  $nodata_tag = new clsHtmlTag();
              	  $nodata = $tag;
              	  $nodata->attributes = $tag->attributes;
              	  $nodata->SetAttributeByName("_template",$t_nodata);
              	  $nodata->SetAttributeByName("_nodatatemplate","");
              	  $nodata->SetAttributeByName("_dataexists","");
              	  $ret = $this->IncludeTemplate($nodata,$SupressError);	
              	}
              	else
                	$ret = "";
              }
            }
            else
                $ret = $this->ParseTemplate($t,0,0,$SupressError);
          }
	    }
	    else
	      $ret = "";
	  }
      $LogLevel--;      
      if($LogLevel<0)
          $LogLevel=0;
      LogEntry("Finished Parsing $t\n");
      return $ret;
  }

  /* <inp:mod_include _Template=".." [_perm=".."] [_modules=".."] [_system=".."] */
  function ModuleInclude($tag)
  {
    global $var_list;
    
    $o = "";
    $el = new clsHtmlTag();
    $el->attributes = $tag->attributes;
    $el->parsed=1;
    $t = $tag->attributes["_template"];
    if(!strlen($t))
        $t = $var_list["t"];
        
    $el->name = "perm_include";
    $tpath = GetModuleArray("template");

    if(!strlen($tag->attributes["_modules"]))
    {
        $mods = array_keys($tpath);
    }
    else
        $mods = explode(",",$tag->attributes["_modules"]);
            
    foreach($mods as $m)
    {       	
       if($t==$var_list["t"] && !strlen($tpath[$m]))
           continue;
       $el->attributes = $tag->attributes;
       $el->attributes["_template"] = $tpath[$m].$t;
       $el->attributes["_module"] = $m;  
       
       if(strlen($tag->attributes["_nodatatemplate"]))
       {    
         $el->attributes["_nodatatemplate"] = $tpath[$m].$tag->attributes["_nodatatemplate"];
       }

       //print_pre($el);
       $o .= $this->IncludeTemplate($el,true);       
    }
    if(!strlen($o) && strlen($tag->attributes["_nodatamaintemplate"]))
      $o = $this->ParseTemplate($tag->attributes["_nodatamaintemplate"]);
    return $o;
  }

  function ParseTag($raw)
  {
	$tag = new clsHtmlTag($raw);
    $res = "";
    switch($tag->name)
    {
    case "lang_include":
    case "include":
    case "perm_include":
        $res = $this->IncludeTemplate($tag);
    break;
    case "mod_include":
          $res = $this->ModuleInclude($tag);  
    break;
    default:
	    //print_pre($tag);
	    $res = $tag->Execute();
      break;
    }
    unset($tag);
    return $res;
  }

  function ParseTemplateText($text)
  {         
      $html = $text;
      $search = "<inp:";
      $next_tag = strpos($html,$search);
      $left = substr($html,0,5);
      while($next_tag || $left=="<inp:")
      {
          $left = "";
          $closer = strpos(strtolower($html),">",$next_tag);
          $tmp = substr($html,$next_tag,$closer+1 - $next_tag);
          while(substr($html,$closer-1,1)=="\\" && $closer < strlen($html))
          {
            $closer = strpos(strtolower($html),">",$closer+1);
          }
          $end_tag = strpos($html,"/>",$next_tag);
                   
          if(($end_tag < $closer || $closer == 0))
          {
            $tagtext = substr($html,$next_tag,($end_tag - $next_tag)+2);
            $pre = substr($html,0,$next_tag);
            $post = substr($html,$end_tag+2);
            $inner = $this->ParseTag($tagtext);
            $html = $pre.$inner.$post;
          }
          else
          {
          	
          	$OldTagStyle = "</inp>";
          	
          	## Try to find end of TagName
          	$TagNameEnd = strpos($html, " ", $next_tag);
          	
          	## Support Old version
//          	$closer = strpos(strtolower($html),"</inp>",$next_tag);          
          	if ($TagNameEnd)
          	{   			
          		$Tag = strtolower(substr($html, $next_tag, $TagNameEnd-$next_tag));
          		$TagName = explode(":", $Tag);          		
          		if (strlen($TagName[1]))
          			$CloserTag = "</inp:".$TagName[1].">";
          	}          	
          	else
          	{
          		$CloserTag = $OldTagStyle;
          	}          	
            
    	  	$closer = strpos(strtolower($html), $CloserTag, $next_tag);
    	  	
    	  	## Try to find old tag closer
    	  	if (!$closer && ($CloserTag != $OldTagStyle))
    	  	{
				$CloserTag = $OldTagStyle;
    	  		$closer = strpos(strtolower($html), $CloserTag, $next_tag);	
    	  	}
    	  	
            $end_tag = strpos($html,">",$next_tag);
            
            $tagtext = substr($html,$next_tag,($end_tag - $next_tag)+1);
            $pre = substr($html,0,$next_tag);
            $inner = substr($html,$end_tag+1,$closer-($end_tag+1));
            $post = substr($html,$end_tag+1+strlen($inner) + strlen($CloserTag));
            $parsed = trim($this->ParseTag($tagtext));
            
            if(strlen($parsed))
            {
                
            	$html = $pre.$this->ParseTemplateText($inner).$post;
            }
            else
                $html = $pre.$post;
          }
          
         
          
          //$next_tag = strpos($html,"<inp:");
          $next_tag = 0;
          $next_tag = strpos($html,$search);
      }
      return $html;
  }

  function ParseTemplate($tname, $NoCache=0, $NoStack=0,$SupressError=FALSE)
  {
    $html = "";
    $t = $this->GetTemplate($tname,$SupressError);

    if(is_object($t))
    {
        if(!$NoStack)
          array_push($this->stack,$tname);
        $html = $t->source;
        $html = $this->ParseTemplateText($html);
        if(!$NoStack)
            array_pop($this->stack);
        $t->out = $html;
        if(!$NoCache)
          $this->SaveTemplateCache($t);
    }
    unset($t);
    return $html;
  }
}

class clsAdminTemplateList extends clsTemplateList
{
    function clsAdminTemplateList()
    {
        global $TemplateRoot;

        $this->clsTemplateList($TemplateRoot);
    }

    function GetTemplate($file)
    {
        $ret = FALSE;

        $ret = new clsTemplate();
        if(!$ret->LoadByFileName($file))
        {
          $this->ErrorNo = -1;
          $this->ErrorStr = "Error Loading Template '$file'";
        }
        return $ret;
    }
}

class clsTemplateChecker extends clsTemplateList
{
	var $Dependencies;
	var $TemplateType;
	var $Tags;
	function clsTemplateChecker($rootdir)
	{
		$this->clsTemplateList($rootdir);
		$this->Dependencies = Array();	
		$this->Tags = array();
		$this->TemplateType="global"; //default
	}
	
	function ParseTag($text)
	{
		$this->Tags[] = new clsHtmlTag($text);
		
		return "";
	}
	
  	function ParseTemplateText($text)
  	{         
      $html = $text;
      $search = "<inp:";
      $next_tag = strpos($html,$search);
      $left = substr($html,0,5);
      while($next_tag || $left=="<inp:")
      {
          $left = "";
          $closer = strpos(strtolower($html),">",$next_tag);
          $end_tag = strpos($html,"/>",$next_tag);
          if($end_tag < $closer || $closer == 0)
          {
            $tagtext = substr($html,$next_tag,($end_tag - $next_tag)+2);
            $pre = substr($html,0,$next_tag);
            $post = substr($html,$end_tag+2);
            $inner = $this->ParseTag($tagtext);
            $html = $pre.$inner.$post;
          }
          else
          {
            $closer = strpos(strtolower($html),"</inp>",$next_tag);
            $end_tag = strpos($html,">",$next_tag);
            $tagtext = substr($html,$next_tag,($end_tag - $next_tag)+1);
            $pre = substr($html,0,$next_tag);
            $inner = substr($html,$end_tag+1,$closer-($end_tag+1));
            $post = substr($html,$end_tag+1+strlen($inner)+6);
            $parsed = $this->ParseTag($tagtext);
            if(strlen($parsed))
            {
                $html = $pre.$inner.$post;
            }
            else
                $html = $pre.$post;
          }
          //$next_tag = strpos($html,"<inp:");
          $next_tag = 0;
          $next_tag = strpos($html,$search);
      }
      return $html;
  	}	
 	
  	function ParseTemplate($tname, $NoCache=0, $NoStack=0,$SupressError=FALSE)
  	{
    	$html = "";
    	$t = $this->GetTemplate($tname,$SupressError);
    	if(is_object($t))
    	{
        	$html = $this->ParseTemplateText($t->source);
        	$t->out = $html;
    	}
    	unset($t);
    	return $html;
  	}
  	  	
  	function ReadTemplateTags($tname)
  	{
  		$this->Tags[]=Array();
  		$this->ParseTemplate($tname,1,1,TRUE);
  		return $this->Tags;
  	}
  	
  	function GetTemplateType($tname)
  	{
  		global $ItemTypePrefixes;
  		
  		$ret = "global";
  		$this->ReadTemplateTags($tname);
  		if(count($this->Tags)>0)
  		{
  		  foreach($this->Tags as $t)
  		  {
  		  	if(in_array($t->name,$ItemTypePrefixes))
  		  	{
  		  		$ret = $t->name;
  		  		break;
  		  	}
  		  }	
  		}
  		return $ret;
  	}
  		
	function IncludeTemplate($tag, $SupressError=FALSE)	
	{
        $this->AddDependency($tag->GetAttributeByName("_template"));
        $this->AddDependency($tag->GetAttributeByName("_noaccess"));		
	}
	
	function ModuleInclude($tag)
	{
		
	}
	
	function AddDependency($template)
	{
		$template = strtolower($template);	
		if(!in_array($template,$this->Dependencies))
			$this->Dependencies[] = $template;
	}
}

function admintemplate($fileh)
{
	if(file_exists($fileh))
	{	$fd = fopen($fileh, "r");
		$ret=fread($fd, filesize($fileh));
		fclose($fd);
		return $ret;
	}
    else
        echo "Unable to load $fileh";
}

function ExtraAttributes($attribs)
{
   $html_attr = "";
   if(is_array($attribs))
   {
     foreach($attribs as $name=>$value)
     {
      if(substr($name,0,1)!="_")
         $html_attr .= $name."=\"$value\" ";
    }
   }
   return $html_attr;
}

?>