<?php

if (!class_exists('patTemplate')) {
    /*
    * Variable prefix
    * @access	public
    * @const patTEMPLATE_TAG_START
    */
    define('patTEMPLATE_TAG_START', '{');

    /*
    * Variable sufix
    * @const patTEMPLATE_TAG_END
    * @access	public
    */
    define('patTEMPLATE_TAG_END', '}');

    /*
    * Template type Standard
    * @const patTEMPLATE_TYPE_STANDARD
    */
    define('patTEMPLATE_TYPE_STANDARD', 'STANDARD');
    /*
    * Template type OddEven
    * @const patTEMPLATE_TYPE_ODDEVEN
    */
    define('patTEMPLATE_TYPE_ODDEVEN', 'ODDEVEN');
    /*
    * Template type Condition
    * @const patTEMPLATE_TYPE_CONDITION
    */
    define('patTEMPLATE_TYPE_CONDITION', 'CONDITION');
    /*
    * Template type SimpleCondition
    * @const patTEMPLATE_TYPE_SIMPLECONDITION
    */
    define('patTEMPLATE_TYPE_SIMPLECONDITION', 'SIMPLECONDITION');

    /**
     * Easy-to-use but powerful template engine.
     *
     * Features include: several templates in one file, automatic repetitions, global variables,
     * alternating lists, conditions, and much more
     *
     * @author Stephan Schmidt <schst@php-tools.de>
     *
     * @version 2.4 ($patTemplate-Id: patTemplate.php,v 1.54 2001/11/03 13:34:16 schst Exp $)
     */
    class patTemplate
    {
        /**
         * Constructor.
         *
         * Create new patTemplate object
         * You can choose between two outputs you want tp generate: html (default) or tex (LaTex).
         * When "tex" is used the patTemplate markings used for variables are changed as LaTex makes use of the default patTemplate markings.
         * You can also change the markings later by calling setTags();
         *
         * @param string $type type of output you want to generate
         */
        public function __construct($type = 'html')
        {
            //	Directory, where Templates are stored
            $this->basedir = '';

            //	counter for template iterations
            $this->iteration = array();

            //	Filenames of the templates
            $this->filenames = array();

            //	HTML/Text of unparsed templates
            $this->plain_templates = array();

            //	HTML/Text of parsed templates
            $this->parsed_templates = array();

            //	Amount and names of all templates
            $this->cnt_templates = 0;
            $this->templates = array();

            //	These vars will be set for all added Templates
            $this->subtemplates = array();

            $this->variables = array();
            $this->globals = array();

            $this->attributes = array();

            //	Does one of the templates contain other templates
            $this->uses_dependencies = false;

            // Set template tags
            $this->setType($type);
        }

        /**
         * Set template type.
         *
         * select a predefined template type
         *
         * @param string $type predefined template type, like "html" or "tex"
         */
        public function setType($type = '')
        {
            switch ($type) {
            case 'tex':
                $this->setTags('<{', '}>');
                break;

            case 'html':
            default:
                $this->setTags(patTEMPLATE_TAG_START, patTEMPLATE_TAG_END);
                break;
            }
        }

        /**
         * Set template tags.
         *
         * Sets the start and end tags of template variables
         *
         * @param string $start start tag
         * @param string $end   end tag
         */
        public function setTags($start = patTEMPLATE_TAG_START, $end = patTEMPLATE_TAG_END)
        {
            $this->tag_start = $start;
            $this->tag_end = $end;

            $this->regex_get_all_vars = '/'.$start.'([^a-z{}]+)'.$end.'/';
        }

        /**
         * Set template directory.
         *
         * Sets the directory where the template are stored.
         * By default the engine looks in the directory where the original file is stored.
         *
         * @param string $basedir directory of the templates
         */
        public function setBasedir($basedir)
        {
            $this->basedir = $basedir;
        }

        /**
         * Check if a template exists.
         *
         * @param string $name name of the template
         *
         * @return bool
         */
        public function exists($name)
        {
            $name = strtoupper($name);

            for ($i = 0; $i < $this->cnt_templates; ++$i) {
                if ($this->templates[$i] == $name) {
                    return    true;
                }
            }

            return    false;
        }

        /**
         * Add a template.
         *
         * Adds a plain text/html to the template engine.
         * The file has to be in the directory that has been set using setBaseDir
         *
         * @param string $name     name of the template
         * @param string $filename filename of the sourcetemplate
         *
         * @deprecated 2.4 2001/11/05
         * @see        setBaseDir(), addTemplates()
         */
        public function addTemplate($name, $filename)
        {
            $this->createTemplate($name, array('type' => 'file', 'filename' => $filename));
            //	Store the filename
            $this->filenames[$name] = $filename;
        }

        /**
         * Adds several templates.
         *
         * Adds several templates to the template engine using an associative array.
         * Names of the templates are stored in the keys, filenames are the values.
         * The templates have to be in the directory set by setBaseDir().
         *
         * @param array $templates associative Array with name/filename pairs
         *
         * @deprecated 2.4 2001/11/05
         * @see        setBaseDir(), addTemplate()
         */
        public function addTemplates($templates)
        {
            while (list($name, $file) = each($templates)) {
                $this->addTemplate($name, $file);
            }
        }

        /**
         * creates a new template.
         *
         * creates all needed variables
         *
         * @param string $name   name of the template
         * @param array  $source data regarding the source of the template
         */
        public function createTemplate($name, $source)
        {
            $name = strtoupper($name);

            //	Store the name of the template in index table
            $this->templates[$this->cnt_templates] = $name;
            ++$this->cnt_templates;

            //	Store the source
            $this->source[$name] = $source;

            //	Init vars for the new Templates

            //	Store all attributes in Array
            $this->attributes[$name] = array('loop' => 1,
                                                            'visibility' => 'visible',
                                                            'unusedvars' => 'strip',
                                                            'type' => 'STANDARD', );
            $this->iteration[$name] = 0;

            //	No vars are set for this template
            $this->variables[$name] = array();
            //	No subtemplates have been specified
            $this->cnt_subtemplates[$name] = 0;

            $this->varsConverted[$name] = false;
        }

        /**
         * Sets the type of the Template.
         *
         * Template types can be STANDARD, CONDITION or ODDEVEN
         * The type of the template can also be set using setAttribute()
         *
         * @param string $template name of the template
         * @param string $type     type of the template
         *
         * @see setAttribute()
         */
        public function setTemplateType($template, $type)
        {
            $template = strtoupper($template);

            $this->setAttribute($template, 'type', $type);
        }

        /**
         * Sets the conditionvar of a condtion Template.
         *
         * The type of the template has to be condition
         *
         * @param string $template     name of the template
         * @param string $conditionvar name of the conditionvariable
         *
         * @see setTemplateType()
         */
        public function setConditionVar($template, $conditionvar)
        {
            $template = strtoupper($template);
            $conditionvar = strtoupper($conditionvar);

            $this->conditionvars[$template] = $conditionvar;
        }

        /**
         * Sets an attribute of a template.
         *
         * supported attributes: visibilty, loop, parse, unusedvars
         *
         * @param string $template  name of the template
         * @param string $attribute name of the attribute
         * @param mixed  $value     value of the attribute
         *
         * @see setAttributes(),getAttribute(), clearAttribute()
         */
        public function setAttribute($template, $attribute, $value)
        {
            $template = strtoupper($template);
            $attribute = strtolower($attribute);

            $this->attributes[$template][$attribute] = $value;
        }

        /**
         * Sets several attribute of a template.
         *
         * $attributes has to be a assotiative arrays containing attribute/value pairs
         * supported attributes: visibilty, loop, parse, unusedvars
         *
         * @param string $template   name of the template
         * @param array  $attributes attribute/value pairs
         *
         * @see setAttribute(), getAttribute(), clearAttribute()
         */
        public function setAttributes($template, $attributes)
        {
            if (!is_array($attributes)) {
                return    false;
            }

            $template = strtoupper($template);

            while (list($attribute, $value) = each($attributes)) {
                $attribute = strtolower($attribute);
                $this->attributes[$template][$attribute] = $value;
            }
        }

        /**
         * Gets an attribute of a template.
         *
         * supported attributes: visibilty, loop, parse, unusedvars
         *
         * @param string $template  name of the template
         * @param string $attribute name of the attribute
         *
         * @return mixed value of the attribute
         *
         * @see setAttribute(), setAttributes(), clearAttribute()
         */
        public function getAttribute($template, $attribute)
        {
            $template = strtoupper($template);
            $attribute = strtolower($attribute);
            if (!isset($this->attributes[$template][$attribute])) {
                return false;
            }

            return    $this->attributes[$template][$attribute];
        }

        /**
         * Clears an attribute of a template.
         *
         * supported attributes: visibilty, loop, parse, unusedvars
         *
         * @param string $template  name of the template
         * @param string $attribute name of the attribute
         *
         * @see setAttribute(), setAttributes(), getAttribute()
         */
        public function clearAttribute($template, $attribute)
        {
            $template = strtoupper($template);
            $attribute = strtolower($attribute);

            unset($this->attributes[$template][$attribute]);
        }

        /**
         * Adds a subtemplate for a condition or oddeven template.
         *
         * template type has to be condition or oddeven
         *
         * @param string $template  name of the template
         * @param string $condition condition for this subtemplate
         *
         * @see setTemplateType()
         */
        public function addSubTemplate($template, $condition)
        {
            $template = strtoupper($template);

            $this->subtemplates[$template][$condition] = '';
            $this->subtemplate_conditions[$template][$this->cnt_subtemplates[$template]] = $condition;

            ++$this->cnt_subtemplates[$template];
        }

        /**
         * Parses several templates from one patTemplate file.
         *
         * Templates can be seperated using Tags
         * The file has to be located in the directory that has been set using setBaseDir.
         *
         * @param string $file filename
         *
         * @see setBasedir()
         */
        public function readTemplatesFromFile($file)
        {
            //	Tag depth
            $this->depth = -1;
            //	Names, extracted from the Tags
            $this->template_names = array();
            //	All HTML code, that is found between the tags
            $this->template_data = array();
            //	Attributes, extracted from tags
            $this->template_types = array();

            $this->last_opened = array();
            $this->last_keep = array();
            $this->whitespace = array('trim');

            $this->createParser($file);

            $open_tag = array_pop($this->last_opened);
            if (null != $open_tag) {
                die("Error in template '".$file."': &lt;/".$open_tag.'&gt; still open at end of file.');
            }
        }

        /**
         *    parse a template file and call the appropriate handlers.
         *
         *    @param string $fname filename of the template
         */
        public function createParser($fname)
        {
            //	Store filename of the first file that has to be opened
            //	If basedir is set, prepend basedir
            $pname = '' != $this->basedir ? $this->basedir.'/'.$fname : $fname;

            //	open file for reading
            $fp = fopen($pname, 'r');

            //	couldn't open the file => exit
            if (!$fp) {
                die("Couldn't open file '".$fname."' for reading!");
            }

            //	Read line for line from the template

            //	current linenumber in file, used for error messages
            $lineno = 1;

            //	read until end of file
            while (!feof($fp)) {
                //	Read one line
                $line = fgets($fp, 4096);

                //	check, wether leading and trailing whitepaces should be stripped
                switch ($this->whitespace[(count($this->whitespace) - 1)]) {
                case    'trim':
                    $line = trim($line);
                    break;
                case    'ltrim':
                    $line = ltrim($line);
                    break;
                case    'rtrim':
                    $line = rtrim($line);
                    break;
                }
                //	========= [ OPEN TAG ] =========

                //	check for any <patTemplate:...> Tag by using RegExp
                if (preg_match("/<patTemplate:([a-z0-9]+)\s*(.*)>/i", $line, $regs)) {
                    //	Get Tag name and attributes
                    $tagname = strtolower($regs[1]);
                    $attributes = $this->parseAttributes($regs[2]);

                    // XooNIps - 'keep' attribute existing check
                    if (isset($attributes['keep']) && $attributes['keep'] > 0) {
                        //	create new attribute
                        $newkeep = $attributes['keep'] > 1 ? ' keep="'.($attributes['keep'] - 1).'"' : '';

                        //	replace old attribute with new attribute
                        $newline = str_replace(' keep="'.$attributes['keep'].'"', $newkeep, $line);

                        //	use this line as data
                        $this->dataHandler($fname, $newline, $lineno);

                        //	if the tag was not empty keep the closing tag, too
                        if ('/' != substr($regs[2], -1)) {
                            $this->last_keep[] = true;
                        }
                    } else {
                        $this->last_keep[] = false;

                        //	handle start Element
                        $this->startElementHandler($fname, $tagname, $attributes, $line, $lineno);

                        if ('/' == substr($regs[2], -1)) {
                            $this->endElementHandler($fname, $tagname, $line, $lineno);
                        }

                        //	Store the name of the last opened tag
                        else {
                            $this->last_opened[] = $tagname;
                        }
                    }
                }

                //	========= [ CLOSING TAG ] =========

                //	Check if a closing <patTemplate:...> Tag has been found
                elseif (preg_match("/<\/patTemplate:([a-z0-9]+)>/i", $line, $regs)) {
                    //	Yes => get the tagname
                    $tagname = strtolower($regs[1]);

                    $keep = array_pop($this->last_keep);
                    if (!$keep) {
                        $last_opened = array_pop($this->last_opened);
                        if (null == $last_opened) {
                            die("Error in template '".$fname."': no opening tag found for &lt;/".$tagname.'&gt; in line '.$lineno);
                        }

                        if ($tagname != $last_opened) {
                            die("Error in template '".$fname."': closing &lt;/".$tagname.'&gt; does not match opened &lt;'.$last_opened.'&gt; in line '.$lineno);
                        }

                        $this->endElementHandler($fname, $tagname, $line, $lineno);
                    } else {
                        $this->dataHandler($fname, $line, $lineno);
                    }
                }

                //	========= [ CDATA SECTION ] =========

                //	No tag found => store the line
                else {
                    $this->dataHandler($fname, $line, $lineno);
                }

                //	goto next line
                ++$lineno;
            }
        }

        /**
         *    handle a <patTemplate:...> start tag in template parser.
         *
         *    @param string $fname      name of the file where the tag was found (kind of parser id)
         *    @param string $tagname    name of the start tag that was found
         *    @param array  $attributes all attributes that were found
         *    @param string $line       the complete line containing the tag
         *    @param int    $lineno     lineno in the parse file (can be used for error messages
         */
        public function startElementHandler($fname, $tagname, $attributes, $line, $lineno)
        {
            //	check for whitespace attribute
            if (isset($attributes['whitespace'])) {
                array_push($this->whitespace, strtolower($attributes['whitespace']));
            }
            //	use whitepspace mode from last opened template
            else {
                array_push($this->whitespace, $this->whitespace[(count($this->whitespace) - 1)]);
            }

            switch ($tagname) {
            //	Beginning of a template found
            case 'tmpl':
                //	parse all attributes from a string into an associative array

                //	Check for name of template, which is a necessary attribute
                if (!$tmpl_name = strtoupper($attributes['name'])) {
                    die("Error in template '".$fname."': missing name for template in line ".$lineno);
                }

                unset($attributes['name']);

                //	Increment Tag Depth
                ++$this->depth;

                //	Start with a blank template
                $this->template_data[$this->depth] = '';

                //	and store the name
                $this->template_names[$this->depth] = $tmpl_name;

                //	Check, if attribute "type" was found
                if (isset($attributes['type'])) {
                    $tmpl_type = strtoupper($attributes['type']);
                    $this->template_types[$this->depth] = $tmpl_type;
                    $attributes['type'] = $tmpl_type;
                }
                //	No type found => this is a boring standard template
                else {
                    $attributes['type'] = 'STANDARD';
                    $this->template_types[$this->depth] = 'STANDARD';
                }

                //	Check for src attribute => external file
                if (isset($attributes['src'])) {
                    //	Store the filename of the external file
                    $filename = $attributes['src'];

                    //	Has the external file to be parsed
                    if ('on' == $attributes['parse']) {
                        $this->createParser($filename);
                    }

                    //	No parsing, just take the whole content of the file
                    else {
                        //	Filename including full path
                        $external = '' != $this->basedir ? $this->basedir.'/'.$filename : $filename;

                        //	Open the file and read all the content
                        if (!$tmp = @implode('', @file($external))) {
                            die("Couldn't open file '".$external."' for reading in template ".$fname.' line '.$lineno);
                        }

                        $this->template_data[$this->depth] .= $tmp;
                    }

                    //	Delete the src attribute, it hasn't to be stored
                    unset($attributes['src']);
                }
                //	No external file => the template is part of teh current file
                else {
                    $filename = '[part of '.$fname.']';
                }

                //	add the template
                $this->addTemplate($this->template_names[$this->depth], $filename);

                //	Set all remaining attributes
                $this->setAttributes($this->template_names[$this->depth], $attributes);

                switch ($this->template_types[$this->depth]) {
                    //	Template type is "ODDEVEN", it contains two alternating subtemplates
                case 'ODDEVEN':
                    $this->setConditionVar($this->template_names[$depth], 'PAT_ROW_VAR mod 2');
                    break;

                    //	Template is a condition Tenplate => it needs a condition var
                case 'CONDITION':
                    //	none found => there is an error
                    if (!$conditionvar = $attributes['conditionvar']) {
                        die("Error in template '".$fname."': missing conditionvar for template in line ".$lineno);
                    }

                    //	conditionvar was found => store it
                    $this->setConditionVar($this->template_names[$this->depth], $conditionvar);
                    break;

                    //	Template is a simple condition Tenplate => it needs required vars
                case 'SIMPLECONDITION':
                    //	none found => there is an error
                    if (isset($attributes['requiredvars'])) {
                        $requiredvars = $attributes['requiredvars'];
                        $this->setAttribute($this->template_names[$this->depth], 'requiredvars', explode(',', $requiredvars));
                    } else {
                        die("Error in template '".$fname."': missing requiredvars attribute for simple condition template in line ".$lineno);
                    }

                    break;
                }

                //	if the template isn't the root( depth=0 ) template, a placeholder has
                //	to be put into the parent template
                if ($this->depth > 0) {
                    //	Is there a placeholder attribute?
                    if (isset($placeholder)) {
                        $placeholder = strtoupper($attributes['placeholder']);
                        //	placeholder="none" found => DO NOT PUT A PLACEHOLDER IN THE PARENT TEMPLATE!
                        if ('NONE' != $placeholder) {
                            $this->template_data[($this->depth - 1)] .= $this->tag_start.$placeholder.$this->tag_end;
                        }
                    }
                    //	No placeholder attribute found => standard placeholder
                    else {
                        $this->template_data[($this->depth - 1)] .= $this->tag_start.'TMPL:'.$this->template_names[$this->depth].$this->tag_end."\n";
                        //	Tell the parent template, that it has to parse the child template, before parsing
                        //	itself
                        $this->addDependency($this->template_names[($this->depth - 1)], $this->template_names[$this->depth]);
                    }
                }
                break;

            //	Found the beginning of a subtemplate
            case 'sub':
                //	A subtemplate needs to have a "condition" attribute
                $condition = $attributes['condition'];

                //	None found => error
                if (0 == isset($condition)) {
                    die("Error in template '".$fname."': missing condition attribute for template in line ".$lineno);
                }

                //	Everything is ok => add the subtemplate and store the condition
                $this->addSubTemplate($this->template_names[$this->depth], $condition);

                //	Store the current condition
                $this->template_condition[$this->depth] = $condition;
                break;

            //	Found a link template
            case 'link':
                $src = strtoupper($attributes['src']);

                if (!$src) {
                    die("Error in template '".$fname."': missing src attribute for link in line ".$lineno);
                }

                //	put a placeholder into the current template
                if ($this->depth >= 0) {
                    $this->template_data[$this->depth] .= $this->tag_start.'TMPL:'.$src.$this->tag_end."\n";
                    //	Tell the parent template, that it has to parse the child template, before parsing
                    //	itself
                    $this->addDependency($this->template_names[$this->depth], $src);
                }
                break;

            //	No valid Tag found =>
            default:
                die("Error in template '".$fname."': unkown Tag in line ".$lineno);
            break;
            }
        }

        /**
         *    handle a </patTemplate:...> end tag in template parser.
         *
         *    @param string $fname   name of the file where the tag was found (kind of parser id)
         *    @param string $tagname name of the start tag that was found
         *    @param string $line    the complete line containing the tag
         */
        public function endElementHandler($fname, $tagname, $line)
        {
            array_pop($this->whitespace);

            switch ($tagname) {
            //	End of a template found
            case 'tmpl':
                //	If the current template is a standard template, store all content
                //	found between <patTemplate> Tags
                if ('STANDARD' == $this->template_types[$this->depth] || 'SIMPLECONDITION' == $this->template_types[$this->depth]) {
                    $this->setPlainContent($this->template_names[$this->depth], $this->template_data[$this->depth]);
                }

                //	Decrease Tagdepth
                --$this->depth;

                break;

            //	End of a subtemplate found
            case 'sub':
                //	Store alle content found between :sub Tags
                $this->setPlainContent($this->template_names[$this->depth], $this->template_data[$this->depth], $this->template_condition[$this->depth]);

                //	clear all Data, to store the data of the next subtemplate
                $this->template_data[$this->depth] = '';
                break;

            //	End of a link found
            case 'link':
                //	Just ignore this tag...
                break;

            //	No kown tag found
            default:
                die("Error in template '".$fname."': unkown closing tag in line ".$lineno);
            break;
            }
        }

        /**
         *    handle a CDATA in template parser.
         *
         *    @param string $fname name of the file where the tag was found (kind of parser id)
         *    @param string $data  all cdata that was found
         */
        public function DataHandler($fname, $data)
        {
            if ($this->depth >= 0) {
                $this->template_data[$this->depth] .= $data;
            }
        }

        /**
         * Adds a variable to a template.
         *
         * Each Template can have an unlimited amount of its own variables
         *
         * @param string $template name of the template
         * @param string $name     name of the variables
         * @param mixed  $value    value of the variable
         *
         * @see addVars(), addRows(), addGlobalVar(), addGlobalVars()
         */
        public function addVar($template, $name, $value)
        {
            $template = strtoupper($template);
            $name = strtoupper($name);

            if (!is_array($value)) {
                $value = (string) $value;
            }

            //	store the value and the name of the variable
            $this->variables[$template][$name] = $value;

            //	if the value is an array, the template has to be repeated
            if (is_array($value)) {
                //	Check, how often the template has to be repeated
                if ($this->getAttribute($template, 'loop') < count($value)) {
                    $this->setAttribute($template, 'loop', count($value));
                }
            }
        }

        /**
         * Adds several variables to a template.
         *
         * Each Template can have an unlimited amount of its own variables
         * $variables has to be an assotiative array containing variable/value pairs
         *
         * @param string $template  name of the template
         * @param array  $variables assotiative array of the variables
         * @param string $prefix    prefix for all variable names
         *
         * @see addVar(), addRows(), addGlobalVar(), addGlobalVars()
         */
        public function addVars($template, $variables, $prefix = '')
        {
            //	Are there variables?
            if (!is_array($variables)) {
                return    false;
            }

            //	Add all vars
            while (list($name, $value) = each($variables)) {
                if (!is_int($name)) {
                    $this->addVar($template, $prefix.$name, $value);
                }
            }
        }

        /**
         * Adds several rows of variables to a template.
         *
         * Each Template can have an unlimited amount of its own variables
         * Can be used to add a database result as variables to a template
         *
         * @param string $template name of the template
         * @param array  $rows     array containing assotiative arrays with variable/value pairs
         * @param string $prefix   prefix for all variable names
         *
         * @see addVar(), addVars(), addGlobalVar(), addGlobalVars()
         */
        public function addRows($template, $rows, $prefix = '')
        {
            //	Store the vars in this array
            $newvars = array();

            //	get amount of rows
            $cnt_rows = count($rows);

            if (1 == $cnt_rows) {
                $this->addVars($template, $rows[0], $prefix);
            } else {
                for ($i = 0; $i < $cnt_rows; ++$i) {
                    if (is_array($rows[$i])) {
                        //	Get key and value
                        while (list($key, $value) = each($rows[$i])) {
                            //	check if the array key is an int value => skip it
                            if (!is_int($key)) {
                                //	prepend prefix and store the value
                                $new_vars[$prefix.$key][$i] = $value;
                            }
                        }
                    }
                }

                //	add the vars to the template
                $this->addVars($template, $new_vars);
            }
        }

        /**
         * Adds a global variable.
         *
         * Global variables are valid in all templates of this object
         *
         * @param string $name  name of the global variable
         * @param string $value value of the variable
         *
         * @see addGlobalVars(), addVar(), addVars(), addRows()
         */
        public function addGlobalVar($name, $value)
        {
            $this->globals[strtoupper($name)] = (string) $value;
        }

        /**
         * Adds several global variables.
         *
         * Global variables are valid in all templates of this object
         * $variables is an assotiative array, containing name/value pairs of the variables
         *
         * @param array  $variables array containing the variables
         * @param string $prefix    prefix for variable names
         *
         * @see addGlobalVar(), addVar(), addVars(), addRows()
         */
        public function addGlobalVars($variables, $prefix = '')
        {
            while (list($variable, $value) = each($variables)) {
                $this->globals[strtoupper($prefix.$variable)] = (string) $value;
            }
        }

        /**
         * Creates a dependeny between two templates.
         *
         * The Dependency tells a template, which templates have to be parsed before parsing the current template, because they are its children.
         *
         * @param string $container the name of the template, that contains the other template
         * @param string $child     the child of the container
         */
        public function addDependency($container, $child)
        {
            $this->dependencies[strtoupper($container)][] = strtoupper($child);
            //	This template now uses dependencies
            $this->uses_dependencies = true;
        }

        /**
         * loads a template.
         *
         * The template has to be defined using addTemplate() or addTemplates()
         *
         * @param string $name name of the template that has to be loaded
         *
         * @deprecated 2.4 2001/11/05
         * @see        addTemplate(), addTemplates();
         */
        public function loadTemplate($name)
        {
            $name = strtoupper($name);

            //	prepend basedirname, if it exists
            $fname = '' != $this->basedir ? $this->basedir.'/'.$this->source[$name]['filename'] : $this->source[$name]['filename'];

            if (stristr($fname, '[part')) {
                return    true;
            }

            if (!$this->plain_templates[$name] = @implode('', @file($fname))) {
                die("Couldn't open template '".$name."' (file: '".$fname."') for reading.");
            }
        }

        /**
         * sets the content of a template.
         *
         * This function should used, if a template is added using tags instead of defining it by a filename
         *
         * @param string $template name of the template
         * @param string $content  the content that has to be set
         * @param string $sub      condition, for the subtemplate, if any
         */
        public function setPlainContent($template, $content, $sub = '')
        {
            $template = strtoupper($template);

            //	The content has to be set for a subtemplate
            if ('' != $sub) {
                $this->plain_templates[$template][$sub] = $content;
            }
            //	content is meant for a template
            else {
                $this->plain_templates[$template] = $content;
            }
        }

        /**
         * parses a template.
         *
         * Parses a template and stores the parsed content.
         * mode can be "w" for write (delete already parsed content) or "a" for append (appends the
         * new parsed content to the already parsed content)
         *
         * @param string $template name of the template
         * @param string $mode     mode for the parsing
         *
         * @see parseStandardTemplate(), parseIterativeTemplate()
         */
        public function parseTemplate($template, $mode = 'w')
        {
            $template = strtoupper($template);
            $this->iteration[$template] = 0;

            //	The template has to be repeated
            if ($this->getAttribute($template, 'loop') > 1) {
                $this->parseIterativeTemplate($template, $mode);
            }
            //	parse it once
            else {
                $this->parseStandardTemplate($template, $mode);
            }
        }

        /**
         * parses a standard template.
         *
         * Parses a template and stores the parsed content.
         * mode can be "w" for write (delete already parsed content) or "a" for append (appends the
         * new parsed content to the already parsed content)
         *
         * @param string $name name of the template
         * @param string $mode mode for the parsing
         *
         * @see parseTemplate(), parseIterativeTemplate()
         */
        public function parseStandardTemplate($name, $mode = 'w')
        {
            $name = strtoupper($name);

            //	get a copy of the plain content

            $temp = $this->getTemplateContent($name);

            $vars = $this->getVars($name);
            $vars[$this->tag_start.'PAT_ROW_VAR'.$this->tag_end] = 1;
            while (list($tag, $value) = each($vars)) {
                if (is_array($value)) {
                    $value = $value[0];
                }
                $temp = str_replace($tag, $value, $temp);
            }

            //	parse all global vars
            $this->parseGlobals($name, $temp);

            //	parse child templates into this template
            $this->parseDependencies($name, $temp, $mode);

            //	Strip unsused vars
            $this->stripUnusedVars($name, $temp);

            if ('a' == $mode) {
                $this->parsed_templates[$name] .= $temp;
            } elseif ('w' == $mode) {
                $this->parsed_templates[$name] = $temp;
            }
        }

        /**
         * parses an iterative template.
         *
         * Parses a template and stores the parsed content.
         * mode can be "w" for write (delete already parsed content) or "a" for append (appends the
         * new parsed content to the already parsed content)
         *
         * @param string $name name of the template
         * @param string $mode mode for the parsing
         *
         * @see parseTemplate(), parseStandardTemplate()
         */
        public function parseIterativeTemplate($name, $mode)
        {
            $name = strtoupper($name);

            $temp = '';

            //	repeat it template_loop[$name] times
            for ($PAT_ROW_VAR = 0; $PAT_ROW_VAR < $this->getAttribute($name, 'loop'); ++$PAT_ROW_VAR) {
                //	add the PAT_ROW_VAR variable to the template
                $this->variables[$name]['PAT_ROW_VAR'][$PAT_ROW_VAR] = $PAT_ROW_VAR + 1;
                $this->iteration[$name] = $PAT_ROW_VAR;

                //	get the content to be parsed (dependent on PAT_ROW_VAR or conditionvar)
                $current = $this->getTemplateContent($name);

                $vars = $this->getVars($name);
                while (list($tag, $value) = each($vars)) {
                    $current = str_replace($tag, $value, $current);
                }

                //	and the dependent Templates
                $this->parseDependencies($name, $current, $mode);

                //	append this parsed to the repetition
                $temp .= $current;
            }

            //	after parsing repetitions, parse the Global Vars
            $this->parseGlobals($name, $temp);

            //	Strip unsused vars
            $this->stripUnusedVars($name, $temp);

            if ('a' == $mode) {
                $this->parsed_templates[$name] .= $temp;
            } elseif ('w' == $mode) {
                $this->parsed_templates[$name] = $temp;
            }
        }

        /**
         *    get variables for a template
         *    if the templates uses the attribute 'varscope' these vars will be fetched, too.
         *
         *    @param string $template name of the template
         *
         *    @return array    $vars        array containign vars
         */
        public function getVars($template)
        {
            $vars = array();
            //	parse all vars
            if (is_array($this->variables[$template])) {
                //	Pointer im Array auf 0 setzen
                reset($this->variables[$template]);

                while (list($variable, $value) = each($this->variables[$template])) {
                    $tag = $this->tag_start.$variable.$this->tag_end;

                    //	if the variable is an array, use the index
                    if (is_array($value)) {
                        $value = $value[$this->iteration[$template]];
                    }

                    $vars[$tag] = $value;
                }
            }

            if ($scope = strtoupper($this->getAttribute($template, 'varscope'))) {
                $parentVars = $this->getVars($scope);
                reset($parentVars);
                while (list($var, $value) = each($parentVars)) {
                    if (!$vars[$var]) {
                        $vars[$var] = $value;
                    }
                }
            }
            reset($vars);

            return    $vars;
        }

        /**
         * parses the global variables in a template.
         *
         * global variables are valid in all templates
         *
         * @param string $name  name of the template
         * @param string &$temp content of the parsed Template
         * @param string $temp
         *
         * @see parseTemplate(), addGlobalVar(), addGlobalVars()
         */
        public function parseGlobals($name, &$temp)
        {
            $name = strtoupper($name);

            //	check, if globals exist
            if (is_array($this->globals)) {
                reset($this->globals);

                while (list($variable, $value) = each($this->globals)) {
                    $tag = $this->tag_start.$variable.$this->tag_end;
                    $temp = str_replace($tag, $value, $temp);
                }
            }
        }

        /**
         * handles unset variables.
         *
         * either strips, comments, replaces or ignores them, depending on the unusedvars attribute
         *
         * @param string $name      name of the template
         * @param string &$template content of the parsed Template
         *
         * @see setAttribute()
         */
        public function stripUnusedVars($name, &$template)
        {
            switch ($this->getAttribute($name, 'unusedvars')) {
            case    'comment':
                $template = preg_replace('/('.$this->tag_start.'[^a-z{}]+'.$this->tag_end.')/', '<!-- \\1 -->', $template);
                break;
            case    'strip':
                $template = preg_replace('/('.$this->tag_start.'[^a-z{}]+'.$this->tag_end.')/', '', $template);
                break;
            case    'nbsp':
                $template = preg_replace('/('.$this->tag_start.'[^a-z{}]+'.$this->tag_end.')/', '&nbsp;', $template);
                break;
            case    'ignore':
                break;
            default:
                $template = preg_replace('/('.$this->tag_start.'[^a-z{}]+'.$this->tag_end.')/', $this->getAttribute($name, 'unusedvars'), $template);
                break;
            }
        }

        /**
         * parses dependencies of a template.
         *
         * parses child templates of a template and inserts their content
         *
         * @param string $name  name of the template
         * @param string &$temp content of the parsed Template
         *
         * @see addDependency()
         */
        public function parseDependencies($name, &$temp, $mode = 'w')
        {
            $name = strtoupper($name);

            $numdeps = (isset($this->dependencies[$name])) ? count($this->dependencies[$name]) : 0;
            for ($i = 0; $i < $numdeps; ++$i) {
                $type = $this->getAttribute(strtoupper($this->dependencies[$name][$i]), 'type');

                //	Templates placeholders have the prefix TMPL:
                $tag = $this->tag_start.'TMPL:'.$this->dependencies[$name][$i].$this->tag_end;
                //	Get the parsed child template and replace it
                $temp = str_replace($tag, $this->getParsedTemplate($this->dependencies[$name][$i]), $temp);

                if ((patTEMPLATE_TYPE_CONDITION == $type || patTEMPLATE_TYPE_SIMPLECONDITION == $type) && 'w' == $mode) {
                    unset($this->parsed_templates[$this->dependencies[$name][$i]]);
                }
            }
        }

        /**
         * returns a parsed Template.
         *
         * If the template already has been parsed, it just returns the parsed template.
         * If the template has not been loaded, it will be loaded.
         *
         * @param string $name name of the template
         *
         * @return string $content    Content of the parsed template
         *
         * @see displayParsedTemplate()
         */
        public function getParsedTemplate($name = '')
        {
            $name = strtoupper($name);

            //	if a name was given, parse only this template
            if ('' != $name) {
                //	check, wther template was disabled
                if ('hidden' == $this->getAttribute($name, 'visibility')) {
                    return    false;
                }

                //	check, if the template has already been parsed => just return it
                if (!empty($this->parsed_templates[$name])) {
                    return    $this->parsed_templates[$name];
                }

                //	Check, if the template has been loaded, if not, load it
                if (empty($this->plain_templates[$name])) {
                    $this->loadTemplate($name);
                }

                //	Template is loaded, but not parsed then parse it!
                $this->parseTemplate($name);

                //	And return the parsed template
                return    $this->parsed_templates[$name];
            }

            //	No name given
            else {
                //	The template uses dependencies, then start with the root template
                if ($this->uses_dependencies) {
                    return    $this->getParsedTemplate($this->templates[0]);
                }
                //	Only one template => parse and return it
                elseif (1 == $this->cnt_templates) {
                    return    $this->getParsedTemplate($this->templates[0]);
                }
                //	No dependencies, but more than one => return all parsed templates in an array
                else {
                    for ($i = 0; $i < $this->cnt_templates; ++$i) {
                        $arr[$this->templates[$i]] = $this->getParsedTemplate($this->templates[$i]);
                    }

                    return    $arr;
                }
            }
        }

        /**
         * displays a parsed Template.
         *
         * If the template has not been loaded, it will be loaded.
         *
         * @param string $name name of the template
         *
         * @see getParsedTemplate()
         */
        public function displayParsedTemplate($name = '')
        {
            $name = strtoupper($name);

            //	if a name was given, parse and display it
            if ($name) {
                echo    $this->getParsedTemplate($name);
            }

            //	No name was given, display them all!
            else {
                //	if the template uses dependencies, start with the root template
                if ($this->uses_dependencies) {
                    echo    $this->getParsedTemplate($this->templates[0]);
                }
                //	Only one template => parse and return it
                elseif (1 == $this->cnt_templates) {
                    echo    $this->getParsedTemplate($this->templates[0]);
                }
                //	parse and display them all
                else {
                    $templates = $this->getParsedTemplate();
                    for ($i = 0; $i < $this->cnt_templates; ++$i) {
                        echo    $templates[$this->templates[$i]];
                    }
                }
            }
        }

        /**
         * returns an unparsed Template.
         *
         * If the template has not been loaded, it will be loaded.
         *
         * @param string $name name of the template
         *
         * @deprecated 2.4 2001/11/05
         *
         * @return string $content    Unparsed content of the template
         *
         * @see getPlainSubTemplate(), displayPlainTemplate()
         */
        public function getPlainTemplate($name)
        {
            $name = strtoupper($name);

            //	check, wether the template is already loaded
            if (empty($this->plain_templates[$name])) {
                $this->loadTemplate($name);
            }

            //	return	it
            return    $this->plain_templates[$name];
        }

        /**
         * returns an unparsed Subtemplate.
         *
         * The template of the template has to be set
         *
         * @param string $name name of the template
         * @param string $sub  condition for the subtemplate
         *
         * @deprecated 2.4 2001/11/05
         *
         * @return string $content    Unparsed content of the template
         *
         * @see getPlainTemplate(), displayPlainTemplate()
         */
        public function getPlainSubTemplate($name, $sub)
        {
            $name = strtoupper($name);

            return    $this->plain_templates[$name][$sub];
        }

        /**
         * displays an unparsed Template.
         *
         * If the template has not been loaded, it will be loaded.
         *
         * @param string $name name of the template
         *
         * @deprecated 2.4 2001/11/05
         * @see        getPlainTemplate(), getPlainSubTemplate()
         */
        public function displayPlainTemplate($name)
        {
            $name = strtoupper($name);

            echo    $this->getPlainTemplate($name);
        }

        /**
         * clears a parsed Template.
         *
         * parsed Content, variables and the loop attribute are cleared
         *
         * @param string $name name of the template
         */
        public function clearTemplate($name)
        {
            $name = strtoupper($name);

            unset($this->parsed_templates[$name]);
            unset($this->variables[$name]);
            $this->clearAttribute($name, 'loop');
        }

        /**
         * clears all templates.
         */
        public function clearAllTemplates()
        {
            for ($i = 0; $i < count($this->templates); ++$i) {
                $this->clearTemplate($this->templates[$i]);
            }
        }

        /**
         * parsed attributes from a string.
         *
         * used for parsing <patTemplate> Tags
         *
         * @param string $string string containing the attributes
         *
         * @return array $array    assotiative array, containing all attributes
         */
        public function parseAttributes($string)
        {
            //	Check for trailing slash, if tag was an empty XML Tag
            if ('/' == substr($string, -1)) {
                $string = substr($string, 0, strlen($string) - 1);
            }

            $pairs = explode(' ', $string);
            for ($i = 0; $i < count($pairs); ++$i) {
                $pair = explode('=', trim(str_replace('"', '', $pairs[$i])));

                if (1 == count($pair)) {
                    $pair[1] = 'yes';
                }

                $attributes[strtolower($pair[0])] = $pair[1];
            }

            return    $attributes;
        }

        /**
         * returns the plain content of a template.
         *
         * return value depends on iteration value
         *
         * @param string $name name of the template
         *
         * @return string $content    plain content of the template
         */
        public function getTemplateContent($name)
        {
            $name = strtoupper($name);
            $index = $this->iteration[$name];

            //	Is it a standard, oddeven or condition template
            switch ($this->getAttribute($name, 'type')) {
            case patTEMPLATE_TYPE_ODDEVEN:
                $sub = 0 == ($index + 1) % 2 ? 'even' : 'odd';

                return    $this->plain_templates[$name][$sub];
                break;
            case patTEMPLATE_TYPE_CONDITION:
                $conditionval = $this->getVar($name, $this->conditionvars[$name]);

                //	check, if conditionvalue is empty
                if (!isset($conditionval) || (is_string($conditionval) && '' == $conditionval) || false === $conditionval) {
                    $conditionval = 'empty';
                }

                //	check if condition was specified, otherwise use default
                $condition_found = false;
                for ($i = 0; $i < $this->cnt_subtemplates[$name]; ++$i) {
                    if ($this->subtemplate_conditions[$name][$i] == $conditionval) {
                        $condition_found = true;
                        break;
                    }
                }
                if (!$condition_found) {
                    $conditionval = 'default';
                }

                return    $this->plain_templates[$name][$conditionval];
                break;

            case patTEMPLATE_TYPE_SIMPLECONDITION:
                //	get required vars
                $requiredVars = $this->getAttribute($name, 'requiredvars');

                //	check, if all are set
                for ($i = 0; $i < count($requiredVars); ++$i) {
                    if (!$this->getVar($name, $requiredVars[$i])) {
                        return    '';
                    }
                }

                return    $this->plain_templates[$name];
                break;

            default:
                return    $this->plain_templates[$name];
                break;
            }
        }

        /**
         *    get the value of a variable.
         *
         *    @param string $template name of the template
         *    @param string $var      name of the variable
         */
        public function getVar($template, $var)
        {
            //	should the var from a different template be used
            if (stristr($var, '.')) {
                list($template, $var) = explode('.', $var);
            }

            $var = strtoupper($var);
            $index = $this->iteration[$template];
            if ($scope = $this->getAttribute($template, 'varscope')) {
                $val = $this->getVar(strtoupper($scope), $var);
            } else {
                $val = (isset($this->variables[$template][$var])) ? $this->variables[$template][$var] : '';
            }

            //	check, if global var should be used
            if (!$val && 'yes' == $this->getAttribute($template, 'useglobals')) {
                $val = $this->globals[$var];
            }

            if (is_array($val)) {
                $val = $val[$index];
            }

            return    $val;
        }

        /**
         * displays useful information about all templates.
         *
         * returns content, variables, attributes and unused variables
         */
        public function dump()
        {
            echo    "<style type=\"text/css\">\n";
            echo    ".text		{font-family: Verdana, Arial, sans-serif; font-size: 10px; color: #000000}\n";
            echo    ".mid		{font-family: Verdana, Arial, sans-serif; font-size: 12px; color: #000000}\n";
            echo    ".head		{font-family: Verdana, Arial, sans-serif; font-size: 16px; font-weight: bold; color: #000000}\n";
            echo    "</style>\n";

            echo    "<table border=\"1\" cellpadding=\"2\" cellspacing=\"1\" bgcolor=\"White\" >\n";

            for ($i = 0; $i < $this->cnt_templates; ++$i) {
                $name = $this->templates[$i];

                //	Template name
                echo    "	<tr bgcolor=\"#EEEEEE\">\n";
                echo    "		<td class=\"head\" valign=\"top\">Template</td>\n";
                echo    '		<td class="head" valign="top">'.$name."</td>\n";
                echo    "	</tr>\n";

                $fname = '' != $this->basedir ? $this->basedir.'/'.$this->filenames[$name] : $this->filenames[$name];

                //	Template file
                echo    "	<tr>\n";
                echo    "		<td class=\"mid\" valign=\"top\"><b>Filename</b></td>\n";
                echo    '		<td class="text" valign="top">'.$fname."</td>\n";
                echo    "	</tr>\n";

                //	Template Attributes

                echo    "	<tr>\n";
                echo    "		<td class=\"mid\" valign=\"top\"><b>Attributes</b></td>\n";
                echo    "		<td class=\"text\" valign=\"top\">\n";

                echo    "			<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";

                //	Display all Attributes in table
                while (list($key, $value) = each($this->attributes[$name])) {
                    echo    "				<tr>\n";
                    echo    '					<td class="text"><b>'.$key."</b></td>\n";
                    echo    "					<td class=\"text\"> : </td>\n";
                    echo    '					<td class="text">'.$value."</td>\n";
                    echo    "				</tr>\n";
                }
                echo    "			</table>\n";

                echo    "		</td>\n";
                echo    "	</tr>\n";

                echo    "	<tr>\n";
                echo    "		<td class=\"text\" valign=\"top\" colspan=\"2\">&nbsp;</td>\n";
                echo    "	</tr>\n";

                if ($this->cnt_subtemplates[$name] > 0) {
                    //	display template Data
                    echo    "	<tr>\n";
                    echo    "		<td class=\"mid\" valign=\"top\"><b>Template Data:</b></td>\n";
                    echo    '		<td class="text" valign="top">Amount of subtemplates: '.$this->cnt_subtemplates[$name]."</td>\n";
                    echo    "	</tr>\n";

                    //	Display all Subtemplates
                    for ($j = 0; $j < $this->cnt_subtemplates[$name]; ++$j) {
                        $condition = $this->subtemplate_conditions[$name][$j];
                        echo    "				<tr bgcolor=\"#DDDDDD\">\n";
                        echo    "					<td class=\"text\"><b>Condition</b></td>\n";
                        echo    '					<td class="text">'.$condition."</td>\n";
                        echo    "				</tr>\n";

                        echo    "				<tr>\n";
                        echo    "					<td class=\"text\" valign=\"top\"><b>Template Data</b></td>\n";
                        echo    '					<td class="text" valign="top"><pre>'.htmlspecialchars($this->getPlainSubTemplate($name, $condition), ENT_QUOTES)."</pre></td>\n";
                        echo    "				</tr>\n";

                        unset($matches);
                        //	Check for unset variables
                        preg_match_all($this->regex_get_all_vars, $this->getPlainSubTemplate($name, $condition), $matches);

                        //	Empty the array that stores the unused Vars
                        unset($unused);

                        if (is_array($matches[0]) && count($matches[0]) > 0) {
                            //	Check, wether variable is unused
                            for ($k = 0; $k <= count($matches[0]); ++$k) {
                                if ('' != $matches[1][$k] && !isset($this->variables[$name][$matches[1][$k]]) && !isset($this->globals[$matches[1][$k]])) {
                                    $unused[] = $matches[0][$k];
                                }
                            }
                        }

                        if (is_array($unused) && count($unused) > 0) {
                            echo    "	<tr>\n";
                            echo    "		<td class=\"mid\" valign=\"top\"><b>Unused variables</b></td>\n";
                            echo    "		<td class=\"text\" valign=\"top\">\n";

                            echo    "			<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";

                            //	Display all Variables in table
                            for ($k = 0; $k <= count($unused); ++$k) {
                                echo    "				<tr>\n";
                                echo    '					<td class="text">'.$unused[$k]."</td>\n";
                                echo    "				</tr>\n";
                            }
                            echo    "			</table>\n";

                            echo    "		</td>\n";
                            echo    "	</tr>\n";
                        }

                        echo    "				<tr>\n";
                        echo    "					<td class=\"text\" colspan=\"2\">&nbsp;</td>\n";
                        echo    "				</tr>\n";
                    }
                } else {
                    //	display template Data
                    echo    "	<tr>\n";
                    echo    "		<td class=\"mid\" valign=\"top\"><b>Template Data:</b></td>\n";
                    echo    "		<td class=\"text\" valign=\"top\">\n";
                    echo    '			<pre>'.htmlspecialchars($this->getPlainTemplate($name), ENT_QUOTES)."</pre>\n";
                    echo    "		</td>\n";
                    echo    "	</tr>\n";

                    unset($matches);
                    //	Check for unset variables
                    preg_match_all($this->regex_get_all_vars, $this->getPlainTemplate($name), $matches);

                    //	Empty the array that stores the unused Vars
                    unset($unused);

                    if (is_array($matches[0]) && count($matches[0]) > 0) {
                        //	Check, wether variable is unused
                        for ($k = 0; $k < count($matches[0]); ++$k) {
                            if ('' != $matches[1][$k] && !isset($this->variables[$name][$matches[1][$k]]) && !isset($this->globals[$matches[1][$k]])) {
                                $unused[] = $matches[0][$k];
                            }
                        }
                    }

                    if (is_array($unused) && count($unused) > 0) {
                        echo    "	<tr>\n";
                        echo    "		<td class=\"mid\" valign=\"top\"><b>Unused variables</b></td>\n";
                        echo    "		<td class=\"text\" valign=\"top\">\n";

                        echo    "			<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";

                        //	Display all Variables in table
                        for ($k = 0; $k <= count($unused); ++$k) {
                            echo    "				<tr>\n";
                            echo    '					<td class="text">'.$unused[$k]."</td>\n";
                            echo    "				</tr>\n";
                        }
                        echo    "			</table>\n";

                        echo    "		</td>\n";
                        echo    "	</tr>\n";
                    }
                }

                //	Display Variables

                if (is_array($this->variables[$name]) && count($this->variables[$name]) > 0) {
                    reset($this->variables[$name]);

                    echo    "	<tr>\n";
                    echo    "		<td class=\"mid\" valign=\"top\"><b>Variables</b></td>\n";
                    echo    "		<td class=\"text\" valign=\"top\">\n";

                    echo    "			<table border=\"0\" cellpadding=\"0\" cellspacing=\"1\">\n";

                    //	Display all Variables in table
                    while (list($key, $value) = each($this->variables[$name])) {
                        if (is_array($value)) {
                            $value = implode(', ', $value);
                        }

                        echo    "				<tr>\n";
                        echo    '					<td class="text"><b>'.$this->tag_start.$key.$this->tag_end."</b></td>\n";
                        echo    "					<td class=\"text\"> => </td>\n";
                        echo    '					<td class="text">'.$value."</td>\n";
                        echo    "				</tr>\n";
                    }
                    echo    "			</table>\n";

                    echo    "		</td>\n";
                    echo    "	</tr>\n";
                }

                echo    "	<tr>\n";
                echo    "		<td class=\"text\" valign=\"top\" colspan=\"2\">&nbsp;</td>\n";
                echo    "	</tr>\n";
            }
            echo    '</table>';
        }
    }
}
