Older Newer
Wed, 30 Nov 2005 12:22:13 . . . . (p_prasse)?


Changes by last author:

Added:
This plugin creates a very versatile html table with sorting, searching, mouseover effects, one or multiple row selects, row color cycling, optional checkboxes and basically everything you will never need. (No, it will not cook coffee)

datatable is useful if you want to rely on one single block function to render all your table needs.

For examples see [block.datatable]. The most recent version of the script can also be found there.

sample.tpl

<code>

<html>

<head>

<link rel="stylesheet" type="text/css" href="style.css">

<script language="JavaScript?" type="text/javascript" src="functions.js">

</head>

<body>

{assign_adv var="data_array" value="array( array('nr'=>1, 'name'=>'Patrick'), array('nr'=>2, 'name'=>'Danny'), array('nr'=>3, 'name'=>Brigit') )"}

{datatable data=$data_array sortable=1 cycle=1 mouseover=1 width="200px" row_onClick="row_clicked(\$nr, '\$name')"}

{column id="nr" name="Number" align="right" sorttype="Numerical"}

{column id="name" name="First Name" align="center"}

{/datatable}

{javascript}

function row_clicked( nr, name )

{

alert( 'You clicked row nr '+nr+', name is '+name+'.' );

}

{/javascript}

</body>

</html>

</code>

Source Listing: function.column.php

<code>

<?php

/**

* Smarty plugin

* @package Smarty

* @subpackage actindo_plugins

*/

/**

* Smarty {columnn} block plugin

*

* This function does virtually nothing except adding the column to the table column description stack,

* see block.datatable.php for reference.

*

* File: function.column.php<br>

* Type: function<br>

* Name: column<br>

* Date: 29.Nov.2005<br>

* Purpose: Define datatable column, for documentation see block "datatable" <br>

* Parameters:<br>

* - id (required) - column-ID in data array

* - name (required) - text in table heading

* - align (optional) - align of data in cell

* - headeralign (optional) - align of text in header cell

* - sortable (optional) - override global table "sortable" attribute for this column

* - sorttype (optional) - set sort algorithm ('Alpha' for alphanumeric(default), 'Numerical' for numeric, 'Link' for Link sorting, <b>obey case!</b>)

* - truncate (optional) - truncate cell contents at char number (default 0, don't truncate)

* - checkboxes (optional) - to display checkboxes in this row, set this arg to the name of the boxes ('[]' is added automagically)

* - default (optional) - Text to display if display text is empty

* - output_printf (optional) - Format for output (default "%s")

* @author Patrick Prasse <pprasse@actindo.de>

* @version $Revision$

* @param array

* @param Smarty

* @return string

*/

function smarty_function_column( $params, &$smarty )

{

global $_smarty_datatable_stack;

for( $i=count($smarty->_tag_stack)-1; $i>=0; $i-- )

{

if( $smarty->_tag_stack[$i][0] == 'datatable' )

{

$datatable_idx = $i;

$datatable_params = $smarty->_tag_stack[$i][1];

break;

}

}

if( !isset($datatable_params) )

$smarty->trigger_error( 'Tag {column} without {datatable}!', E_USER_ERROR );

$_smarty_datatable_stack[$datatable_idx][] = $params;

return '';

}

?>

</code>

Source Listing: block.datatable.php

<code>

<?php

/**

* Smarty plugin

* @package Smarty

* @subpackage actindo_plugins

*/

