/*
 * @(#)Class.Fasttemplate.php   1.1.1 2002/05/10
 * Original Perl module CGI::FastTemplate by Jason Moore jmoore@sober.com 
 * PHP3 port by CDI cdi@thewebmasters.net 
 * PHP3 Version Copyright (c) 1999 CDI, cdi@thewebmasters.net, 
 * All Rights Reserved. 
 * Perl Version Copyright (c) 1998 Jason Moore jmoore@sober.com. 
 * All Rights Reserved. 
 * This program is free software; you can redistribute it and/or modify it 
 * under the GNU General Artistic License, with the following stipulations: 
 * Changes or modifications must retain these Copyright statements. Changes 
 * or modifications must be submitted to both AUTHORS. 
 * This program is released under the General Artistic License. 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Artistic 
 * License for more details. This software is distributed AS-IS. 
 * 
 * AiK' modifications:
 *  -- more strict dynamic templates handling, including "silently removing"
 * of unassigned  dynamic blocks 
 *  -- showDebugInfo() method that print into html conole some debug info
 */
 /**
  * The FastTemplate class provides easy and quite fast
  * template handling functionality.
  * @author Jason Moore jmoore@sober.com. 
  * @author CDI cdi@thewebmasters.net 
  * @author Artyem V. Shkondin aka AiK artvs@clubpro.spb.ru 
  * @version 1.1.2
  */
