source: subversion/trunk/roundcubemail/program/include/html.php @ 1806

Last change on this file since 1806 was 1806, checked in by alec, 5 years ago
  • Allow and use spellcheck attribute for input/textarea fields (#1485060)
File size: 17.7 KB
Line 
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-2008, 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 */
28class 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('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 for line breaks
185     *
186     * @return string HTML code
187     * @see html::tag()
188     */
189    public static function br()
190    {
191        return self::tag('br');
192    }
193
194    /**
195     * Create string with attributes
196     *
197     * @param array Associative arry with tag attributes
198     * @param array List of allowed attributes
199     * @return string Valid attribute string
200     */
201    public static function attrib_string($attrib = array(), $allowed = null)
202    {
203        if (empty($attrib)) {
204            return '';
205        }
206
207        $allowed_f = array_flip((array)$allowed);
208        $attrib_arr = array();
209        foreach ($attrib as $key => $value) {
210            // skip size if not numeric
211            if (($key=='size' && !is_numeric($value))) {
212                continue;
213            }
214
215            // ignore "internal" or not allowed attributes
216            if ($key == 'nl' || ($allowed && !isset($allowed_f[$key])) || $value === null) {
217                continue;
218            }
219
220            // skip empty eventhandlers
221            if (preg_match('/^on[a-z]+/', $key) && !$value) {
222                continue;
223            }
224
225            // attributes with no value
226            if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) {
227                if ($value) {
228                    $attrib_arr[] = sprintf('%s="%s"', $key, $key);
229                }
230            }
231            else if ($key=='value') {
232                $attrib_arr[] = sprintf('%s="%s"', $key, Q($value, 'strict', false));
233            }
234            else {
235                $attrib_arr[] = sprintf('%s="%s"', $key, Q($value));
236            }
237        }
238        return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
239    }
240}
241
242/**
243 * Class to create an HTML input field
244 *
245 * @package HTML
246 */
247class html_inputfield extends html
248{
249    protected $tagname = 'input';
250    protected $type = 'text';
251    protected $allowed = array('type','name','value','size','tabindex','autocomplete','checked','onchange','onclick','disabled','readonly','spellcheck');
252
253    public function __construct($attrib = array())
254    {
255        if (is_array($attrib)) {
256            $this->attrib = $attrib;
257        }
258
259        if ($attrib['type']) {
260            $this->type = $attrib['type'];
261        }
262
263        if ($attrib['newline']) {
264            $this->newline = true;
265        }
266    }
267
268    /**
269     * Compose input tag
270     *
271     * @param string Field value
272     * @param array Additional attributes to override
273     * @return string HTML output
274     */
275    public function show($value = null, $attrib = null)
276    {
277        // overwrite object attributes
278        if (is_array($attrib)) {
279            $this->attrib = array_merge($this->attrib, $attrib);
280        }
281
282        // set value attribute
283        if ($value !== null) {
284            $this->attrib['value'] = $value;
285        }
286        // set type
287        $this->attrib['type'] = $this->type;
288        return parent::show();
289    }
290}
291
292/**
293 * Class to create an HTML password field
294 *
295 * @package HTML
296 */
297class html_passwordfield extends html_inputfield
298{
299    protected $type = 'password';
300}
301
302/**
303 * Class to create an hidden HTML input field
304 *
305 * @package HTML
306 */
307
308class html_hiddenfield extends html_inputfield
309{
310    protected $type = 'hidden';
311    protected $fields_arr = array();
312    protected $newline = true;
313
314    /**
315     * Constructor
316     *
317     * @param array Named tag attributes
318     */
319    public function __construct($attrib = null)
320    {
321        if (is_array($attrib)) {
322            $this->add($attrib);
323        }
324    }
325
326    /**
327     * Add a hidden field to this instance
328     *
329     * @param array Named tag attributes
330     */
331    public function add($attrib)
332    {
333        $this->fields_arr[] = $attrib;
334    }
335
336    /**
337     * Create HTML code for the hidden fields
338     *
339     * @return string Final HTML code
340     */
341    public function show()
342    {
343        $out = '';
344        foreach ($this->fields_arr as $attrib) {
345            $out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
346        }
347        return $out;
348    }
349}
350
351/**
352 * Class to create HTML radio buttons
353 *
354 * @package HTML
355 */
356class html_radiobutton extends html_inputfield
357{
358    protected $type = 'radio';
359
360    /**
361     * Get HTML code for this object
362     *
363     * @param string Value of the checked field
364     * @param array Additional attributes to override
365     * @return string HTML output
366     */
367    public function show($value = '', $attrib = null)
368    {
369        // overwrite object attributes
370        if (is_array($attrib)) {
371            $this->attrib = array_merge($this->attrib, $attrib);
372        }
373
374        // set value attribute
375        $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
376
377        return parent::show();
378    }
379}
380
381/**
382 * Class to create HTML checkboxes
383 *
384 * @package HTML
385 */
386class html_checkbox extends html_inputfield
387{
388    protected $type = 'checkbox';
389
390    /**
391     * Get HTML code for this object
392     *
393     * @param string Value of the checked field
394     * @param array Additional attributes to override
395     * @return string HTML output
396     */
397    public function show($value = '', $attrib = null)
398    {
399        // overwrite object attributes
400        if (is_array($attrib)) {
401            $this->attrib = array_merge($this->attrib, $attrib);
402        }
403
404        // set value attribute
405        $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
406
407        return parent::show();
408    }
409}
410
411/**
412 * Class to create an HTML textarea
413 *
414 * @package HTML
415 */
416class html_textarea extends html
417{
418    protected $tagname = 'textarea';
419    protected $allowed = array('name','rows','cols','wrap','tabindex','onchange','disabled','readonly','spellcheck');
420
421    /**
422     * Get HTML code for this object
423     *
424     * @param string Textbox value
425     * @param array Additional attributes to override
426     * @return string HTML output
427     */
428    public function show($value = '', $attrib = null)
429    {
430        // overwrite object attributes
431        if (is_array($attrib)) {
432            $this->attrib = array_merge($this->attrib, $attrib);
433        }
434
435        // take value attribute as content
436        if (empty($value) && !empty($this->attrib['value'])) {
437            $value = $this->attrib['value'];
438        }
439
440        // make shure we don't print the value attribute
441        if (isset($this->attrib['value'])) {
442            unset($this->attrib['value']);
443        }
444
445        if (!empty($value) && !ereg('mce_editor', $this->attrib['class'])) {
446            $value = Q($value, 'strict', false);
447        }
448
449        return self::tag($this->tagname, $this->attrib, $value, array_merge(self::$common_attrib, $this->allowed));
450    }
451}
452
453/**
454 * Builder for HTML drop-down menus
455 * Syntax:<pre>
456 * // create instance. arguments are used to set attributes of select-tag
457 * $select = new html_select(array('name' => 'fieldname'));
458 *
459 * // add one option
460 * $select->add('Switzerland', 'CH');
461 *
462 * // add multiple options
463 * $select->add(array('Switzerland','Germany'), array('CH','DE'));
464 *
465 * // generate pulldown with selection 'Switzerland'  and return html-code
466 * // as second argument the same attributes available to instanciate can be used
467 * print $select->show('CH');
468 * </pre>
469 *
470 * @package HTML
471 */
472class html_select extends html
473{
474    protected $tagname = 'select';
475    protected $options = array();
476    protected $allowed = array('name','size','tabindex','autocomplete','multiple','onchange','disabled');
477   
478    /**
479     * Add a new option to this drop-down
480     *
481     * @param mixed Option name or array with option names
482     * @param mixed Option value or array with option values
483     */
484    public function add($names, $values = null)
485    {
486        if (is_array($names)) {
487            foreach ($names as $i => $text) {
488                $this->options[] = array('text' => $text, 'value' => $values[$i]);
489            }
490        }
491        else {
492            $this->options[] = array('text' => $names, 'value' => $values);
493        }
494    }
495
496
497    /**
498     * Get HTML code for this object
499     *
500     * @param string Value of the selection option
501     * @param array Additional attributes to override
502     * @return string HTML output
503     */
504    public function show($select = array(), $attrib = null)
505    {
506        // overwrite object attributes
507        if (is_array($attrib)) {
508            $this->attrib = array_merge($this->attrib, $attrib);
509        }
510
511        $this->content = "\n";
512        $select = (array)$select;
513        foreach ($this->options as $option) {
514            $attr = array(
515                'value' => $option['value'],
516                'selected' => (in_array($option['value'], $select, true) ||
517                  in_array($option['text'], $select, true)) ? 1 : null);
518
519            $this->content .= self::tag('option', $attr, Q($option['text']));
520        }
521        return parent::show();
522    }
523}
524
525
526/**
527 * Class to build an HTML table
528 *
529 * @package HTML
530 */
531class html_table extends html
532{
533    protected $tagname = 'table';
534    protected $allowed = array('id','class','style','width','summary','cellpadding','cellspacing','border');
535    private $header = array();
536    private $rows = array();
537    private $rowindex = 0;
538    private $colindex = 0;
539
540
541    public function __construct($attrib = array())
542    {
543        $this->attrib = array_merge($attrib, array('summary' => '', 'border' => 0));
544    }
545
546    /**
547     * Add a table cell
548     *
549     * @param array Cell attributes
550     * @param string Cell content
551     */
552    public function add($attr, $cont)
553    {
554        if (is_string($attr)) {
555            $attr = array('class' => $attr);
556        }
557
558        $cell = new stdClass;
559        $cell->attrib = $attr;
560        $cell->content = $cont;
561
562        $this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
563        $this->colindex++;
564
565        if ($this->attrib['cols'] && $this->colindex == $this->attrib['cols']) {
566            $this->add_row();
567        }
568    }
569
570    /**
571     * Add a table header cell
572     *
573     * @param array Cell attributes
574     * @param string Cell content
575     */
576    public function add_header($attr, $cont)
577    {
578        if (is_string($attr))
579        $attr = array('class' => $attr);
580
581        $cell = new stdClass;
582        $cell->attrib = $attr;
583        $cell->content = $cont;
584        $this->header[] = $cell;
585    }
586
587    /**
588     * Jump to next row
589     *
590     * @param array Row attributes
591     */
592    public function add_row($attr = array())
593    {
594        $this->rowindex++;
595        $this->colindex = 0;
596        $this->rows[$this->rowindex] = new stdClass;
597        $this->rows[$this->rowindex]->attrib = $attr;
598        $this->rows[$this->rowindex]->cells = array();
599    }
600
601
602    /**
603     * Build HTML output of the table data
604     *
605     * @param array Table attributes
606     * @return string The final table HTML code
607     */
608    public function show($attrib = null)
609    {
610        if (is_array($attrib))
611            $this->attrib = array_merge($this->attrib, $attrib);
612       
613        $thead = $tbody = "";
614
615        // include <thead>
616        if (!empty($this->header)) {
617            $rowcontent = '';
618            foreach ($this->header as $c => $col) {
619                $rowcontent .= self::tag('td', $col->attrib, $col->content);
620            }
621            $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent));
622        }
623
624        foreach ($this->rows as $r => $row) {
625            $rowcontent = '';
626            foreach ($row->cells as $c => $col) {
627                $rowcontent .= self::tag('td', $col->attrib, $col->content);
628            }
629
630            if ($r < $this->rowindex || count($row->cells)) {
631                $tbody .= self::tag('tr', $row->attrib, $rowcontent);
632            }
633        }
634
635        if ($this->attrib['rowsonly']) {
636            return $tbody;
637        }
638
639        // add <tbody>
640        $this->content = $thead . self::tag('tbody', null, $tbody);
641
642        unset($this->attrib['cols'], $this->attrib['rowsonly']);
643        return parent::show();
644    }
645}
646
647?>
Note: See TracBrowser for help on using the repository browser.