<?php

/**
* 
* Handle templating functions to separate business logic from display
* logic.
* 
* Based on the gospel according to Harry Fuecks, I have become
* disillusioned with templating engines.  To do anything more useful
* than simple token replacement, you need to invent a new set of
* constructs for looping, if/then/else, and so on.
* 
* In short, you might as well use PHP as your template language.  Yes,
* still have a script do your main logic, but then pass off to another
* PHP script (the template) to do the display portion.  Kind of
* counter-intuitive, but it's just as powerful as (or more so than)
* something like Smarty.
* 
* So why have a template object at all?  To make sure variable scopes
* are not exceeded.  You don't want a template to override the values
* determined by your logic script, and you certainly don't want a
* template to manipulate global variables if you can help it.  The
* object can't prevent you from exceeding scope, but it sure helps.
* 
* @author Paul M. Jones <pjones@ciaweb.net>
* @since 4.1.2
* @version $Id$
* 
*/

require_once 'PEAR.php';

class HTML_Template_Dummy extends PEAR {
	
	/**
	* 
	* Holds all assigned variables, arrays, and objects.
	* 
	* @access public
	* 
	* @var array
	* 
	*/
	
	var $vars = array();
	
	
	/**
	* 
	* Constructor.
	* 
	* @return void
	* 
	*/
	
	function HTML_Template_Dummy()
	{
		$this->PEAR();
	}
	
	
	/**
	* 
	* Assigns a token-name and value to $this->vars for use in a
	* template.
	* 
	* There are two valid ways to assign values to a template.
	* 
	* Case 1: $v1 is an array and $v2 is null. Assign a series of
	* variables where the key is the token name, and the value is token
	* value.
	* 
	* Case 2: $v1 is non-array and $v2 is non-null, which means $v1 is a
	* token name and $v2 is the token value (this allows objects,
	* strings, and numbers).
	* 
	* If $v1 is non-array and $v2 is null, that's an error (ignored).
	* 
	* If $v1 is null and $v2 is non-null, that's an error (ignored).
	* 
	* 
	* @access public
	* 
	* @param mixed $v1 This param can be either an array or a string. If
	* $v1 is an array, it must be an associative array of key-value
	* pairs where the key is a variable name in the template and the
	* value is the value for that variable in the template.  If $v1 is a
	* string, it is the name of a variable in the template.
	* 
	* @param mixed $v2 If $v1 is an array, $v2 is ignored.  Otherwise,
	* the value of $v2 is assigned to a template variable named after
	* $v1.
	* 
	* @return void
	* 
	*/
	
	function assign($v1, $v2 = null)
	{
		if (is_array($v1)) {
			// array of key-value pairs (token_name => token_value)
			foreach ($v1 as $key => $val) {
				$this->vars[$key] = $val;
			}
		} elseif (! is_array($v1) && ! is_null($v2)) {
			// straight-up variable assignment
			$this->vars[$v1] = $v2;
		}
	}
	
	
	/**
	* 
	* Parse and display a template file using the values in $this->vars.
	* 
	* @param string $_f_i_l_e_ The path and name of the .tpl.php
	* template file to parse.
	* 
	* @return void
	* 
	*/
	
	function display($_f_i_l_e_)
	{
		if (! file_exists($_f_i_l_e_)) {
			return $this->raiseError("Template file '$_f_i_l_e_' does not exist.");
		} else {
			// unset any tokenized variable named $_f_i_l_e_ so as not to
			// screw up the final include() attempt
			unset($this->vars['_f_i_l_e_']);
			
			// create local-scope variables from the assigned tokens and
			// values
			extract($this->vars);
			
			// include the requested template filename in the local scope
			// (this will execute the template display logic)
			include ($_f_i_l_e_);
		}
	}
	
	
	/**
	* 
	* Parse a template file using the token values in $this->vars and
	* return the results as a string.
	* 
	* @param string $file The path and name of the .tpl.php
	* template file to parse.
	* 
	* @return string The parsed template.
	* 
	*/
	