/**

* Smarty {datatable} block plugin

*

* Uses lots of javascript and css and does basically everything except cooking coffee.

*

* File: block.datatable.php<br>

* Type: block<br>

* Name: datatable<br>

* Date: 29.Nov.2005<br>

* Purpose: Prints out a datatable with lots of options<br>

* Parameters:<br>

* - data (required) - data-Array (or data-Object) (for example array( array('col1'=>1, 'col2'=>2, 'col3'=>3), ... )

* - sortable (optional) - make tabs sortable, default 0

* - searchable (optional) - make table sortable, default 0

* - mouseover (optional) - mouseover effect, default 0

* - selectable (optional) - make rows selectable (1=yes, 2=multiple), default 0

* - cycle (optional) - cycle btw. alternate row colors, default 0

* - width (optional) - Width of table

* - id (optional) - id tag of datatable

* - class (optional) - class tag of datatable

* - row_* (optional) - tags of <tr> elements can be defined here (like row_onClick="" --> <tr onClick="">)<br>

* With all row_on* tags you can do variable substitution (Example: row_onClick="click_handler( \$col1 )" becomes <tr onClick="click_handler( [value of col1] )"> )

* Example:

* <pre>

* {datatable data=$data_array sortable="1" cycle="1" width="100%" row_onClick="click_handler( \$col1 )"}

* {column id="col1" name="A" align="center" checkboxes="box"}

* - this one creates a &lt;input type="checkbox" name="box[]" value="[value-of-col1]"&gt; in this column

* and checks for "checked" using get_var with the name "box"

* {column id="col1" name="Column #1 Table Heading" align="right" headeralign="left"}

* {column id="col2" name="Column #2 Table Heading" align="left"}

* {column id="col3" name="Column #3 Table Heading" align="center" default="&nbsp;"}

* {/datatable}

* </pre>

*

* @author Patrick Prasse <pprasse@actindo.de>

* @version $Revision$

* @param array

* @param string

* @param Smarty

* @param int

* @return string

*/

function smarty_block_datatable( $params, $content, &$smarty, &$repeat )

