Login   Register  
PHP Classes
elePHPant
Icontem

File: class.eyedatagrid.inc.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Mike Frank  >  Eyesis Data Grid Control  >  class.eyedatagrid.inc.php  >  Download  
File: class.eyedatagrid.inc.php
Role: Class source
Content type: text/plain
Description: The main data grid class
Class: Eyesis Data Grid Control
Display data from a database in a sortable table
Author: By
Last change: Fixed filter bug
Date: 5 years ago
Size: 28,623 bytes
 

Contents

Class file image Download
<?php
/**
 * EyeDataGrid
 * Provides datagrid control features
 *
 * LICENSE: This source file is subject to the BSD license
 * that is available through the world-wide-web at the following URI:
 * http://www.eyesis.ca/license.txt.  If you did not receive a copy of
 * the BSD License and are unable to obtain it through the web, please
 * send a note to mike@eyesis.ca so I can send you a copy immediately.
 *
 * @author     Micheal Frank <mike@eyesis.ca>
 * @copyright  2008 Eyesis
 * @license    http://www.eyesis.ca/license.txt  BSD License
 * @version    v1.1.6 12/3/2008 10:04:44 AM
 * @link       http://www.eyesis.ca/projects/datagrid.html
 */

class EyeDataGrid
{
	private $results_per_page = 10;
	private $column_count = 0; // Num of columns
	private $row_count = 0; // Number of rows
	private $hide_header = false; // Header visibility
	private $hide_footer = false; // Footer visibility
	private $hide_order = false; // Show ordering option
	private $show_checkboxes = false; // Show checkboxes
	private $allow_filters = false; // Allow filters or not
	private $row_select = false; // Enable row selection
	private $create_button = false; // Show create button
	private $reset_button = false; // Show reset grid button
	private $show_row_number = false; // Show row numbers
	private $hide_page_list = false; // Hide page list
	private $page = 1; // Current page
	private $primary = ''; // Tables primary key column
	private $query; // SQL query
	private $hidden = array(); // Hidden columns
	private $header = array(); // Header titles
	private $type = array(); // Column types
	private $controls = array(); // Row controls, std or custom
	private $order = false; // Current order
	private $filter = false; // Current filter
	private $limit = false; // Current limit
	private $_db, $result; // Database related
	private $select_fields = ''; // Field used to select
	private $select_where = ''; // Where clause
	private $select_table = ''; // Table to read
	private $image_path = ''; // Path to images


	 // Filename of required images
	public $img_edit = 'edit.png';
	public $img_delete = 'delete.png';
	public $img_create = 'create.png';
	public $img_reset = 'reset.png';


	// Configuration constants
	const CUSCTRL_TEXT = 1;
	const CUSCTRL_IMAGE = 2;
	const STDCTRL_EDIT = 3;
	const STDCTRL_DELETE = 4;
	const TYPE_DATE = 1;
	const TYPE_IMAGE = 2;
	const TYPE_ONCLICK = 3;
	const TYPE_ARRAY = 4;
	const TYPE_DOLLAR = 5;
	const TYPE_HREF = 6;
	const TYPE_CHECK = 7;
	const TYPE_PERCENT = 8;
	const TYPE_CUSTOM = 9;
	const TYPE_FUNCTION = 10;
	const ORDER_DESC = 'DESC';
	const ORDER_ASC = 'ASC';


	// Default text
	const TXT_RESET = 'Reset Table';
	const TXT_NORESULTS = 'No results found!';

	/**
	* Constructor
	*
	* @param EyeMySQLAdap $_db The Eyesis MySQL Adapter class
	* @param string $image_path The path to datagrid images
	*/
	public function __construct(EyeMySQLAdap $_db, $image_path = '')
	{
		$this->_db = $_db;

		if (empty($image_path))
			$this->image_path = 'images/';
		else
			$this->image_path = $image_path;

		$page = (isset($_GET['page'])) ? (int) $_GET['page'] : 0; // Page number
		$order = (isset($_GET['order'])) ? $_GET['order'] : ''; // Order clause
		$filter = (isset($_GET['filter'])) ? $_GET['filter'] : ''; // Filter clause

		// Set the limit
		if (empty($page) or $page <= 0)
			$this->setLimit(0, $this->results_per_page);
		else
			$this->page = $page;

		// Set the order
		if ($order)
		{
			list($column, $order) = $this->parseInputCond($order);
			$this->setOrder($column, $order);
		}

		// Set the filter
		if ($filter)
		{
			list($column, $value) = $this->parseInputCond($filter);
			$this->setFilter($column, $value);
		}
	}

