Aleksander Machniak
2012-12-07 996af3bfd9bfcac84396790a9a215d177b17c79e
commit | author | age
47124c 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/html.php                                              |
6  |                                                                       |
e019f2 7  | This file is part of the Roundcube Webmail client                     |
011e80 8  | Copyright (C) 2005-2011, The Roundcube Dev Team                       |
7fe381 9  |                                                                       |
T 10  | Licensed under the GNU General Public License version 3 or            |
11  | any later version with exceptions for skins & plugins.                |
12  | See the README file for a full license statement.                     |
47124c 13  |                                                                       |
T 14  | PURPOSE:                                                              |
15  |   Helper class to create valid XHTML code                             |
16  |                                                                       |
17  +-----------------------------------------------------------------------+
18  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
19  +-----------------------------------------------------------------------+
041c93 20 */
47124c 21
T 22
23 /**
24  * Class for HTML code creation
25  *
9ab346 26  * @package    Framework
AM 27  * @subpackage HTML
47124c 28  */
T 29 class html
30 {
31     protected $tagname;
32     protected $attrib = array();
e3e597 33     protected $allowed = array();
47124c 34     protected $content;
T 35
f23073 36     public static $doctype = 'xhtml';
47124c 37     public static $lc_tags = true;
878030 38     public static $common_attrib = array('id','class','style','title','align');
f52c93 39     public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script');
47124c 40
T 41     /**
42      * Constructor
43      *
5c461b 44      * @param array $attrib Hash array with tag attributes
47124c 45      */
T 46     public function __construct($attrib = array())
47     {
48         if (is_array($attrib)) {
49             $this->attrib = $attrib;
50         }
51     }
52
53     /**
54      * Return the tag code
55      *
56      * @return string The finally composed HTML tag
57      */
58     public function show()
59     {
e3e597 60         return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed));
47124c 61     }
T 62
63     /****** STATIC METHODS *******/
64
65     /**
66      * Generic method to create a HTML tag
67      *
5c461b 68      * @param string $tagname Tag name
A 69      * @param array  $attrib  Tag attributes as key/value pairs
70      * @param string $content Optinal Tag content (creates a container tag)
71      * @param array  $allowed_attrib List with allowed attributes, omit to allow all
47124c 72      * @return string The XHTML tag
T 73      */
74     public static function tag($tagname, $attrib = array(), $content = null, $allowed_attrib = null)
75     {
0501b6 76         if (is_string($attrib))
T 77             $attrib = array('class' => $attrib);
78
47124c 79         $inline_tags = array('a','span','img');
T 80         $suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : '';
81
82         $tagname = self::$lc_tags ? strtolower($tagname) : $tagname;
448409 83         if (isset($content) || in_array($tagname, self::$containers)) {
011e80 84             $suffix = $attrib['noclose'] ? $suffix : '</' . $tagname . '>' . $suffix;
T 85             unset($attrib['noclose'], $attrib['nl']);
86             return '<' . $tagname  . self::attrib_string($attrib, $allowed_attrib) . '>' . $content . $suffix;
47124c 87         }
T 88         else {
011e80 89             return '<' . $tagname  . self::attrib_string($attrib, $allowed_attrib) . '>' . $suffix;
47124c 90         }
f23073 91     }
T 92
93     /**
94      *
95      */
96     public static function doctype($type)
97     {
98         $doctypes = array(
99             'html5'        => '<!DOCTYPE html>',
100             'xhtml'        => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
101             'xhtml-trans'  => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
102             'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
103         );
104
105         if ($doctypes[$type]) {
106             self::$doctype = preg_replace('/-\w+$/', '', $type);
107             return $doctypes[$type];
108         }
109
110         return '';
47124c 111     }
T 112
113     /**
114      * Derrived method for <div> containers
115      *
5c461b 116      * @param mixed  $attr Hash array with tag attributes or string with class name
A 117      * @param string $cont Div content
47124c 118      * @return string HTML code
T 119      * @see html::tag()
120      */
121     public static function div($attr = null, $cont = null)
122     {
123         if (is_string($attr)) {
124             $attr = array('class' => $attr);
125         }
f5aa16 126         return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick')));
47124c 127     }
T 128
129     /**
130      * Derrived method for <p> blocks
131      *
5c461b 132      * @param mixed  $attr Hash array with tag attributes or string with class name
A 133      * @param string $cont Paragraph content
47124c 134      * @return string HTML code
T 135      * @see html::tag()
136      */
137     public static function p($attr = null, $cont = null)
138     {
139         if (is_string($attr)) {
140             $attr = array('class' => $attr);
141         }
142         return self::tag('p', $attr, $cont, self::$common_attrib);
143     }
144
145     /**
146      * Derrived method to create <img />
147      *
5c461b 148      * @param mixed $attr Hash array with tag attributes or string with image source (src)
47124c 149      * @return string HTML code
T 150      * @see html::tag()
151      */
152     public static function img($attr = null)
153     {
154         if (is_string($attr)) {
155             $attr = array('src' => $attr);
156         }
878030 157         return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib,
315418 158             array('src','alt','width','height','border','usemap','onclick')));
47124c 159     }
T 160
161     /**
162      * Derrived method for link tags
163      *
5c461b 164      * @param mixed  $attr Hash array with tag attributes or string with link location (href)
A 165      * @param string $cont Link content
47124c 166      * @return string HTML code
T 167      * @see html::tag()
168      */
169     public static function a($attr, $cont)
170     {
171         if (is_string($attr)) {
172             $attr = array('href' => $attr);
173         }
878030 174         return self::tag('a', $attr, $cont, array_merge(self::$common_attrib,
413df0 175         array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup')));
47124c 176     }
T 177
178     /**
179      * Derrived method for inline span tags
180      *
5c461b 181      * @param mixed  $attr Hash array with tag attributes or string with class name
A 182      * @param string $cont Tag content
47124c 183      * @return string HTML code
T 184      * @see html::tag()
185      */
186     public static function span($attr, $cont)
187     {
188         if (is_string($attr)) {
189             $attr = array('class' => $attr);
190         }
191         return self::tag('span', $attr, $cont, self::$common_attrib);
192     }
193
194     /**
195      * Derrived method for form element labels
196      *
5c461b 197      * @param mixed  $attr Hash array with tag attributes or string with 'for' attrib
A 198      * @param string $cont Tag content
47124c 199      * @return string HTML code
T 200      * @see html::tag()
201      */
202     public static function label($attr, $cont)
203     {
204         if (is_string($attr)) {
205             $attr = array('for' => $attr);
206         }
207         return self::tag('label', $attr, $cont, array_merge(self::$common_attrib, array('for')));
208     }
209
210     /**
95fcc3 211      * Derrived method to create <iframe></iframe>
T 212      *
5c461b 213      * @param mixed $attr Hash array with tag attributes or string with frame source (src)
95fcc3 214      * @return string HTML code
T 215      * @see html::tag()
216      */
217     public static function iframe($attr = null, $cont = null)
218     {
219         if (is_string($attr)) {
220             $attr = array('src' => $attr);
221         }
878030 222         return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib,
011e80 223             array('src','name','width','height','border','frameborder')));
T 224     }
225
226     /**
227      * Derrived method to create <script> tags
228      *
229      * @param mixed $attr Hash array with tag attributes or string with script source (src)
3a7dec 230      * @param string $cont Javascript code to be placed as tag content
011e80 231      * @return string HTML code
T 232      * @see html::tag()
233      */
234     public static function script($attr, $cont = null)
235     {
236         if (is_string($attr)) {
237             $attr = array('src' => $attr);
238         }
239         if ($cont) {
240             if (self::$doctype == 'xhtml')
241                 $cont = "\n/* <![CDATA[ */\n" . $cont . "\n/* ]]> */\n";
242             else
243                 $cont = "\n" . $cont . "\n";
244         }
245
246         return self::tag('script', $attr + array('type' => 'text/javascript', 'nl' => true),
247             $cont, array_merge(self::$common_attrib, array('src','type','charset')));
95fcc3 248     }
T 249
250     /**
47124c 251      * Derrived method for line breaks
T 252      *
253      * @return string HTML code
254      * @see html::tag()
255      */
031491 256     public static function br($attrib = array())
47124c 257     {
031491 258         return self::tag('br', $attrib);
47124c 259     }
T 260
261     /**
262      * Create string with attributes
263      *
5c461b 264      * @param array $attrib Associative arry with tag attributes
A 265      * @param array $allowed List of allowed attributes
47124c 266      * @return string Valid attribute string
T 267      */
268     public static function attrib_string($attrib = array(), $allowed = null)
269     {
270         if (empty($attrib)) {
271             return '';
272         }
273
274         $allowed_f = array_flip((array)$allowed);
275         $attrib_arr = array();
276         foreach ($attrib as $key => $value) {
277             // skip size if not numeric
0c2596 278             if ($key == 'size' && !is_numeric($value)) {
47124c 279                 continue;
T 280             }
281
282             // ignore "internal" or not allowed attributes
283             if ($key == 'nl' || ($allowed && !isset($allowed_f[$key])) || $value === null) {
284                 continue;
285             }
286
287             // skip empty eventhandlers
288             if (preg_match('/^on[a-z]+/', $key) && !$value) {
289                 continue;
290             }
291
292             // attributes with no value
293             if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) {
294                 if ($value) {
011e80 295                     $attrib_arr[] = $key . '="' . $key . '"';
47124c 296                 }
T 297             }
298             else {
d66e50 299                 $attrib_arr[] = $key . '="' . self::quote($value) . '"';
47124c 300             }
T 301         }
0c2596 302
47124c 303         return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
T 304     }
0c2596 305
A 306     /**
307      * Convert a HTML attribute string attributes to an associative array (name => value)
308      *
309      * @param string Input string
310      * @return array Key-value pairs of parsed attributes
311      */
312     public static function parse_attrib_string($str)
313     {
314         $attrib = array();
315         $regexp = '/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui';
316
317         preg_match_all($regexp, stripslashes($str), $regs, PREG_SET_ORDER);
318
319         // convert attributes to an associative array (name => value)
320         if ($regs) {
321             foreach ($regs as $attr) {
322                 $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]);
323             }
324         }
325
326         return $attrib;
327     }
328
329     /**
330      * Replacing specials characters in html attribute value
331      *
d66e50 332      * @param string $str Input string
0c2596 333      *
d66e50 334      * @return string The quoted string
0c2596 335      */
d66e50 336     public static function quote($str)
0c2596 337     {
a92beb 338         return @htmlspecialchars($str, ENT_COMPAT, RCUBE_CHARSET);
0c2596 339     }
47124c 340 }
0c2596 341
47124c 342
T 343 /**
344  * Class to create an HTML input field
345  *
346  * @package HTML
347  */
348 class html_inputfield extends html
349 {
350     protected $tagname = 'input';
351     protected $type = 'text';
0c2596 352     protected $allowed = array(
b8dc3e 353         'type','name','value','size','tabindex','autocapitalize',
fd6f6e 354         'autocomplete','checked','onchange','onclick','disabled','readonly',
0c2596 355         'spellcheck','results','maxlength','src','multiple','placeholder',
A 356     );
47124c 357
5c461b 358     /**
A 359      * Object constructor
360      *
361      * @param array $attrib Associative array with tag attributes
362      */
47124c 363     public function __construct($attrib = array())
T 364     {
365         if (is_array($attrib)) {
366             $this->attrib = $attrib;
367         }
368
369         if ($attrib['type']) {
370             $this->type = $attrib['type'];
371         }
372     }
373
374     /**
375      * Compose input tag
376      *
5c461b 377      * @param string $value Field value
A 378      * @param array  $attrib Additional attributes to override
47124c 379      * @return string HTML output
T 380      */
381     public function show($value = null, $attrib = null)
382     {
383         // overwrite object attributes
384         if (is_array($attrib)) {
385             $this->attrib = array_merge($this->attrib, $attrib);
386         }
387
388         // set value attribute
389         if ($value !== null) {
390             $this->attrib['value'] = $value;
391         }
392         // set type
393         $this->attrib['type'] = $this->type;
394         return parent::show();
395     }
396 }
397
398 /**
399  * Class to create an HTML password field
400  *
401  * @package HTML
402  */
403 class html_passwordfield extends html_inputfield
404 {
405     protected $type = 'password';
406 }
407
408 /**
409  * Class to create an hidden HTML input field
410  *
411  * @package HTML
412  */
413
66d215 414 class html_hiddenfield extends html
47124c 415 {
66d215 416     protected $tagname = 'input';
47124c 417     protected $type = 'hidden';
T 418     protected $fields_arr = array();
66d215 419     protected $allowed = array('type','name','value','onchange','disabled','readonly');
47124c 420
T 421     /**
422      * Constructor
423      *
5c461b 424      * @param array $attrib Named tag attributes
47124c 425      */
T 426     public function __construct($attrib = null)
427     {
428         if (is_array($attrib)) {
429             $this->add($attrib);
430         }
431     }
432
433     /**
434      * Add a hidden field to this instance
435      *
5c461b 436      * @param array $attrib Named tag attributes
47124c 437      */
T 438     public function add($attrib)
439     {
440         $this->fields_arr[] = $attrib;
441     }
442
443     /**
444      * Create HTML code for the hidden fields
445      *
446      * @return string Final HTML code
447      */
448     public function show()
449     {
450         $out = '';
451         foreach ($this->fields_arr as $attrib) {
452             $out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
453         }
454         return $out;
455     }
456 }
457
458 /**
459  * Class to create HTML radio buttons
460  *
461  * @package HTML
462  */
463 class html_radiobutton extends html_inputfield
464 {
465     protected $type = 'radio';
466
467     /**
468      * Get HTML code for this object
469      *
5c461b 470      * @param string $value  Value of the checked field
A 471      * @param array  $attrib Additional attributes to override
47124c 472      * @return string HTML output
T 473      */
474     public function show($value = '', $attrib = null)
475     {
476         // overwrite object attributes
477         if (is_array($attrib)) {
478             $this->attrib = array_merge($this->attrib, $attrib);
479         }
480
481         // set value attribute
ff6def 482         $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
47124c 483
T 484         return parent::show();
485     }
486 }
487
488 /**
489  * Class to create HTML checkboxes
490  *
491  * @package HTML
492  */
493 class html_checkbox extends html_inputfield
494 {
495     protected $type = 'checkbox';
496
497     /**
498      * Get HTML code for this object
499      *
5c461b 500      * @param string $value  Value of the checked field
A 501      * @param array  $attrib Additional attributes to override
47124c 502      * @return string HTML output
T 503      */
504     public function show($value = '', $attrib = null)
505     {
506         // overwrite object attributes
507         if (is_array($attrib)) {
508             $this->attrib = array_merge($this->attrib, $attrib);
509         }
510
511         // set value attribute
ff6def 512         $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
47124c 513
T 514         return parent::show();
515     }
516 }
517
518 /**
519  * Class to create an HTML textarea
520  *
521  * @package HTML
522  */
523 class html_textarea extends html
524 {
525     protected $tagname = 'textarea';
878030 526     protected $allowed = array('name','rows','cols','wrap','tabindex',
413df0 527         'onchange','disabled','readonly','spellcheck');
47124c 528
T 529     /**
530      * Get HTML code for this object
531      *
5c461b 532      * @param string $value  Textbox value
A 533      * @param array  $attrib Additional attributes to override
47124c 534      * @return string HTML output
T 535      */
536     public function show($value = '', $attrib = null)
537     {
538         // overwrite object attributes
539         if (is_array($attrib)) {
540             $this->attrib = array_merge($this->attrib, $attrib);
541         }
542
543         // take value attribute as content
54d830 544         if (empty($value) && !empty($this->attrib['value'])) {
47124c 545             $value = $this->attrib['value'];
T 546         }
547
548         // make shure we don't print the value attribute
549         if (isset($this->attrib['value'])) {
550             unset($this->attrib['value']);
551         }
552
0a1dd5 553         if (!empty($value) && empty($this->attrib['is_escaped'])) {
d66e50 554             $value = self::quote($value);
47124c 555         }
6025c8 556
878030 557         return self::tag($this->tagname, $this->attrib, $value,
413df0 558             array_merge(self::$common_attrib, $this->allowed));
47124c 559     }
T 560 }
561
562 /**
563  * Builder for HTML drop-down menus
564  * Syntax:<pre>
565  * // create instance. arguments are used to set attributes of select-tag
566  * $select = new html_select(array('name' => 'fieldname'));
567  *
568  * // add one option
569  * $select->add('Switzerland', 'CH');
570  *
571  * // add multiple options
572  * $select->add(array('Switzerland','Germany'), array('CH','DE'));
573  *
574  * // generate pulldown with selection 'Switzerland'  and return html-code
575  * // as second argument the same attributes available to instanciate can be used
576  * print $select->show('CH');
577  * </pre>
578  *
579  * @package HTML
580  */
581 class html_select extends html
582 {
583     protected $tagname = 'select';
584     protected $options = array();
878030 585     protected $allowed = array('name','size','tabindex','autocomplete',
413df0 586         'multiple','onchange','disabled','rel');
0c2596 587
47124c 588     /**
T 589      * Add a new option to this drop-down
590      *
5c461b 591      * @param mixed $names  Option name or array with option names
A 592      * @param mixed $values Option value or array with option values
47124c 593      */
T 594     public function add($names, $values = null)
595     {
596         if (is_array($names)) {
597             foreach ($names as $i => $text) {
598                 $this->options[] = array('text' => $text, 'value' => $values[$i]);
599             }
600         }
601         else {
602             $this->options[] = array('text' => $names, 'value' => $values);
603         }
604     }
605
606     /**
607      * Get HTML code for this object
608      *
5c461b 609      * @param string $select Value of the selection option
A 610      * @param array  $attrib Additional attributes to override
47124c 611      * @return string HTML output
T 612      */
613     public function show($select = array(), $attrib = null)
614     {
615         // overwrite object attributes
616         if (is_array($attrib)) {
617             $this->attrib = array_merge($this->attrib, $attrib);
618         }
619
620         $this->content = "\n";
621         $select = (array)$select;
622         foreach ($this->options as $option) {
623             $attr = array(
624                 'value' => $option['value'],
ff6def 625                 'selected' => (in_array($option['value'], $select, true) ||
5b3dd4 626                   in_array($option['text'], $select, true)) ? 1 : null);
47124c 627
0a1dd5 628             $option_content = $option['text'];
AM 629             if (empty($this->attrib['is_escaped'])) {
d66e50 630                 $option_content = self::quote($option_content);
0a1dd5 631             }
AM 632
633             $this->content .= self::tag('option', $attr, $option_content);
47124c 634         }
0c2596 635
47124c 636         return parent::show();
T 637     }
638 }
639
640
641 /**
642  * Class to build an HTML table
643  *
644  * @package HTML
645  */
646 class html_table extends html
647 {
648     protected $tagname = 'table';
878030 649     protected $allowed = array('id','class','style','width','summary',
413df0 650         'cellpadding','cellspacing','border');
878030 651
47124c 652     private $header = array();
T 653     private $rows = array();
654     private $rowindex = 0;
655     private $colindex = 0;
656
5c461b 657     /**
A 658      * Constructor
659      *
660      * @param array $attrib Named tag attributes
661      */
47124c 662     public function __construct($attrib = array())
T 663     {
f23073 664         $default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array();
T 665         $this->attrib = array_merge($attrib, $default_attrib);
47124c 666     }
T 667
668     /**
669      * Add a table cell
670      *
5c461b 671      * @param array  $attr Cell attributes
A 672      * @param string $cont Cell content
47124c 673      */
T 674     public function add($attr, $cont)
675     {
676         if (is_string($attr)) {
677             $attr = array('class' => $attr);
678         }
679
680         $cell = new stdClass;
681         $cell->attrib = $attr;
682         $cell->content = $cont;
683
684         $this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
ae44bf 685         $this->colindex += max(1, intval($attr['colspan']));
47124c 686
ae44bf 687         if ($this->attrib['cols'] && $this->colindex >= $this->attrib['cols']) {
47124c 688             $this->add_row();
T 689         }
690     }
691
692     /**
693      * Add a table header cell
694      *
5c461b 695      * @param array  $attr Cell attributes
A 696      * @param string $cont Cell content
47124c 697      */
83a763 698     public function add_header($attr, $cont)
47124c 699     {
413df0 700         if (is_string($attr)) {
AM 701             $attr = array('class' => $attr);
702         }
47124c 703
T 704         $cell = new stdClass;
705         $cell->attrib = $attr;
706         $cell->content = $cont;
707         $this->header[] = $cell;
708     }
709
cc97ea 710      /**
T 711      * Remove a column from a table
712      * Useful for plugins making alterations
713      * 
714      * @param string $class 
715      */
716     public function remove_column($class)
717     {
718         // Remove the header
feac48 719         foreach ($this->header as $index=>$header){
A 720             if ($header->attrib['class'] == $class){
cc97ea 721                 unset($this->header[$index]);
T 722                 break;
723             }
724         }
725
726         // Remove cells from rows
feac48 727         foreach ($this->rows as $i=>$row){
A 728             foreach ($row->cells as $j=>$cell){
729                 if ($cell->attrib['class'] == $class){
cc97ea 730                     unset($this->rows[$i]->cells[$j]);
T 731                     break;
732                 }
733             }
734         }
735     }
736
47124c 737     /**
T 738      * Jump to next row
739      *
5c461b 740      * @param array $attr Row attributes
47124c 741      */
83a763 742     public function add_row($attr = array())
47124c 743     {
T 744         $this->rowindex++;
745         $this->colindex = 0;
746         $this->rows[$this->rowindex] = new stdClass;
747         $this->rows[$this->rowindex]->attrib = $attr;
748         $this->rows[$this->rowindex]->cells = array();
749     }
750
ffae15 751     /**
feac48 752      * Set row attributes
ffae15 753      *
feac48 754      * @param array $attr  Row attributes
A 755      * @param int   $index Optional row index (default current row index)
ffae15 756      */
feac48 757     public function set_row_attribs($attr = array(), $index = null)
ffae15 758     {
413df0 759         if (is_string($attr)) {
AM 760             $attr = array('class' => $attr);
761         }
ffae15 762
413df0 763         if ($index === null) {
feac48 764             $index = $this->rowindex;
413df0 765         }
feac48 766
24201d 767         $this->rows[$index]->attrib = $attr;
feac48 768     }
A 769
770     /**
771      * Get row attributes
772      *
773      * @param int $index Row index
774      *
775      * @return array Row attributes
776      */
777     public function get_row_attribs($index = null)
778     {
413df0 779         if ($index === null) {
feac48 780             $index = $this->rowindex;
413df0 781         }
feac48 782
A 783         return $this->rows[$index] ? $this->rows[$index]->attrib : null;
ffae15 784     }
47124c 785
T 786     /**
787      * Build HTML output of the table data
788      *
5c461b 789      * @param array $attrib Table attributes
47124c 790      * @return string The final table HTML code
T 791      */
46290a 792     public function show($attrib = null)
47124c 793     {
f92aba 794         if (is_array($attrib))
T 795             $this->attrib = array_merge($this->attrib, $attrib);
feac48 796
f92aba 797         $thead = $tbody = "";
47124c 798
T 799         // include <thead>
800         if (!empty($this->header)) {
801             $rowcontent = '';
802             foreach ($this->header as $c => $col) {
83a763 803                 $rowcontent .= self::tag('td', $col->attrib, $col->content);
47124c 804             }
3f3ec1 805             $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib));
47124c 806         }
T 807
808         foreach ($this->rows as $r => $row) {
809             $rowcontent = '';
810             foreach ($row->cells as $c => $col) {
811                 $rowcontent .= self::tag('td', $col->attrib, $col->content);
812             }
813
814             if ($r < $this->rowindex || count($row->cells)) {
3f3ec1 815                 $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib);
47124c 816             }
T 817         }
818
819         if ($this->attrib['rowsonly']) {
820             return $tbody;
821         }
822
823         // add <tbody>
824         $this->content = $thead . self::tag('tbody', null, $tbody);
825
826         unset($this->attrib['cols'], $this->attrib['rowsonly']);
827         return parent::show();
828     }
feac48 829
35c31e 830     /**
T 831      * Count number of rows
832      *
833      * @return The number of rows
834      */
835     public function size()
836     {
837       return count($this->rows);
838     }
6f6efa 839
A 840     /**
841      * Remove table body (all rows)
842      */
843     public function remove_body()
844     {
845         $this->rows     = array();
846         $this->rowindex = 0;
847     }
848
47124c 849 }