]> git.donarmstrong.com Git - roundcube.git/blob - program/include/html.php
Imported Upstream version 0.2.1
[roundcube.git] / program / include / html.php
1 <?php
2
3 /*
4  +-----------------------------------------------------------------------+
5  | program/include/html.php                                              |
6  |                                                                       |
7  | This file is part of the RoundCube Webmail client                     |
8  | Copyright (C) 2005-2009, RoundCube Dev, - Switzerland                 |
9  | Licensed under the GNU GPL                                            |
10  |                                                                       |
11  | PURPOSE:                                                              |
12  |   Helper class to create valid XHTML code                             |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id: $
19
20  */
21
22
23 /**
24  * Class for HTML code creation
25  *
26  * @package HTML
27  */
28 class html
29 {
30     protected $tagname;
31     protected $attrib = array();
32     protected $allowed = array();
33     protected $content;
34
35     public static $common_attrib = array('id','class','style','title','align');
36     public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style');
37     public static $lc_tags = true;
38
39     /**
40      * Constructor
41      *
42      * @param array Hash array with tag attributes
43      */
44     public function __construct($attrib = array())
45     {
46         if (is_array($attrib)) {
47             $this->attrib = $attrib;
48         }
49     }
50
51     /**
52      * Return the tag code
53      *
54      * @return string The finally composed HTML tag
55      */
56     public function show()
57     {
58         return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed));
59     }
60
61     /****** STATIC METHODS *******/
62
63     /**
64      * Generic method to create a HTML tag
65      *
66      * @param string Tag name
67      * @param array  Tag attributes as key/value pairs
68      * @param string Optinal Tag content (creates a container tag)
69      * @param array  List with allowed attributes, omit to allow all
70      * @return string The XHTML tag
71      */
72     public static function tag($tagname, $attrib = array(), $content = null, $allowed_attrib = null)
73     {
74         $inline_tags = array('a','span','img');
75         $suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : '';
76
77         $tagname = self::$lc_tags ? strtolower($tagname) : $tagname;
78         if ($content || in_array($tagname, self::$containers)) {
79             $templ = $attrib['noclose'] ? "<%s%s>%s" : "<%s%s>%s</%s>%s";
80             unset($attrib['noclose']);
81             return sprintf($templ, $tagname, self::attrib_string($attrib, $allowed_attrib), $content, $tagname, $suffix);
82         }
83         else {
84             return sprintf("<%s%s />%s", $tagname, self::attrib_string($attrib, $allowed_attrib), $suffix);
85         }
86     }
87
88     /**
89      * Derrived method for <div> containers
90      *
91      * @param mixed  Hash array with tag attributes or string with class name
92      * @param string Div content
93      * @return string HTML code
94      * @see html::tag()
95      */
96     public static function div($attr = null, $cont = null)
97     {
98         if (is_string($attr)) {
99             $attr = array('class' => $attr);
100         }
101         return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick')));
102     }
103
104     /**
105      * Derrived method for <p> blocks
106      *
107      * @param mixed  Hash array with tag attributes or string with class name
108      * @param string Paragraph content
109      * @return string HTML code
110      * @see html::tag()
111      */
112     public static function p($attr = null, $cont = null)
113     {
114         if (is_string($attr)) {
115             $attr = array('class' => $attr);
116         }
117         return self::tag('p', $attr, $cont, self::$common_attrib);
118     }
119
120     /**
121      * Derrived method to create <img />
122      *
123      * @param mixed Hash array with tag attributes or string with image source (src)
124      * @return string HTML code
125      * @see html::tag()
126      */
127     public static function img($attr = null)
128     {
129         if (is_string($attr)) {
130             $attr = array('src' => $attr);
131         }
132         return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib, array('src','alt','width','height','border','usemap')));
133     }
134
135     /**
136      * Derrived method for link tags
137      *
138      * @param mixed  Hash array with tag attributes or string with link location (href)
139      * @param string Link content
140      * @return string HTML code
141      * @see html::tag()
142      */
143     public static function a($attr, $cont)
144     {
145         if (is_string($attr)) {
146             $attr = array('href' => $attr);
147         }
148         return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, array('href','target','name','onclick','onmouseover','onmouseout','onmousedown','onmouseup')));
149     }
150
151     /**
152      * Derrived method for inline span tags
153      *
154      * @param mixed  Hash array with tag attributes or string with class name
155      * @param string Tag content
156      * @return string HTML code
157      * @see html::tag()
158      */
159     public static function span($attr, $cont)
160     {
161         if (is_string($attr)) {
162             $attr = array('class' => $attr);
163         }
164         return self::tag('span', $attr, $cont, self::$common_attrib);
165     }
166
167     /**
168      * Derrived method for form element labels
169      *
170      * @param mixed  Hash array with tag attributes or string with 'for' attrib
171      * @param string Tag content
172      * @return string HTML code
173      * @see html::tag()
174      */
175     public static function label($attr, $cont)
176     {
177         if (is_string($attr)) {
178             $attr = array('for' => $attr);
179         }
180         return self::tag('label', $attr, $cont, array_merge(self::$common_attrib, array('for')));
181     }
182
183     /**
184      * Derrived method to create <iframe></iframe>
185      *
186      * @param mixed Hash array with tag attributes or string with frame source (src)
187      * @return string HTML code
188      * @see html::tag()
189      */
190     public static function iframe($attr = null, $cont = null)
191     {
192         if (is_string($attr)) {
193             $attr = array('src' => $attr);
194         }
195         return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib, array('src','name','width','height','border','frameborder')));
196     }
197
198     /**
199      * Derrived method for line breaks
200      *
201      * @return string HTML code
202      * @see html::tag()
203      */
204     public static function br()
205     {
206         return self::tag('br');
207     }
208
209     /**
210      * Create string with attributes
211      *
212      * @param array Associative arry with tag attributes
213      * @param array List of allowed attributes
214      * @return string Valid attribute string
215      */
216     public static function attrib_string($attrib = array(), $allowed = null)
217     {
218         if (empty($attrib)) {
219             return '';
220         }
221
222         $allowed_f = array_flip((array)$allowed);
223         $attrib_arr = array();
224         foreach ($attrib as $key => $value) {
225             // skip size if not numeric
226             if (($key=='size' && !is_numeric($value))) {
227                 continue;
228             }
229
230             // ignore "internal" or not allowed attributes
231             if ($key == 'nl' || ($allowed && !isset($allowed_f[$key])) || $value === null) {
232                 continue;
233             }
234
235             // skip empty eventhandlers
236             if (preg_match('/^on[a-z]+/', $key) && !$value) {
237                 continue;
238             }
239
240             // attributes with no value
241             if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) {
242                 if ($value) {
243                     $attrib_arr[] = sprintf('%s="%s"', $key, $key);
244                 }
245             }
246             else if ($key=='value') {
247                 $attrib_arr[] = sprintf('%s="%s"', $key, Q($value, 'strict', false));
248             }
249             else {
250                 $attrib_arr[] = sprintf('%s="%s"', $key, Q($value));
251             }
252         }
253         return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
254     }
255 }
256
257 /**
258  * Class to create an HTML input field
259  *
260  * @package HTML
261  */
262 class html_inputfield extends html
263 {
264     protected $tagname = 'input';
265     protected $type = 'text';
266     protected $allowed = array('type','name','value','size','tabindex','autocomplete','checked','onchange','onclick','disabled','readonly','spellcheck','results');
267
268     public function __construct($attrib = array())
269     {
270         if (is_array($attrib)) {
271             $this->attrib = $attrib;
272         }
273
274         if ($attrib['type']) {
275             $this->type = $attrib['type'];
276         }
277
278         if ($attrib['newline']) {
279             $this->newline = true;
280         }
281     }
282
283     /**
284      * Compose input tag
285      *
286      * @param string Field value
287      * @param array Additional attributes to override
288      * @return string HTML output
289      */
290     public function show($value = null, $attrib = null)
291     {
292         // overwrite object attributes
293         if (is_array($attrib)) {
294             $this->attrib = array_merge($this->attrib, $attrib);
295         }
296
297         // set value attribute
298         if ($value !== null) {
299             $this->attrib['value'] = $value;
300         }
301         // set type
302         $this->attrib['type'] = $this->type;
303         return parent::show();
304     }
305 }
306
307 /**
308  * Class to create an HTML password field
309  *
310  * @package HTML
311  */
312 class html_passwordfield extends html_inputfield
313 {
314     protected $type = 'password';
315 }
316
317 /**
318  * Class to create an hidden HTML input field
319  *
320  * @package HTML
321  */
322
323 class html_hiddenfield extends html_inputfield
324 {
325     protected $type = 'hidden';
326     protected $fields_arr = array();
327     protected $newline = true;
328
329     /**
330      * Constructor
331      *
332      * @param array Named tag attributes
333      */
334     public function __construct($attrib = null)
335     {
336         if (is_array($attrib)) {
337             $this->add($attrib);
338         }
339     }
340
341     /**
342      * Add a hidden field to this instance
343      *
344      * @param array Named tag attributes
345      */
346     public function add($attrib)
347     {
348         $this->fields_arr[] = $attrib;
349     }
350
351     /**
352      * Create HTML code for the hidden fields
353      *
354      * @return string Final HTML code
355      */
356     public function show()
357     {
358         $out = '';
359         foreach ($this->fields_arr as $attrib) {
360             $out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
361         }
362         return $out;
363     }
364 }
365
366 /**
367  * Class to create HTML radio buttons
368  *
369  * @package HTML
370  */
371 class html_radiobutton extends html_inputfield
372 {
373     protected $type = 'radio';
374
375     /**
376      * Get HTML code for this object
377      *
378      * @param string Value of the checked field
379      * @param array Additional attributes to override
380      * @return string HTML output
381      */
382     public function show($value = '', $attrib = null)
383     {
384         // overwrite object attributes
385         if (is_array($attrib)) {
386             $this->attrib = array_merge($this->attrib, $attrib);
387         }
388
389         // set value attribute
390         $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
391
392         return parent::show();
393     }
394 }
395
396 /**
397  * Class to create HTML checkboxes
398  *
399  * @package HTML
400  */
401 class html_checkbox extends html_inputfield
402 {
403     protected $type = 'checkbox';
404
405     /**
406      * Get HTML code for this object
407      *
408      * @param string Value of the checked field
409      * @param array Additional attributes to override
410      * @return string HTML output
411      */
412     public function show($value = '', $attrib = null)
413     {
414         // overwrite object attributes
415         if (is_array($attrib)) {
416             $this->attrib = array_merge($this->attrib, $attrib);
417         }
418
419         // set value attribute
420         $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
421
422         return parent::show();
423     }
424 }
425
426 /**
427  * Class to create an HTML textarea
428  *
429  * @package HTML
430  */
431 class html_textarea extends html
432 {
433     protected $tagname = 'textarea';
434     protected $allowed = array('name','rows','cols','wrap','tabindex','onchange','disabled','readonly','spellcheck');
435
436     /**
437      * Get HTML code for this object
438      *
439      * @param string Textbox value
440      * @param array Additional attributes to override
441      * @return string HTML output
442      */
443     public function show($value = '', $attrib = null)
444     {
445         // overwrite object attributes
446         if (is_array($attrib)) {
447             $this->attrib = array_merge($this->attrib, $attrib);
448         }
449
450         // take value attribute as content
451         if (empty($value) && !empty($this->attrib['value'])) {
452             $value = $this->attrib['value'];
453         }
454
455         // make shure we don't print the value attribute
456         if (isset($this->attrib['value'])) {
457             unset($this->attrib['value']);
458         }
459
460         if (!empty($value) && !ereg('mce_editor', $this->attrib['class'])) {
461             $value = Q($value, 'strict', false);
462         }
463
464         return self::tag($this->tagname, $this->attrib, $value, array_merge(self::$common_attrib, $this->allowed));
465     }
466 }
467
468 /**
469  * Builder for HTML drop-down menus
470  * Syntax:<pre>
471  * // create instance. arguments are used to set attributes of select-tag
472  * $select = new html_select(array('name' => 'fieldname'));
473  *
474  * // add one option
475  * $select->add('Switzerland', 'CH');
476  *
477  * // add multiple options
478  * $select->add(array('Switzerland','Germany'), array('CH','DE'));
479  *
480  * // generate pulldown with selection 'Switzerland'  and return html-code
481  * // as second argument the same attributes available to instanciate can be used
482  * print $select->show('CH');
483  * </pre>
484  *
485  * @package HTML
486  */
487 class html_select extends html
488 {
489     protected $tagname = 'select';
490     protected $options = array();
491     protected $allowed = array('name','size','tabindex','autocomplete','multiple','onchange','disabled');
492     
493     /**
494      * Add a new option to this drop-down
495      *
496      * @param mixed Option name or array with option names
497      * @param mixed Option value or array with option values
498      */
499     public function add($names, $values = null)
500     {
501         if (is_array($names)) {
502             foreach ($names as $i => $text) {
503                 $this->options[] = array('text' => $text, 'value' => $values[$i]);
504             }
505         }
506         else {
507             $this->options[] = array('text' => $names, 'value' => $values);
508         }
509     }
510
511
512     /**
513      * Get HTML code for this object
514      *
515      * @param string Value of the selection option
516      * @param array Additional attributes to override
517      * @return string HTML output
518      */
519     public function show($select = array(), $attrib = null)
520     {
521         // overwrite object attributes
522         if (is_array($attrib)) {
523             $this->attrib = array_merge($this->attrib, $attrib);
524         }
525
526         $this->content = "\n";
527         $select = (array)$select;
528         foreach ($this->options as $option) {
529             $attr = array(
530                 'value' => $option['value'],
531                 'selected' => (in_array($option['value'], $select, true) ||
532                   in_array($option['text'], $select, true)) ? 1 : null);
533
534             $this->content .= self::tag('option', $attr, Q($option['text']));
535         }
536         return parent::show();
537     }
538 }
539
540
541 /**
542  * Class to build an HTML table
543  *
544  * @package HTML
545  */
546 class html_table extends html
547 {
548     protected $tagname = 'table';
549     protected $allowed = array('id','class','style','width','summary','cellpadding','cellspacing','border');
550     private $header = array();
551     private $rows = array();
552     private $rowindex = 0;
553     private $colindex = 0;
554
555
556     public function __construct($attrib = array())
557     {
558         $this->attrib = array_merge($attrib, array('summary' => '', 'border' => 0));
559     }
560
561     /**
562      * Add a table cell
563      *
564      * @param array Cell attributes
565      * @param string Cell content
566      */
567     public function add($attr, $cont)
568     {
569         if (is_string($attr)) {
570             $attr = array('class' => $attr);
571         }
572
573         $cell = new stdClass;
574         $cell->attrib = $attr;
575         $cell->content = $cont;
576
577         $this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
578         $this->colindex++;
579
580         if ($this->attrib['cols'] && $this->colindex == $this->attrib['cols']) {
581             $this->add_row();
582         }
583     }
584
585     /**
586      * Add a table header cell
587      *
588      * @param array Cell attributes
589      * @param string Cell content
590      */
591     public function add_header($attr, $cont)
592     {
593         if (is_string($attr))
594             $attr = array('class' => $attr);
595
596         $cell = new stdClass;
597         $cell->attrib = $attr;
598         $cell->content = $cont;
599         $this->header[] = $cell;
600     }
601
602     /**
603      * Jump to next row
604      *
605      * @param array Row attributes
606      */
607     public function add_row($attr = array())
608     {
609         $this->rowindex++;
610         $this->colindex = 0;
611         $this->rows[$this->rowindex] = new stdClass;
612         $this->rows[$this->rowindex]->attrib = $attr;
613         $this->rows[$this->rowindex]->cells = array();
614     }
615
616     /**
617      * Set current row attrib
618      *
619      * @param array Row attributes
620      */
621     public function set_row_attribs($attr = array())
622     {
623         if (is_string($attr))
624             $attr = array('class' => $attr);
625
626         $this->rows[$this->rowindex]->attrib = $attr;
627     }
628
629     /**
630      * Build HTML output of the table data
631      *
632      * @param array Table attributes
633      * @return string The final table HTML code
634      */
635     public function show($attrib = null)
636     {
637         if (is_array($attrib))
638             $this->attrib = array_merge($this->attrib, $attrib);
639         
640         $thead = $tbody = "";
641
642         // include <thead>
643         if (!empty($this->header)) {
644             $rowcontent = '';
645             foreach ($this->header as $c => $col) {
646                 $rowcontent .= self::tag('td', $col->attrib, $col->content);
647             }
648             $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent));
649         }
650
651         foreach ($this->rows as $r => $row) {
652             $rowcontent = '';
653             foreach ($row->cells as $c => $col) {
654                 $rowcontent .= self::tag('td', $col->attrib, $col->content);
655             }
656
657             if ($r < $this->rowindex || count($row->cells)) {
658                 $tbody .= self::tag('tr', $row->attrib, $rowcontent);
659             }
660         }
661
662         if ($this->attrib['rowsonly']) {
663             return $tbody;
664         }
665
666         // add <tbody>
667         $this->content = $thead . self::tag('tbody', null, $tbody);
668
669         unset($this->attrib['cols'], $this->attrib['rowsonly']);
670         return parent::show();
671     }
672     
673     /**
674      * Count number of rows
675      *
676      * @return The number of rows
677      */
678     public function size()
679     {
680       return count($this->rows);
681     }
682 }
683
684 ?>