	/**
	* Hides page drop down selection and replaces it with text
	*
	* @param $hide Show or hide the page drop down
	*/
	public function hidePageSelectList($hide = true)
	{
		$this->hide_page_list = $hide;
	}

	/**
	 * Allow filters
	 *
	 * @param boolean $allow
	 */
	public function allowFilters($allow = true)
	{
		$this->allow_filters = $allow;
	}

	/**
	 * Hide order functionality
	 *
	 * @param boolean $hide
	 */
	public function hideOrder($hide = true)
	{
		$this->hide_order = $hide;
	}

	/**
	 * Show checkboxes on each row
	 *
	 * @param boolean $show
	 */
	public function showCheckboxes($show = true)
	{
		$this->show_checkboxes = $show;
	}

	/**
	 * Hide header row
	 *
	 * @param boolean $hide
	 */
	public function hideHeader($hide = true)
	{
		$this->hide_header = $hide;
	}

	/**
	 * Hide footer row
	 *
	 * @param boolean $hide
	 */
	public function hideFooter($hide = true)
	{
		$this->hide_footer = $hide;
	}

	/**
	 * Show reset control
	 *
	 * @param string $text Display caption
	 */
	public function showReset($text = self::TXT_RESET)
	{
		$this->reset_button = $text;
	}

	/**
	 * Show row numbers
	 *
	 * @param boolean $show
	 */
	public function showRowNumber($show = true)
	{
		$this->show_row_number = $show;
	}

	/**
	 * Set the SELECT query
	 *
	 * @param string $fields Feilds to fetch from table. * for all columns
	 * @param string $table Table to select from
	 * @param string $primay Optional primary key column
	 * @param string $where Optional where condition
	 */
	public function setQuery($fields, $table, $primary = '', $where = '')
	{
		$this->primary = $primary;

		$this->select_fields = $fields;
		$this->select_table = $table;
		$this->select_where = $where;
	}

	/**
	 * Set filter
	 *
	 * @param string $column Column to apply filter clause on
	 * @param string $value Value to compare to
	 */
	private function setFilter($column, $value)
	{
		$this->filter = array('Column' => $column,
							'Value' => $value);
	}

	/**
	 * Set order
	 *
	 * @param string $column Column to apply order clause on
	 * @param string $order Direction, use ORDER_* const
	 */
	private function setOrder($column, $order = self::ORDER_DESC)
	{
		$order = ($order == self::ORDER_DESC)
					? self::ORDER_DESC
					: self::ORDER_ASC;

		$this->order = array('Column' => $column,
							'Order' => $order);
	}

	/**
	* Hides a column
	*
	* @param string $column The column to be hidden
	*/
	public function hideColumn($column)
	{
		$this->hidden[] = $column;
	}

	/**
	* Change column header caption
	*
	* @param string $column The column name
	* @param string $header The new header caption
	*/
	public function setColumnHeader($column, $header)
	{
		$this->header[$column] = $header;
	}

	/**
	* Set a column type
	*
	* @param string $column The column to apply the type to
	* @param integer $type The type of column, use TYPE_* const
	* @param mixed $criteria Specific value to each column type
	* @param mixed $criteria_2 Second specific value to each column type
	*/
	public function setColumnType($column, $type, $criteria = '', $criteria_2 = '')
	{
		$this->type[$column] = array($type, $criteria, $criteria_2);
	}

	/**
	* Sets the maximum amount of rows per page
	*
	* @param integer $num Amount of rows per page
	*/
	public function setResultsPerPage($num)
	{
		$this->results_per_page = (int) $num;
		$this->setLimit(0, (int) $num);
	}

	/**
	* Adds a standard control to a row
	*
	* @param integer $type The type of standard control, use STDCTRL_* const
	* @param string $action The action of the control (onclick code or href link)
	* @param integer $action_type The type of action, use TYPE_ONCLICK or TYPE_HREF
	*/
	public function addStandardControl($type, $action, $action_type = self::TYPE_ONCLICK)
	{
		$action = $this->parseLinkAction($action, $action_type);

		switch ($type)
		{
			case self::STDCTRL_EDIT:
				$this->controls[] = '<a ' . $action . '><img src="' . $this->image_path . $this->img_edit . '" alt="Edit" title="Edit" class="tbl-control-image"></a>';
				break;
			case self::STDCTRL_DELETE:
				$this->controls[] = '<a ' . $action . '><img src="' . $this->image_path . $this->img_delete . '" alt="Delete" title="Delete" class="tbl-control-image"></a>';
				break;
			default:
				// Invalid standard control
				break;
		}
	}