	function fetch($file)
	{
		if (! file_exists($file)) {
			return $this->raiseError("Template file '$file' does not exist.");
		} else {
			ob_start();
			$this->display($file);
			$output = ob_get_contents();
			ob_end_clean();
			return $output;
		}
	}
	
		
	/**
	* 
	* Output a value using echo after processing with optional modifier
	* functions.
	* 
	* Allows you to pass a space-separated list of value-manipulation
	* functions so that the value is "massaged" before output. For
	* example, if you want to strip slashes, force to lower case, and
	* convert to HTML entities (as for an input text box), you might do
	* this:
	* 
	* $this->val($value, 'stripslashes strtolower htmlentities');
	* 
	* @access public
	* 
	* @param string $value The value to be printed.
	* 
	* @param string $functions A space-separated list of
	* single-parameter functions to be applied to the $value before
	* printing.
	* 
	* @return void
	* 
	*/
	
	function val($value, $functions = null)
	{
		// is there a space-delimited function list?
		if (is_string($functions)) {
			
			// yes.  split into an array of the
			// functions to be called.
			$list = explode(' ', $functions);
			
			// loop through the function list and
			// apply to the output in sequence.
			foreach ($list as $func) {
				if (function_exists($func)) {
					$value = $func($value);
				}
			}
		}
		
		echo $value;
	}
	
	
	/**
	* 
	* Output a series of HTML <option>s based on an associative array
	* where the key is the option value and the value is the option
	* label. You can pass a "selected" value as well to tell the
	* function which option value(s) should be marked as seleted.
	* 
	* @access public
	* 
	* @param array $options An associative array of key-value pairs; the
	* key is the option value, the value is the option lable.
	* 
	* @param mixed $selected A string or array that matches one or more
	* option values, to tell the function what options should be marked
	* as selected.  Defaults to an empty array.
	* 
	* @return void
	* 
	*/
		
	function options($options, $selected = array())
	{
		// force $selected to be an array.  this allows multi-selects to
		// have multiple selected options.
		settype($selected, 'array');
		
		// is $options an array?
		if (is_array($options)) {
			
			// loop through the options array
			foreach ($options as $value => $label) {
				
				// if the option value is in the list of selected options,
				// mark it as selected, otherwise don't.
				if (in_array($value, $selected)) {
					echo "<option value=\"$value\" label=\"$label\" ";
					echo "selected=\"selected\">$label</option>\n";
				} else {
					echo "<option value=\"$value\" label=\"$label\">";
					echo "$label</option>\n";
				}
			}
		}
	}
	
	
	/**
	* 
	* Output a set of checkbox <input>s.
	* 
	* @access public
	* 
	* @param string $name The HTML "name=" value of all the checkbox
	* <input>s. The name will get [] appended to it to make it an array
	* when returned to the server.
	* 
	* @param array $options An array of key-value pairs where the key is
	* the checkbox value and the value is the checkbox label.
	* 
	* $options = array (
	* 	0 => 'zero',
	*	1 => 'one',
	*	2 => 'two'
	* );
	* 
	* @param string $set_unchecked If null, this will add no HTML to the
	* output. However, if set to any non-null value, the value will be
	* added as a hidden element before every checkbox so that if the
	* checkbox is unchecked, the hidden value will be returned instead
	* of the checked value.
	* 
	* @param string $sep The HTML text to place between every checkbox
	* in the set.
	* 
	* @param string $extra Any "extra" HTML code to place within the
	* checkbox element.
	* 
	* @return void
	* 
	*/
	
	function checkbox(
		$name,
		$options,
		$selected = array(),
		$set_unchecked = null,
		$sep = "<br />\n",
		$extra = null)
	{
		// force $selected to be an array.  this allows multi-checks to
		// have multiple checked boxes.
		settype($selected, 'array');
		
		if (is_array($options)) {
			
			// an iteration counter.  we use this to track which array
			// elements are checked and which are unchecked.
			$i = 0;
			
			foreach ($options as $value => $label) {
				
				if (! is_null($set_unchecked)) {
					// this sets the unchecked value of the checkbox.
					echo "<input type=\"hidden\" ";
					echo "name=\"{$name}[$i]\" ";
					echo "value=\"$set_unchecked\" />\n";
				}
				
				
				echo "<input type=\"checkbox\" ";
				echo "name=\"{$name}[$i]\" ";
				echo "value=\"$value\"";
				
				if (in_array($value, $selected)) {
					echo " checked=\"checked\"";
				}
				
				if (! is_null($extra)) {
					echo " $extra";
				}
				
				echo " />$label$sep";
				
				$i++;
			}
		}
	}
	
	
	/**
	* 
	* Output a set of radio <input>s with the same name.
	* 
	* @access public
	* 
	* @param string $name The HTML "name=" value of all the radio <input>s.
	* 
	* @param array $options An array of key-value pairs where the key is the
	* radio button value and the value is the radio button label.
	* 
	* $options = array (
	* 	0 => 'zero',
	*	1 => 'one',
	*	2 => 'two'
	* );
	* 
	* @param string $checked A comparison string; if any of the $option
	* element values and $checked are the same, that radio button will
	* be marked as "checked" (otherwise not).
	* 
	* @param string $extra Any "extra" HTML code to place within the
	* <input /> element.
	* 
	* @param string $sep The HTML text to place between every radio
	* button in the set.
	* 
	* @return void
	* 
	*/
	