{

global $_smarty_datatable_stack, $__smarty_datatable_js_output;

$datatable_idx = count($smarty->_tag_stack)-1;

require_once $smarty->_get_plugin_filepath('shared','escape_special_chars');

require_once $smarty->_get_plugin_filepath('shared','get_var');

require_once $smarty->_get_plugin_filepath('modifier','truncate');

require_once $smarty->_get_plugin_filepath('function','html_button');

require_once $smarty->_get_plugin_filepath('function','html_text');

require_once $smarty->_get_plugin_filepath('function','html_checkboxes');

if( !isset($content) )

{

if( !isset($_smarty_datatable_stack[$datatable_idx]) )

$_smarty_datatable_stack[$datatable_idx] = array();

return '';

}

$selectable = 0;

$row_attribs = array( );

foreach($params as $_key => $_val)

{

if( substr($_key,0,4) == 'row_' )

{

$row_attribs[substr($_key,4)] = $_val;

continue;

}

switch($_key)

{

case 'class':

case 'id':

$$_key = $_val;

break;

case 'sortable':

case 'cycle':

case 'searchable':

case 'mouseover':

case 'selectable':

$$_key = (int)$_val;

break;

case 'data':

$$_key = (array)$_val;

break;

default:

if(!is_array($_val))

$extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"';

else

$smarty->trigger_error("html_select: extra attribute '$_key' cannot be an array", E_USER_NOTICE);

break;

}

}

isset($class) or $class = 'artikel_liste';

isset($id) or $id = 'datatable_'.$datatable_idx;

$htmlcode = '';

if( $searchable )

{

$htmlcode .= '<table class="artikel_liste" '.$extra.' cellpadding="0" cellspacing="0"><tr class="heading">';

$htmlcode .= '<td style="text-align: left; width: 150px;">Suchen:</td>';

$htmlcode .= '<td style="text-align: left; width: 150px;">'.smarty_function_html_text( array('name'=>'txt_'.$id, 'size'=>20, 'maxlength'=>20, 'onKeyUp'=>"searchTable('{$id}',event)", 'onKeyDown'=>"searchTable('{$id}',event)", 'autocomplete'=>'off'), $smarty ).'</td>';

$htmlcode .= '<td style="text-align: left;">'.smarty_function_html_button( array('template'=>'reset', 'type'=>'button', 'onClick'=>"document.getElementsByName( 'txt_{$id}' )[0].value = ''; searchTable('{$id}');"), $smarty ).'</td>';

$htmlcode .= '<td>&nbsp;</td></tr></table>';

}

$htmlcode .= '<table id="'.$id.'" border="0" class="'.$class.'" cellpadding="0" cellspacing="0" '.$extra.' pp_cycle="'.(int)$cycle.'" last_sortFun="" last_sortCol="-1" pp_selectable="'.$selectable.'">';

$htmlcode .= '<thead><tr class="heading">';

$col_idx = 0;

foreach( $_smarty_datatable_stack[$datatable_idx] as $col_id => $col )

{

isset($col['sortable']) or $col['sortable'] = 1;

isset($col['sorttype']) or $col['sorttype'] = 'Alpha';

$htmlcode .= '<td style="'.(isset($col['width']) ? 'width: '.$col['width'].';' : ).(isset($col['headeralign']) ? 'text-align: '.$col['headeralign'].';' : ).'">';

if( $sortable && $col['sortable'] )

{

$htmlcode .= '<a class="nodeco" href="javascript: testSortTable'.$col['sorttype'].'( document.getElementById(\''.$id.'\'), '.$col_idx.' )">';

}

$htmlcode .= $col['name'];

if( $sortable && $col['sortable'] )

$htmlcode .= '</a>';

$htmlcode .= '</td> ';

$col_idx++;

}

$htmlcode .= '</tr></thead><tbody>';

$rowidx = 0;

foreach( $data as $rowid => $row )

{

if( $cycle )

$tr_class = ( $rowidx % 2 != 0 ? 'h' : 'n' );

else

$tr_class = 'n';

$tr_extra = '';

$row_js = array();

foreach( $row_attribs as $_key => $_val )

{

if( substr($_key,0,2) != 'on' )

{

$_val = "{$_val}";

$tr_extra .= " {$_key}=\"{$_val}\"";

}

else

$row_js[strtolower($_key)][] = smarty_block_datatable_fill_var( $_val, $row );

}

if( $mouseover )

{

$row_js['onmouseover'][] = 'row_hl( document.getElementById(\''.$id.'\'), this )';

$row_js['onmouseout'][] = 'row_ll( document.getElementById(\''.$id.'\'), this )';

}

if( $selectable )

{

$row_js['onclick'][] = 'row_sel( document.getElementById(\''.$id.'\'), this )';

}

foreach( $row_js as $_key => $codes )

{

is_array($codes) or $codes = array( $codes );

$str = '';

foreach( $codes as $cod )

{

$cod = trim( $cod );

if( $cod{strlen($cod)-1} != ';' )

$cod .= ';';

$str .= $cod;

}

$tr_extra .= " {$_key}=\"{$str}\"";

}

$htmlcode .= '<tr class="'.$tr_class.'" dflt_class="'.$tr_class.'" '.$tr_extra.'>';

foreach( $_smarty_datatable_stack[$datatable_idx] as $col_id => $col )

{

if( empty($col['checkboxes']) )

{

$title = '';

isset($col['output_printf']) or $col['output_printf'] = '%s';

$tmp = sprintf( $col['output_printf'], $row[$col['id']] );

if( $col['truncate'] )

{

$tmp1 = smarty_modifier_truncate( $tmp, (int)$col['truncate'] );

if( strlen($tmp) != strlen($tmp1) )

$title = $tmp;

}

else

$tmp1 = $tmp;

if( !strlen($tmp1) && isset($col['default']) )

$tmp1 = $col['default'];

}

elseif( !empty($col['checkboxes']) )

{

if( !isset($col['checkboxes_sel']) )

$_smarty_datatable_stack[$datatable_idx][$col_id]['checkboxes_sel'] = $col['checkboxes_sel'] = smarty_function_get_var( $col['checkboxes'], $smarty );

$tmp1 = smarty_function_html_checkboxes_output( $col['checkboxes'], $row[$col['id']], , $col['checkboxes_sel'], , '', 0 );

}

$htmlcode .= '<td style="'.(isset($col['align']) ? 'text-align: '.$col['align'].';' : ).'" '.(!empty($title) ? 'title="'.$title.'"' : ).'>';

$htmlcode .= $tmp1;

$htmlcode .= '</td>';

}

$htmlcode .= '</tr>'; // DO NOT add linebreak here, triggers mozilla bug (sorting takes like 10 seconds for 2 rows!)

$rowidx++;

}

$htmlcode .= '</tbody></table>';

if( !$__smarty_datatable_js_output )

{

$__smarty_datatable_js_output = TRUE;

$htmlcode .= <<<ENDJS

\n<script language="JavaScript?" type="text/javascript">

var sort_col = 0;

</script>\n

ENDJS;

}

$htmlcode .= <<<ENDJS

\n<script language="JavaScript?" type="text/javascript">

var sort_{$id}_idx = -1;

var sortorder_{$id} = false;\n

var rows_{$id} = new Array();

var sort_col = 0;

</script>

ENDJS;

unset( $_smarty_datatable_stack[$datatable_idx] );

return $htmlcode;

}