	/**
	* Adds a custom control to a row
	*
	* @param integer $type The type of custom control, use CUSCTRL_* const
	* @param string $action The action of the control (onclick code or href link)
	* @param integer $action_type The type of action, use TYPE_ONCLICK or TYPE_HREF
	* @param string $text The textual description of the control
	* @param string $image_path The location of the image if type is CUSCTRL_IMAGE
	*/
	public function addCustomControl($type = self::CUSCTRL_TEXT, $action, $action_type = self::TYPE_ONCLICK, $text, $image_src = '')
	{
		$action = $this->parseLinkAction($action, $action_type);

		switch ($type)
		{
			case self::CUSCTRL_IMAGE:
				$this->controls[] = '<a ' . $action . '><img src="' . $image_src . '" alt="' . $text . '" title="' . $text . '" class="tbl-control-image"></a>';
				break;
			default: // Default to text
				$this->controls[] = '<a ' . $action . '>' . $text . '</a>';
				break;
		}
	}

	/**
	* Adds a create control above the table
	*
	* @param string $action The action associated to the create (onclick code or href link)
	* @param integer $action_type The type of action, use TYPE_ONCLICK or TYPE_HREF
	* @param string $text The textual description of the create
	*/
	public function showCreateButton($action, $action_type = self::TYPE_ONCLICK, $text = 'New Record')
	{
		$action = $this->parseLinkAction($action, $action_type);

		$this->create_button = array('Action' => $action,
										'Text' => $text);
	}

	/**
	* Adds ability to select a entire row
	*
	* @param string $onclick The JS function to call when a row is clicked
	*/
	public function addRowSelect($onclick)
	{
		$this->row_select = $onclick;
	}

	/**
	* Data sanitization and control for filters and ordering
	*
	* @param string $in The value to be sanitized and parsed
	*/
	private function parseInputCond($in)
	{
		return explode(':', ereg_replace("[\'\"\<\>\\]", '%', $in), 2);
	}

	/**
	* Replaces our variables place holders with values
	*
	* @param array $row The row associated array
	* @param string $act The string containing place holders to replace
	* @return string
	*/
	private function parseVariables(array $row, $act)
	{
		// The only way we get an array for $act is for parameters from a column type of function
		if (is_array($act))
		{
			// Loop through each passed param and replace variables where necessary
			foreach ($act as $key => $value)
				$act[$key] = $this->parseVariables($row, $value);

			return $act;
		}

		// %_P% is an alias for the primary key, replace it with the primary key
		if ($this->primary)
			$act = str_replace('%_P%', '%' . $this->primary . '%', $act);

		preg_match_all("/%([A-Za-z0-9_ \-]*)%/", $act, $vars);

		foreach($vars[0] as $v)
			$act = str_replace($v, $row[str_replace('%', '', $v)], $act);

		return $act;
	}

	/**
	* Builds a link action
	*
	* @param string $action The action
	* @param integer $action_type The type of actions (onclick code or href link)
	* @return string
	*/
	private function parseLinkAction($action, $action_type)
	{
		if ($action_type == self::TYPE_ONCLICK)
			$action = 'href="javascript:;" onclick="' . $action . '"';
		else
			$action = 'href="' . $action . '"';

		return $action;
	}

	/**
	* Sets the limit by clause
	*
	* @param integer $low The minimum row number
	* @param integer $high The maximum row number
	*/
	private function setLimit($low, $high)
	{
		$this->limit = array('Low' => $low,
							'High' => $high);
	}

	/**
	* Checks to see if this is an ajax table
	*
	* @return boolean
	*/
	public static function isAjaxUsed()
	{
		if (!empty($_GET['useajax']) and $_GET['useajax'] == 'true')
			return true;

		return false;
	}

