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