function smarty_block_datatable_fill_var( $def, $row )

{

return eval( 'extract($row); return "'.$def.'";' );

}

?>

</code>

functions.js

<code>

var datatable_search_timeout = new Array();

function createRowsArray (table) {

var rows = new Array();

var r = 0;

if (table.tHead == null && table.tFoot == null)

for (var r1 = 0; r1 < table.rows.length; r1, r)

rows[r] = table.rows[r1];

else

for (var t = 0; t < table.tBodies.length; t++)

for (var r1 = 0; r1 < table.tBodies[t].rows.length; r1, r)

rows[r] = table.tBodies[t].rows[r1];

return rows;

}

function visibleRows( rows )

{

var n = 0;

var vrows = new Array( );

for( var i in rows )

{

if( rows[i].style.display != 'none' )

vrows[vrows.length] = rows[i];

}

return vrows;

}

function insertSortedRows(table, rows)

{

var cycle = parseInt( table.getAttribute('pp_cycle') );

if (document.all) var rowsCopy = new Array(rows.length)

for (var r = 0; r < rows.length; r++) {

if (document.all) rowsCopy[r] = rows[r].cloneNode(true);

table.deleteRow(rows[r].rowIndex);

}

if( document.all )

rowsCopy = tableSearch( table, rowsCopy );

else

rows = tableSearch( table, rows );

var tableSection = table.tBodies[table.tBodies.length - 1];

for (var r = 0; r < rows.length; r++)

{

var row = document.all ? rowsCopy[r] : rows[r];

if( cycle )

row.className = (r % 2 != 0 ? 'h' : 'n')

tableSection.appendChild(row);

}

}

function tableSearch( table, rows )

{

var srch = document.getElementsByName( 'txt_'+table.id );

if( srch && srch.length && srch[0].value.length )

{

var processed=0;

srch = srch[0].value.toLowerCase();

for( var i=0; i<rows.length; i++ )

{

var is_in = 0;

for( j=0; j<rows[i].childNodes.length; j++ )

{

if( !rows[i].childNodes[j].firstChild )

continue;

val = rows[i].childNodes[j].firstChild.nodeValue;

// is_in |= ( val.toLowerCase().search( srch ) != -1 );

is_in |= ( val.toLowerCase().indexOf( srch ) != -1 );

processed++;

}

if( !is_in )

rows[i].style.display = 'none';

else

rows[i].style.display = '';

}

// alert( processed);

}

else

{

for( var i=0; i<rows.length; i++ )

{

rows[i].style.display = '';

}

}

return rows;

}

function sortRowsAlpha (row1 , row2) {

var column = sortRowsAlpha.col;

var cell1 = row1.cells[column].firstChild.nodeValue;

var cell2 = row2.cells[column].firstChild.nodeValue;

return cell1 < cell2 ? - 1 : (cell1 == cell2 ? 0 : 1);

}

function sortRowsNumber (row1 , row2) {

var column = sortRowsNumber.col;

var cell1 = parseFloat(row1.cells[column].firstChild.nodeValue);

var cell2 = parseFloat(row2.cells[column].firstChild.nodeValue);

return cell1 < cell2 ? - 1 : (cell1 == cell2 ? 0 : 1);

}

function findFirstLinkChild (el) {

var child = el.firstChild;

while (child.tagName != 'A')

child = child.nextSibling;

return child;

}

function testSortTableAlpha(table, col) {

sortRowsAlpha.col = col;

sortTable(table, sortRowsAlpha);

table.setAttribute( 'last_sortCol', col );

table.setAttribute( 'last_sortFun', 'Alpha' );

}

