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