	/**
	* Creates the table header
	*
	*/
	private function buildHeader()
	{
		// If entire header is hidden, skip all together
		if ($this->hide_header)
			return;

		echo '<thead><tr>';

		// Get field names of result
		$headers = $this->_db->fieldNameArray($this->result);
		$this->column_count = count($headers);

		// Add a blank column if the row number is to be shown
		if ($this->show_row_number)
		{
			$this->column_count++;
			echo '<td class="tbl-header">&nbsp;</td>';
		}

		// Show checkboxes
		if ($this->show_checkboxes)
		{
			$this->column_count++;
			echo '<td class="tbl-header tbl-checkall"><input type="checkbox" name="checkall" onclick="tblToggleCheckAll()"></td>';
		}

		// Loop through each header and output it
		foreach ($headers as $t)
		{
			// Skip column if hidden
			if (in_array($t, $this->hidden))
			{
				$this->column_count--;
				continue;
			}

			// Check for header caption overrides
			if (array_key_exists($t, $this->header))
				$header = $this->header[$t];
			else
				$header = $t;

			if ($this->hide_order)
				echo '<td class="tbl-header">' . $header; // Prevent the user from changing order
			else {
				if ($this->order and $this->order['Column'] == $t)
					$order = ($this->order['Order'] == self::ORDER_ASC)
										? self::ORDER_DESC
										: self::ORDER_ASC;
				else
					$order = self::ORDER_ASC;

				echo '<td class="tbl-header"><a href="javascript:;" onclick="tblSetOrder(\'' . $t . '\', \'' . $order . '\')">' . $header . "</a>";

				// Show the user the order image if set
				if ($this->order and $this->order['Column'] == $t)
					echo '&nbsp;<img src="' . $this->image_path . 'sort_' . strtolower($this->order['Order']) . '.gif" class="tbl-order">';
			}

			// Add filters if allowed and only if the column type is not "special"
			if ($this->allow_filters and
				!in_array($this->type[$t][0], array(
									self::TYPE_ARRAY,
									self::TYPE_IMAGE,
									self::TYPE_FUNCTION,
									self::TYPE_DATE,
									self::TYPE_CHECK,
									self::TYPE_CUSTOM,
									self::TYPE_PERCENT
									)))
			{
				if ($this->filter['Column'] == $t and !empty($this->filter['Value']))
				{
					$filter_display = 'block';
					$filter_value = $this->filter['Value'];
				} else {
					$filter_display = 'none';
					$filter_value = '';
				}

				echo '<a href="javascript:;" onclick="tblShowHideFilter(\'' . $t . '\')"><img src="' . $this->image_path . 'filter.gif" class="tbl-filter-image"></a><br><div class="tbl-filter-box" id="filter-' . $t . '" style="display:' . $filter_display . '"><input type="text" size="6" id="filter-value-' . $t . '" value="'.$filter_value.'">&nbsp;<a href="javascript:;" onclick="tblSetFilter(\'' . $t . '\')">filter</a></div>';
			}

			echo '</td>';
		}

		// If we have controls, add a blank column
		if (count($this->controls) > 0)
		{
			$this->column_count++;
			echo '<td class="tbl-header">&nbsp;</td>';
		}

		echo '</tr></thead>';
	}