class FastTemplate {
    var $FILELIST   =   array();    //  Holds the array of filehandles
                                    //  FILELIST[HANDLE] == "fileName"
    var $DYNAMIC    =   array();    //  Holds the array of dynamic
                                    //  blocks, and the fileHandles they
                                    //  live in.
    var $PARSEVARS  =   array();    //  Holds the array of Variable
                                    //  handles.
                                    //  PARSEVARS[HANDLE] == "value"
    var $LOADED     =   array();    //  We only want to load a template
                                    //  once - when it's used.
                                    //  LOADED[FILEHANDLE] == 1 if loaded
                                    //  undefined if not loaded yet.
    var $HANDLE     =   array();    //  Holds the handle names assigned
                                    //  by a call to parse()
    var $ROOT       =   "";         //  Holds path-to-templates
    var $WIN32      =   false;      //  Set to true if this is a WIN32 server
    var $ERROR      =   "";         //  Holds the last error message
    var $LAST       =   "";         //  Holds the HANDLE to the last
                                    //  template parsed by parse()
    var $STRICT     =   true;       //  Strict template checking.
                                    //  Unresolved vars in templates will
                                    //  generate a warning when found.
//by AiK
    var $start;                     // holds time of start generation
    /**
     * Constructor.
     * @param $pathToTemplates template root.
     * @return FastTemplate
     */
    function FastTemplate ($pathToTemplates = ""){
        global $php_errormsg;
        if(!empty($pathToTemplates)){
            $this->set_root($pathToTemplates);
        }
        // by AiK
        $this->start = $this->utime();
    }   // end (new) FastTemplate ()
   /**
    * Sets template root
    * All templates will be loaded from this "root" directory
    * Can be changed in mid-process by re-calling with a new
    * value.
    * @param $root path to templates dir
    * @return void
    */
    function set_root ($root){
        $trailer = substr($root,-1);
        if(!$this->WIN32){
            if( (ord($trailer)) != 47 ){
                $root = "$root". chr(47);
            }
            if(is_dir($root)){
                $this->ROOT = $root;
            }else{
                $this->ROOT = "";
                $this->error("Specified ROOT dir [$root] is not a directory");
            }
        }else{
            // WIN32 box - no testing
            if( (ord($trailer)) != 92 ){
                $root = "$root" . chr(92);
            }
            $this->ROOT = $root;
        }
    }   // End set_root()
   /**
    * Calculates current microtime
    * I throw this into all my classes for benchmarking purposes
    * It's not used by anything in this class and can be removed
    * if you don't need it.
    * @return void
    */
    function utime () {
        $time = explode( " ", microtime());
        $usec = (double)$time[0];
        $sec = (double)$time[1];
        return $sec + $usec;
    }
   /**
    * Strict template checking, if true sends warnings to STDOUT when
    * parsing a template with undefined variable references
    * Used for tracking down bugs-n-such. Use no_strict() to disable.
    * @return void
    */
    function strict (){
        $this->STRICT = true;
    }
   /**
    * Silently discards (removes) undefined variable references
    * found in templates
    * @return void
    */
    function no_strict (){
        $this->STRICT = false;
    }
    /**
    * A quick check of the template file before reading it.
    * This is -not- a reliable check, mostly due to inconsistencies
    * in the way PHP determines if a file is readable.
    * @return boolean
    */
    function is_safe ($filename){
        if(!file_exists($filename)){
            $this->error("[$filename] does not exist",0);
            return false;
        }
        return true;
    }
   /**
    * Grabs a template from the root dir and 
    * reads it into a (potentially REALLY) big string
    * @param $template template name
    * @return string
    */
    function get_template ($template){
        if(empty($this->ROOT)){
            $this->error("Cannot open template. Root not valid.",1);
            return false;
        }
        $filename   =   "$this->ROOT"."$template";
        $contents = implode("",(@file($filename)));
        if( (!$contents) or (empty($contents)) ){
            $this->error("get_template() failure: [$filename] $php_errormsg",1);
        }
        return $contents;
    } // end get_template
   /**
    * Prints the warnings for unresolved variable references
    * in template files. Used if STRICT is true
    * @param $Line string for variable references checking
    * @return void
    */
    function show_unknowns ($Line){
        $unknown = array();
        if (ereg("({[A-Z0-9_]+})",$Line,$unknown)) {
            $UnkVar = $unknown[1];
            if(!(empty($UnkVar))){
                @error_log("[FastTemplate] Warning: no value found for variable: $UnkVar ",0);
            }
        }
    }   // end show_unknowns()
   /**  
    * This routine get's called by parse() and does the actual
    * {VAR} to VALUE conversion within the template.
    * @param $template string to be parsed
    * @param $tpl_array array of variables
    * @return string
    * @author CDI cdi@thewebmasters.net 
    * @author Artyem V. Shkondin artvs@clubpro.spb.ru 
    * @version 1.1.1
    */
    function parse_template ($template, $tpl_array){
        while ( list ($key,$val) = each ($tpl_array) ){
            if (!(empty($key))){
                if(gettype($val) != "string"){
                    settype($val,"string");
                }
        //php4 doesn't like '{$' combinations.
        $key = '{'."$key".'}';
        $template = ereg_replace("$key","$val","$template");
        //$template = str_replace("$key","$val","$template");
            }
        }
        if(!$this->STRICT){
            // Silently remove anything not already found
            $template = ereg_replace("{([A-Z0-9_]+)}","",$template);
            
            //by AiK: remove dynamic blocks
            
                $lines = split("\n",$template);
                $inside_block = false;
                $template="";
               
            while (list ($num,$line) = each($lines) ){
                if (substr_count($line, "" == "$lineTest" )
                {
                    $start = true;
                    $end = false;
                    $outside = false;
                }
                if("" == "$lineTest" )
                {
                    $start = false;
                    $end = true;
                    $outside = true;
                }
                if( (!$outside) and (!$start) and (!$end) )
                {
                    $newMacro .= "$lineData\n"; // Restore linebreaks
                }
                if( ($outside) and (!$start) and (!$end) )
                {
                    $newParent .= "$lineData\n"; // Restore linebreaks
                }
                if($end)
                {
                    $newParent .= '{'."$MacroName}\n";
                }
                // Next line please
                if($end) { $end = false; }
                if($start) { $start = false; }
            }   // end While
            $this->$Macro = $newMacro;
            $this->$ParentTag = $newParent;
            return true;
        }   // $ParentTag NOT loaded - MAJOR oopsie
        else
        {
            @error_log("ParentTag: [$ParentTag] not loaded!",0);
            $this->error("ParentTag: [$ParentTag] not loaded!",0);
        }
        return false;
    }
//  ************************************************************
//  Strips a DYNAMIC BLOCK from a template.
    function clear_dynamic ($Macro="")
    {
        if(empty($Macro)) { return false; }
        // The file must already be in memory.
        $ParentTag = $this->DYNAMIC["$Macro"];
        if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
        {
            $fileName = $this->FILELIST[$ParentTag];
            $this->$ParentTag = $this->get_template($fileName);
            $this->LOADED[$ParentTag] = 1;
        }
        if($this->$ParentTag)
        {
            $template = $this->$ParentTag;
            $DataArray = split("\n",$template);
            $newParent = "";
            $outside = true;
            $start = false;
            $end = false;
            while ( list ($lineNum,$lineData) = each ($DataArray) )
            {
                $lineTest = trim($lineData);
                if("" == "$lineTest" )
                {
                    $start = true;
                    $end = false;
                    $outside = false;
                }
                if("" == "$lineTest" )
                {
                    $start = false;
                    $end = true;
                    $outside = true;
                }
                if( ($outside) and (!$start) and (!$end) )
                {
                    $newParent .= "$lineData\n"; // Restore linebreaks
                }
                // Next line please
                if($end) { $end = false; }
                if($start) { $start = false; }
            }   // end While
            $this->$ParentTag = $newParent;
            return true;
        }   // $ParentTag NOT loaded - MAJOR oopsie
        else
        {
            @error_log("ParentTag: [$ParentTag] not loaded!",0);
            $this->error("ParentTag: [$ParentTag] not loaded!",0);
        }
        return false;
    }
//  ************************************************************
    function define ($fileList)
    {
        while ( list ($FileTag,$FileName) = each ($fileList) )
        {
            $this->FILELIST["$FileTag"] = $FileName;
        }
        return true;
    }
//  ************************************************************
    function clear_parse ( $ReturnVar = "")
    {
        $this->clear($ReturnVar);
    }
//  ************************************************************
    function clear ( $ReturnVar = "" )
    {
        // Clears out hash created by call to parse()
        if(!empty($ReturnVar))
        {
            if( (gettype($ReturnVar)) != "array")
            {
                unset($this->$ReturnVar);
                return;
            }
            else
            {
                while ( list ($key,$val) = each ($ReturnVar) )
                {
                    unset($this->$val);
                }
                return;
            }
        }
        // Empty - clear all of them
        while ( list ( $key,$val) = each ($this->HANDLE) )
        {
            $KEY = $key;
            unset($this->$KEY);
        }
        return;
    }   //  end clear()
//  ************************************************************
    function clear_all ()
    {
        $this->clear();
        $this->clear_assign();
        $this->clear_define();
        $this->clear_tpl();
        return;
    }   //  end clear_all
//  ************************************************************
    function clear_tpl ($fileHandle = "")
    {
        if(empty($this->LOADED))
        {
            // Nothing loaded, nothing to clear
            return true;
        }
        if(empty($fileHandle))
        {
            // Clear ALL fileHandles
            while ( list ($key, $val) = each ($this->LOADED) )
            {
                unset($this->$key);
            }
            unset($this->LOADED);
            return true;
        }
        else
        {
            if( (gettype($fileHandle)) != "array")
            {
                if( (isset($this->$fileHandle)) || (!empty($this->$fileHandle)) )
                {
                    unset($this->LOADED[$fileHandle]);
                    unset($this->$fileHandle);
                    return true;
                }
            }
            else
            {
                while ( list ($Key, $Val) = each ($fileHandle) )
                {
                    unset($this->LOADED[$Key]);
                    unset($this->$Key);
                }
                return true;
            }
        }
        return false;
    }   // end clear_tpl
//  ************************************************************
    function clear_define ( $FileTag = "" )
    {
        if(empty($FileTag))
        {
            unset($this->FILELIST);
            return;
        }
        if( (gettype($Files)) != "array")
        {
            unset($this->FILELIST[$FileTag]);
            return;
        }
        else
        {
            while ( list ( $Tag, $Val) = each ($FileTag) )
            {
                unset($this->FILELIST[$Tag]);
            }
            return;
        }
    }
//  ************************************************************
//  Aliased function - used for compatibility with CGI::FastTemplate
/*    function clear_parse ()
    {
        $this->clear_assign();
    }
*/
//  ************************************************************
//  Clears all variables set by assign()
    function clear_assign ()
    {
        if(!(empty($this->PARSEVARS)))
        {
            while(list($Ref,$Val) = each ($this->PARSEVARS) )
            {
                unset($this->PARSEVARS["$Ref"]);
            }
        }
    }
//  ************************************************************
    function clear_href ($href)
    {
        if(!empty($href))
        {
            if( (gettype($href)) != "array")
            {
                unset($this->PARSEVARS[$href]);
                return;
            }
            else
            {
                while (list ($Ref,$val) = each ($href) )
                {
                    unset($this->PARSEVARS[$Ref]);
                }
                return;
            }
        }
        else
        {
            // Empty - clear them all
            $this->clear_assign();
        }
        return;
    }
//  ************************************************************
    /**
     * assign variables
     */
    function assign ($tpl_array, $trailer="")
    {
        if(gettype($tpl_array) == "array")
        {
            while ( list ($key,$val) = each ($tpl_array) )
            {
                if (!(empty($key)))
                {
                    //  Empty values are allowed
                    //  Empty Keys are NOT
                    $this->PARSEVARS["$key"] = $val;
                }
            }
        }
        else
        {
            // Empty values are allowed in non-array context now.
            if (!empty($tpl_array))
            {
                $this->PARSEVARS["$tpl_array"] = $trailer;
            }
        }
    }
//  ************************************************************
//  Return the value of an assigned variable.
//  Christian Brandel cbrandel@gmx.de
    function get_assigned($tpl_name = "")
    {
        if(empty($tpl_name)) { return false; }
        if(isset($this->PARSEVARS["$tpl_name"]))
        {
            return ($this->PARSEVARS["$tpl_name"]);
        }
        else
        {
            return false;
        }
    }
//  ************************************************************
    function error ($errorMsg, $die = 0)
    {
        $this->ERROR = $errorMsg;
        echo "ERROR: $this->ERROR 
 \n";
        if ($die == 1)
        {
            exit;
        }
        return;
    } // end error()
//  ************************************************************
    /**
     *  Prints debug info into console
     * @return void
     * @author AiK
     * @since 1.1.1
     */
    function showDebugInfo(){
        $tm =  $this->utime()  - $this->start;
        // print time
        print "
         ";
    }//end of showDebugInfo()
    /**
     *
     */
     function printarray($arr,$caption){
     if (count($arr)!=0){
        print " 
        _debug_console.document.write('$caption ');\n 
        _debug_console.document.write(\"
| key | value | 
|---|---|
| $key | $val  |