	function radio(
		$name,
		$options,
		$checked = null,
		$set_unchecked = null,
		$sep = "<br />\n",
		$extra = null)
	{
		if (is_array($options)) {
			
			if (! is_null($set_unchecked)) {
				// this sets the unchecked value of the
				// radio button set.
				echo "<input type=\"hidden\" ";
				echo "name=\"$name\"}";
				echo "value=\"$set_unchecked\" />\n";
			}
			
			foreach ($options as $value => $label) {
				echo "<input type=\"radio\" ";
				echo "name=\"$name\" ";
				echo "value=\"$value\" ";
				
				if ($value == $checked) {
					echo "checked=\"checked\"";
				}
				echo " $extra />$label$sep";
			}
		}
	}
	
	
	/**
	* 
	* Cycle through a series of values based on an iteration number,
	* with optional group repetition.
	* 
	* You could use this for alternating background colors. Defaults to
	* "list_light" and "list_dark" as the cycle values. For example,
	* these iterations result in these returns:
	* 
	* 0	=> list_light
	* 1	=> list_dark
	* 2	=> list_light
	* 3	=> list_dark
	* 4	=> list_light
	* 5	=> list_dark
	* 
	* If you have three values in a cycle (a, b, c) the iteration
	* returns look like this:
	* 
	* 0	=> a
	* 1	=> b
	* 2	=> c
	* 3	=> a
	* 4	=> b
	* 5	=> c
	* 
	* If you repeat each cycle value (a,b,c) 2 times on the iterations,
	* the returns look like this:
	* 
	* 0 => a
	* 1 => a
	* 2 => b
	* 3 => b
	* 4 => c
	* 5 => c
	* 
	* 
	* @access public
	* 
	* @param int $iteration The iteration number for the cycle.
	* 
	* @param array $values The values to cycle through.
	* 
	* @param int $repeat The number of times to repeat a cycle value.
	* 
	* @return void
	* 
	*/
	
	function cycle($iteration, $values = null, $repeat = 1)
	{
		// default values for the cycle
		if (is_null($values) || count($values) == 0) {
			$values = array ('list_light', 'list_dark');
		}
		
		// prevent divide-by-zero errors
		if ($repeat == 0) {
			$repeat = 1;
		}
		
		echo $values[($iteration / $group) % count($values)];
	}
	
	
	/**
	* 
	* Output a formatted date using strftime() conventions.
	* 
	* @access public
	* 
	* @param string $datestring Any date-time string suitable for
	* strtotime().
	* 
	* @param string $format The strftime() formatting string.
	* 
	* @return void
	* 
	*/
	
	function date($datestring, $format = '%d %b %Y')
	{
		if (trim($datestring != '')) {
			echo strftime($format, strtotime($datestring));
		}
	}
	
	
	/**
	* 
	* Output a <script></script> link to a JavaScript file.
	* 
	* @access public
	* 
	* @param string $href The HREF leading to the JavaScript source
	* file.
	* 
	* @return void
	* 
	*/
	
	function javascript($href)
	{
		echo '<script language="javascript" type="text/javascript" src="';
		echo $href . '"></script>';
	}
	
	
	/**
	* 
	* Output a <link> link to a CSS stylesheet.
	* 
	* @access public
	* 
	* @param string $href The HREF leading to the stylesheet file.
	* 
	* @return void
	* 
	*/
	
	function stylesheet($href)
	{
		echo '<link rel="stylesheet" type="text/css" href="';
		echo $href . '" />';
	}
}
?>