	/**
	* Creates the table footer
	*
	* @param integer $shown The amounts of rows being shown in the current page
	* @param integer $first The row number of the first row
	* @param integer $last The row number of the last row
	*/
	private function buildFooter($shown, $first = 0, $last = 0)
	{
		// Skip adding the footer if it is hidden
		if ($this->hide_footer)
			return;

		$pages = ceil($this->row_count / $this->results_per_page); // Total number of pages

		echo '<tfoot><tr class="tbl-footer"><td class="tbl-nav" colspan="' . $this->column_count . '"><table width="100%" class="tbl-footer"><tr><td width="33%" class="tbl-found">Found <em>' . $this->row_count . '</em> results';

		if ($this->row_count > 0)
			echo ', showing <em>' . $first . '</em> to <em>' . $last . '</em>';

		echo '</td><td wdith="33%" class="tbl-pages">';

		// Handle results that span multiple pages
		if ($this->row_count > $this->results_per_page)
		{
			if ($this->page > 1)
				echo '<a href="javascript:;" onclick="tblSetPage(1)"><img src="' . $this->image_path . 'arrow_first.gif" class="tbl-arrows" alt="&lt;&lt;" title="First Page"></a><a href="javascript:;" onclick="tblSetPage(' . ($this->page - 1) . ')"><img src="' . $this->image_path . 'arrow_left.gif" class="tbl-arrows" alt="&lt;" title="Previous Page"></a>';
			else
				echo '<img src="' . $this->image_path . 'arrow_first_disabled.gif" class="tbl-arrows" alt="&lt;&lt;" title="First Page"><img src="' . $this->image_path . 'arrow_left_disabled.gif" class="tbl-arrows" alt="&lt;" title="Previous Page">';

			// Special thanks to ionut for this next few lines
			$startpage = ($this->page > 10)
										? $this->page - 10
										: 1;
			
			$endpage = (($pages - 10) > $this->page)
										? $this->page + 10
										: $pages;

      // Only display a portion of the selectable pages
      for ($i = $startpage; $i <= $endpage; $i++)
			{
				if ($i == $this->page)
					echo '&nbsp;<span class="page-selected">' . $i . '</span>&nbsp;';
				else
					echo '&nbsp;<a href="javascript:;" onclick="tblSetPage(' . $i . ')">' . $i . '</a>&nbsp;';
			}

			if ($this->page < $pages)
				echo '<a href="javascript:;" onclick="tblSetPage(' . ($this->page + 1) . ')"><img src="' . $this->image_path . 'arrow_right.gif" class="tbl-arrows" alt="&gt;" title="Next Page"></a><a href="javascript:;" onclick="tblSetPage(' . $pages . ')"><img src="' . $this->image_path . 'arrow_last.gif" class="tbl-arrows" alt="&gt;&gt;" title="Last Page"></a>';
			else
				echo '<img src="' . $this->image_path . 'arrow_right_disabled.gif" class="tbl-arrows" alt="&gt;" title="Next Page"><img src="' . $this->image_path . 'arrow_last_disabled.gif" class="tbl-arrows" alt="&gt;&gt;" title="Last Page">';
		}

		echo '</td><td width="33%" class="tbl-page">';

		// Only show page section if we have more than one page
		if ($pages > 0)
		{
			echo 'Page ';
			if (!$this->hide_page_list and $pages > 1)
			{
				// Create a selectable drop down list for pages
				echo '<select name="tbl-page" onchange="tblSetPage(this.options[this.selectedIndex].value)">';
				for ($x = 1; $x <= $pages; $x++)
				{
					echo '<option value="' . $x . '"';
					if ($x == $this->page)
						echo ' selected="selected"';
					echo '>' . $x . '</option>';
				}
				echo '</select>';
			} else
				echo $this->page; // Just write the page number, nothing to fancy

			echo ' of ' . $pages;
		}

		echo '</td></tr></table></td></tr></tfoot>';
	}

	/**
	* Builds row controls
	*
	* @param array $row The row associated array
	*/
	private function buildControls(array $row)
	{
			// Add controls as needed
			if (count($this->controls) > 0)
			{
				echo '<td class="tbl-controls">';
				foreach ($this->controls as $ctl)
					echo $this->parseVariables($row, $ctl);
				echo '</td>';
			}
	}