function testSortTableNumerical (table, col) {

sortRowsNumber.col = col;

sortTable(table, sortRowsNumber);

table.setAttribute( 'last_sortCol', col );

table.setAttribute( 'last_sortFun', 'Numerical' );

}

function sortTable (table, sortFun)

{

var rows = createRowsArray(table);

if (rows.length > 0)

{

rows.sort(sortFun);

insertSortedRows(table, rows);

}

}

function do_searchTable( table_id )

{

var table = document.getElementById( table_id );

var sortCol = parseInt( table.getAttribute('last_sortCol') );

var sortFun = table.getAttribute('last_sortFun');

if( sortFun == 'Numerical' )

testSortTableNumerical( table, sortCol );

else if( sortFun == 'Alpha' )

testSortTableAlpha( table, sortCol );

else if( sortFun == '' )

{

var rows = createRowsArray( table );

insertSortedRows( table, rows );

}

}

function searchTable( table_id, event )

{

if( event && event.keyCode == 13 )

{

var vrows;

var table = document.getElementById( table_id );

do_searchTable( table_id );

vrows = visibleRows( createRowsArray(table) );

if( vrows.length == 1 )

{

if( vrows[0].getAttribute('onClick').length )

{

eval( vrows[0].getAttribute('onClick') );

return false;

}

}

return false;

}

if( datatable_search_timeout[table_id] )

{

window.clearTimeout( datatable_search_timeout[table_id] );

datatable_search_timeout[table_id] = undefined;

}

datatable_search_timeout[table_id] = window.setTimeout( 'do_searchTable("'+table_id+'")', 500 );

return true;

}

function row_hl( table, row )

{

if( row.className.match(/sel/) )

return;

row.className = row.getAttribute('dflt_class') + '_hl';

}

function row_ll( table, row )

{

if( row.className.match(/sel/) )

return;

row.className = row.getAttribute('dflt_class');

}

function row_sel( table, row )

{

var selectable = parseInt( table.getAttribute('pp_selectable') );

if( !selectable )

return;

else if( selectable == 2 )

{

if( row.className.match(/sel/) )

row.className = row.getAttribute('dflt_class');

else

row.className = row.getAttribute('dflt_class') + '_sel';

}

else

{

for (var t = 0; t < table.tBodies.length; t++)

for (var r1 = 0; r1 < table.tBodies[t].rows.length; r1++)

table.tBodies[t].rows[r1].className = table.tBodies[t].rows[r1].getAttribute('dflt_class');

row.className = row.getAttribute('dflt_class') + '_sel';

}

}

function datatable_get_selected( table )

{

var sel = new Array();

var r=0;

for (var t = 0; t < table.tBodies.length; t++)

for (var r1 = 0; r1 < table.tBodies[t].rows.length; r1, r)

{

if( table.tBodies[t].rows[r1].className.match(/sel/) )

sel[sel.length] = r;

}

return sel;

}

</code>

style.css

<code>

table.artikel_liste td, table.mahn_liste td

{

padding: 3px 2px 3px 2px;

font-size: 10px;

border-right: 1px solid #C0C0C0;

border-bottom: 1px solid #C0C0C0;

text-align: center;

}

table.artikel_liste td.invisible, table.mahn_liste td.invisible

{

border-bottom: none;

border-right: none;

}

table.artikel_liste tr.heading td, table.mahn_liste tr.heading td

{

background-color: #AAAAAA;

text-align: center;

font-weight: bold;

padding: 2px 3px 2px 3px;

}

table.artikel_liste tr.n td

{

background-color: #FFFFFF;

}

table.artikel_liste tr.h td

{ }

.n, .n_hl, .n_sel

{

background-color: #FFFFFF;

color: black;

font-weight: normal;

}

.h, .h_hl, .h_sel

{

background-color: #F0F0F0;

color: black;

font-weight: normal;

}

.n_hl, .h_hl

{

background-color: #CCCCCC;

cursor: pointer;

}

.n_sel, .h_sel

{

background-color: #c1b5de;

}

</code>