Mercurial > hg > rc2
comparison program/lib/Roundcube/html.php @ 0:4681f974d28b
vanilla 1.3.3 distro, I hope
author | Charlie Root |
---|---|
date | Thu, 04 Jan 2018 15:52:31 -0500 |
parents | |
children | aff04b06b685 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4681f974d28b |
---|---|
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 } |