	/**
	* Outputs the datagrid to the screen
	*
	*/
	public function printTable()
	{
		// Set the limit
		$this->setLimit(($this->page - 1) * $this->results_per_page, $this->results_per_page);

		// FILTER
		$filter_query = '';
		if ($this->select_where)
			$filter_query .= "(" . $this->select_where . ")";

		if ($this->allow_filters and $this->filter)
		{
			if (!strstr($this->filter['Value'], '%'))
				$filter_value = '%' . $this->filter['Value'] . '%';
			else
				$filter_value = $this->filter['Value'];

			if ($this->select_where)
				$filter_query = $filter_query . " AND ";

			$filter_query .= "(`" . $this->filter['Column'] . "` LIKE '" . $filter_value . "')";
		}
		
		if ($filter_query)
			$filter = 'WHERE ' . $filter_query;

		// ORDER
		if ($this->order)
			$order = "ORDER BY `" . $this->order['Column'] . "` " . $this->order['Order'];
		else
			$order = '';

		// LIMIT
		if ($this->limit)
			$limit = "LIMIT " . $this->limit['Low'] . ", " . $this->limit['High'];
		else
			$limit = '';

		$query = 'SELECT ' . $this->select_fields . ' FROM ' . $this->select_table . ' ' . $filter;

		// Inform the user of any errors. Commonly caused when a column is specified in the filter or order clause that does not exist
		$this->result = $this->_db->query($query . ' ' . $order . ' ' . $limit, false);
		if (!$this->result)
		{
			echo '<div style="color: red; font-weight: bold; border: 2px solid red; padding: 10px;">Oops! We ran into a problem while trying to output the table. <a href="javascript:;" onclick="tblReset()">Click here</a> to reset the table or <a href="javascript:;" onclick="alert(\'' . ereg_replace('[\'"]', '', $this->_db->error()) . '\')">here</a> to review the error.</div>';
			return;
		}

		// Count the number of rows without the limit clause
		$this->row_count = (int) $this->_db->selectOneValue('COUNT(*)', $this->select_table, $filter_query); // Old code which does not support large data sets: $this->_db->countRows($query);

		if (!$this->isAjaxUsed())
		{
			// Print out required javascript functions
			$this->printJavascript();
			echo '<script type="text/javascript">function updateTable() { window.location = "?" + params; }</script>';
		}

		echo '<form action="#" name="dg" id="dg">';

		// Output the create button
		if ($this->create_button)
			echo '<span class="tbl-create"><a ' . $this->create_button['Action'] . ' title="' . $this->create_button['Text'] . '"><img src="' . $this->image_path . $this->img_create . '" class="tbl-create-image">' . $this->create_button['Text'] . '</a></span>';

		// Output the reset button
		if ($this->reset_button)
			echo '<span class="tbl-reset"><a href="javascript:;" onclick="tblReset()" title="' . $this->reset_button .'"><img src="' . $this->image_path . $this->img_reset . '" class="tbl-reset-image">' . $this->reset_button .'</a></span>';

		echo '<table class="tbl">';

		$this->buildHeader();

		echo '<tbody>';

		if ($this->row_count == 0)
			echo '<tr><td colspan="' . $this->column_count . '" class="tbl-noresults">' . self::TXT_NORESULTS . '</td></tr>';
		else {
			$i = 0; $first = 0; $last = 0;

			while ($row = $this->_db->fetchAssoc($this->result))
			{
				echo '<tr class="tbl-row tbl-row-' . (($i % 2) ? 'odd' : 'even'); // Switch up the bgcolors on each row

				// Handle row selects
				if ($this->row_select)
					echo ' tbl-row-highlight" onclick="' . $this->parseVariables($row, $this->row_select);

				echo '">';

				$line = ($this->page == 1)
							? $i + 1
							: $i + 1 + (($this->page - 1) * $this->results_per_page);

				$last = $line; // Last line
				if ($first == 0)
					$first = $line; // First line

				if ($this->show_row_number)
					echo '<td class="tbl-row-num">' . $line . '</td>';

				if ($this->show_checkboxes)
					echo '<td align="center"><input type="checkbox" class="tbl-checkbox" id="checkbox" name="tbl-checkbox" value="' . $row[$this->primary] . '"></td>';

				foreach ($row as $key => $value)
				{
					// Skip if column is hidden
					if (in_array($key, $this->hidden))
						continue;

					// Apply a column type to the value
					if (array_key_exists($key, $this->type))
					{
						list($type, $criteria, $criteria_2) = $this->type[$key];

						switch ($type)
						{
							case self::TYPE_ONCLICK:
								if ($value)
									$value = '<a href="javascript:;" onclick="' . $this->parseVariables($row, $criteria) . '">' . $value . '</a>';
								break;

							case self::TYPE_HREF:
								if ($value)
									$value = '<a href="' . $this->parseVariables($row, $criteria) . '">' . $value . '</a>';
								break;

							case self::TYPE_DATE:
								if ($criteria_2 == true)
									$value = date($criteria, strtotime($value));
								else
									$value = date($criteria, $value);
								break;

							case self::TYPE_IMAGE:
								$value = '<img src="' . $this->parseVariables($row, $criteria) . '" id="' . $key . '-' . $i . '">';
								break;

							case self::TYPE_ARRAY:
								$value = $criteria[$value];
								break;

							case self::TYPE_CHECK:
								if ($value == '1' or $value == 'yes' or $value == 'true' or ($criteria != '' and $value == $criteria))
									$value = '<img src="' . $this->image_path . 'check.gif">';
								break;

							case self::TYPE_PERCENT:
								if ($criteria == true)
									$value *= 100; // Value is in decimal format

								$value = round($value); // Round to the nearest decimal

								$value .= '%';

								// Apply a bar if an array is supplied via criteria_2
								if (is_array($criteria_2))
									$value = '<div style="background: ' . $criteria_2['Back'] . '; width: ' . $value . '; color: ' . $criteria_2['Fore'] . ';">' . $value . '</div>';
								break;

							case self::TYPE_DOLLAR:
								$value = '$' . number_format($value, 2);
								break;

							case self::TYPE_CUSTOM:
								$value = $this->parseVariables($row, $criteria);
								break;

							case self::TYPE_FUNCTION:
								if (is_array($criteria_2))
									$value = call_user_func_array($criteria, $this->parseVariables($row, $criteria_2));
								else
									$value = call_user_func($criteria, $this->parseVariables($row, $criteria_2));
								break;

							default:
								// Invalid column type
								break;
							}
					}

					echo '<td class="tbl-cell">' . $value . '</td>';
				}

				$this->buildControls($row);

				echo '</tr>';

				$i++;
			}
		}

		echo '</tbody>';

		$this->buildFooter($i, $first, $last);

		echo '</table></form>';
	}

