<?php
/*##############################################################################
AJAX Data Grid
An AJAX enabled datagrid with pagewise scrolling, column sorting, and in-place
editing of data cells with textbox, textarea, select, and checkbox controls.
Written by: Hugo Weijes 28 DEC 2006 (hide@address.com)
License: MIT (http://www.opensource.org/licenses/mit-license.html)
This file is an extended version of TableEditor written by Andrew Sullivan
http://www.phpclasses.org/browse/package/3104.html - hide@address.com
================================================================================
Copyright (c) 2006, Hugo Weijes
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
##############################################################################*/
/* Global Variables:
$DataGrid_InstanceCount counts instances of this class (to generate unique $JsClass names)
$DataGrid_JsIncluded set to true after the Js includes have been output to the page
*/
class DataGrid
{
var $pos; //one based position of start data page
var $poscnt; //total number of records in all data (not just current page)
var $pospage; //paging size
var $JsClass = 'te'; //javascript class (variable) name and html id prefix
/*
The datapage contains a multidimensional array that consists of the data for the table.
It should be in the following format:
In the primary array, each element should have a key equal to the row id and the value is
a sub array that contains column data.
The column data subarray should have key values that are the column values from the db and the values
are the data to be displayed.
An example would be:
[1] => array(
[Name] => 'Some Guy'
[Address] => '123 Some Street'
[Phone] => '123-123-1234'
)
[2] => array(
[Name] => 'Some Girl'
[Address] => '321 Some Drive'
[Phone] => '987-987-9876'
}
etc.
*/
var $data = array();
var $RequestFile = "";
function DataGrid($data='',$RequestFile='')
{
global $DataGrid_InstanceCount;
$DataGrid_InstanceCount++;
$this->JsClass.=$DataGrid_InstanceCount;
if($data) $this->data=$data;
if($RequestFile) $this->RequestFile=$RequestFile; else $this->RequestFile=$_SERVER['PHP_SELF'];
}
function HtmlId($rowid,$colid)
{
return $this->JsClass . '.' . $rowid . '.' . $colid;
}
var $columns = array();
function AddColumnReadonly($colname,$coltitle)
{
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'readonly');
}
function AddColumnHidden($colname='',$coltitle='')
{
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'hidden');
}
function AddColumnText($colname,$coltitle)
{
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'text');
}
function AddColumnCheckbox($colname,$coltitle)
{
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'checkbox');
}
function AddColumnSelect($colname,$coltitle,$selectvalues)
{
if(!is_array($selectvalues)) $selectvalues=$this->db_array($selectvalues);
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'select','selectvalues'=>$selectvalues);
}
function AddColumnSelectkey($colname,$coltitle,$selectvalues)
{
if(!is_array($selectvalues)) $selectvalues=$this->db_assoc($selectvalues);
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'selectkey','selectvalues'=>$selectvalues);
}
function AddColumnTextarea($colname,$coltitle,$width,$height)
{
$this->columns[]=array('colname'=>$colname,'coltitle'=>$coltitle,'coltype'=>'textarea','style'=>'width:'.$width.'px; height:'.$height.'px;');
}
function db_array($sql)
{
$arr=array();
$result=mysql_query($sql);
while($row=@mysql_fetch_row($result)) $arr[]=$row[0];
return $arr;
}
function db_assoc($sql)
{
$arr=array();
$result=mysql_query($sql);
while($row=@mysql_fetch_row($result)) $arr[$row[0]]=$row[1];
return $arr;
}
/*
These vars are arrays that contain key => value pairs of attribs for the table.
For example:
['class'] => 'evenrow'
Would result in the even rows having the attrib <tr class="evenrow">
['align'] => 'center'
['valign'] => 'top'
['style'] => 'background-color: #EEEEEE;'
Would result in <tr align="center" valign="top" style="background-color: #EEEEEE;">
*/
//array that contains the table attribs
var $tattrib = array();
//odd row attribs
var $oddattrib = array();
var $odd = "<tr>";
//even row class
var $evenattrib = array();
var $even = "<tr>";
//header attribs
var $headerattrib = array();
function SetEvenRowAttribs($attrib) {
$this->evenattrib = $attrib;
$this->evenTR();
}
function SetOddRowAttribs($attrib) {
$this->oddattrib = $attrib;
$this->oddTR();
}
function SetTableAttribs($attrib) {
$this->tattrib = $attrib;
}
function SetHeaderAttribs($attrib)
{
$this->headerattrib = $attrib;
}
function oddTR()
{
$html = "<tr";
foreach ($this->oddattrib as $key => $value) $html .= " $key=\"$value\"";
$html .= ">";
$this->odd = $html;
}
function evenTR()
{
$html = "<tr";
foreach ($this->evenattrib as $key => $value) $html .= " $key=\"$value\"";
$html .= ">";
$this->even = $html;
}
//returns <span> tag with table
function GenerateTable()
{
$JsClass=$this->JsClass;
//generate table first, this creates $column (if not predefined). $column is referenced by GenerateJS)
$s = $this->GenerateTableSpan();
//include JS
return $this->GenerateJS() . "<span id=\"$JsClass.span\">\n" . $s . '</span>';
}
//returns content of the table <span> tag (this part is reloaded on scrolling the table)
function GenerateTableSpan()
{
$JsClass=$this->JsClass;
//$table .= $this->GenerateNav();
//build the table opener
$table .= "\n<table id=\"$JsClass.table\" ";
foreach ($this->tattrib as $key => $value) {
$table .= " $key=\"$value\"";
}
$table .= ">";
$table .= $this->GenerateTableBody();
//nav
$table .= "\n<tr style=\"background-color:lightyellow;\"><td colspan=\"$this->colcnt\">";
$table .= $this->GenerateNav();
$table .= "</td></tr>";
$table .= '</table>';
return $table;
}
//generates the table navigation and busy indicator
function GenerateNav()
{
$JsClass=$this->JsClass;
$pos=$this->pos; //current position 1-based
if($pos<1) $pos=1;
$posto=$pos+count($this->data)-1; //last pos in current page
$pospage=$this->pospage; //page length
$posprev=$pos-$pospage; //start pos of prev page
$posnext=$pos+$pospage; //start pos of next page
$poscnt=$this->poscnt; //total number of records
$poslast=floor(($poscnt-1)/$pospage)*$pospage+1; //start pos of last page
//build table navigator
$table .= "<span style=\"cursor:pointer; color:blue; font-weight:900; font-family:arial;\">";
$table .= " <a onclick=\"dg_move($JsClass,1)\">|<</a>";
$table .= " <a onclick=\"dg_move($JsClass,$posprev)\"><<</a>";
$table .= " <a onclick=\"dg_move($JsClass,$posnext)\">>></a>";
$table .= " <a onclick=\"dg_move($JsClass,$poslast)\">>|</a> ";
$table .= "</span>";
$table .= " Showing $pos to $posto of $poscnt records.";
$table .= " <span id=\"$JsClass.navbusy\" style=\"background-color:yellow;\"></span>";
$table .= "<br>\n";
return $table;
}
//generates the table header and data rows
function GenerateTableBody()
{
$colcnt = 0;
$JsClass=$this->JsClass;
//rowid is first column
//get column count from this->columns
$colcnt = count($this->columns);
if($colcnt==0)
{
//else get column count from first datarow and build columns headers
foreach(current($this->data) as $k=>$v) if(count($this->columns)==0)
$this->AddColumnReadonly($k,$k);
else
$this->AddColumnText($k,$k);
$colcnt = count($this->columns);
}
//build the header section
$c = 0;
$s = "\n<tr";
if (!empty($this->headerattrib))
{
foreach($this->headerattrib as $k=>$v) $s .= " $k=\"$v\"";
}
$s .= ">\n";
foreach($this->columns as $k=>$v)
{
if($v['coltype']!='hidden') $s .= "\t<th onclick=\"dg_onHeadClick($this->JsClass,'$v[colname]')\">$v[coltitle]</th>\n";
$c++;
}
while ($c < $colcnt)
{
$s .= "\t<th> </th>\n";
$c++;
}
$s .= "\n</tr>";
//build the data portion of the table
$r = 0;
foreach ($this->data as $k => $rowdat)
{
//apply odd/even row style
if ($r % 2 == 0)
$s .= "\n" . $this->even;
else
$s .= "\n" . $this->odd;
//add missing columns
while(count($rowdat)<$colcnt) $rowdat[]='';
//get rowid from first column
reset($rowdat);
$rowid=current($rowdat);
$c=0;
foreach ($rowdat as $kk => $coldat)
{
//exit if more columns than defined
if($c>=$colcnt) break;
$HtmlId=$this->HtmlId($r,$c);
$col = &$this->columns[$c];
$colid = $col['colname'];
switch($col['coltype'])
{
case 'hidden':
break;
case 'checkbox':
$s .= "\n\t<td><input type=\"checkbox\" id=\"$HtmlId\" onClick=\"dg_checkChange($JsClass,'$rowid','$colid','$HtmlId')\"" . ($coldat?'checked':'') . '>';
break;
case 'selectkey':
$s .= "\n\t<td id=\"$HtmlId\" onClick=\"dg_editCell($JsClass,'$rowid','$colid','$HtmlId')\"><input type=\"hidden\" id=\"$HtmlId.h\" value=\"".htmlspecialchars($coldat, ENT_QUOTES)."\">";
$txt=$col['selectvalues'][$coldat];
if($txt)
$s .= htmlspecialchars($txt, ENT_QUOTES);
else
$s .= "KEY: " . htmlspecialchars($coldat, ENT_QUOTES); //XXX
break;
default:
$s .= "\n\t<td id=\"$HtmlId\" onClick=\"dg_editCell($JsClass,'$rowid','$colid','$HtmlId')\">";
if ($coldat !== '')
$s .= htmlspecialchars($coldat, ENT_QUOTES);
else
$s .= " ";
}
$s .= "</td>";
$c++;
}
$s .= "\n</tr>";
$r++;
}
//save column count
$this->colcnt=$colcnt;
return $s;
}
//generate the JavaScript for the table
function GenerateJS()
{
$JsClass=$this->JsClass;
$js = '';
//determine if we need the 'includes'
global $DataGrid_JsIncluded;
if(!$DataGrid_JsIncluded)
{
$js .= '<script type="text/javascript" src="sack.js"></script>';
$js .= '<script type="text/javascript" src="datagrid.js"></script>';
$DataGrid_JsIncluded=1;
}
$js .= '<script type="text/javascript">'."\n";
$js .= "var $JsClass = new dataGrid('$JsClass','$this->RequestFile');\n";
if(is_array($this->columns)) foreach($this->columns as $k=>$v)
{
$js .= "$JsClass.m_columns['$v[colname]']={'coltype':'$v[coltype]','style':'$v[style]'};\n";
$opt=array();
if(is_array($v['selectvalues'])) foreach($v['selectvalues'] as $kk=>$vv)
{
$opt[]="'".htmlentities($kk,ENT_QUOTES)."':'".htmlentities($vv,ENT_QUOTES)."'";
}
if($opt) $js .= "$JsClass.m_columns['$v[colname]']['selectvalues']={".join($opt,",")."};\n";
}
$js .= '</script>';
return $js;
}
}