	/**
	 * Prints out script to handle Ajax data grids
	 *
	 * @param string $responce Responce script
	 */
	public static function useAjaxTable($responce = '')
	{
		self::printJavascript();

		// If no responce script is set, use the current script
		if (empty($responce))
			$responce = $_SERVER['PHP_SELF'];

		echo "<script type=\"text/javascript\">\n";
		echo "var xmlHttp\n";
		echo "function SetXmlHttpObject() {\n";
		echo "xmlHttp = null;\n";
		echo "try { xmlHttp = new XMLHttpRequest(); }\n";
		echo "catch (e) {\n";
		echo "try { xmlHttp = new ActiveXObject('Msxml2.XMLHTTP'); }\n";
		echo "catch (e) { xmlHttp = new ActiveXObject('Microsoft.XMLHTTP'); } }\n";
		echo "if (xmlHttp == null) {alert('Your web browser does not support Ajax'); }\n";
		echo "return xmlHttp; }\n";
		echo "function stateChanged() { if (xmlHttp.readyState == 4) { document.getElementById('eyedatagrid').innerHTML = xmlHttp.responseText; } }\n";
		echo "function updateTable() { xmlHttp = SetXmlHttpObject(); xmlHttp.onreadystatechange = stateChanged; xmlHttp.open('GET', '" . $responce . "?useajax=true' + params, true); xmlHttp.send(null); }\n";
		echo "</script>\n";
		echo "<div id=\"eyedatagrid\"></div>\n";
		echo "<script type=\"text/javascript\">updateTable();</script>\n";
	}

	/**
	* Prints the required JS functions
	*
	*/
	public function printJavascript()
	{
		if ($this)
		{
			$page = $this->page;
			$order = (($this->order) ? implode(':', $this->order) : '');
			$filter = (($this->filter) ? implode(':', $this->filter) : '');
		}

		echo "<script type=\"text/javascript\">\n";
		echo "var params = ''; var tblpage = '" . $page . "'; var tblorder = '" . $order . "'; var tblfilter = '" . $filter . "';\n";
		echo "function tblSetPage(page) { tblpage = page; params = '&page=' + page + '&order=' + tblorder + '&filter=' + tblfilter; updateTable(); }\n";
		echo "function tblSetOrder(column, order) { tblorder = column + ':' + order; params = '&page=' + tblpage + '&order=' + tblorder + '&filter=' + tblfilter; updateTable(); }\n";
		echo "function tblSetFilter(column) { val = document.getElementById('filter-value-' + column).value; tblfilter = column + ':' + val; tblpage = 1; params = '&page=1&order=' + tblorder + '&filter=' + tblfilter; updateTable(); }\n";
		echo "function tblClearFilter() { tblfilter = ''; params = '&page=1&order=' + tblorder + '&filter='; updateTable(); }\n";
		echo "function tblToggleCheckAll() { for (i = 0; i < document.dg.checkbox.length; i++) { document.dg.checkbox[i].checked = !document.dg.checkbox[i].checked; } }\n";
		echo "function tblShowHideFilter(column) { var o = document.getElementById('filter-' + column); if (o.style.display == 'block') { tblClearFilter(); } else {	o.style.display = 'block'; } }\n";
		echo "function tblReset() { params = '&page=1'; updateTable(); }\n";
		echo "</script>\n";
	}
}