diff options
Diffstat (limited to 'vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php')
-rw-r--r-- | vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php new file mode 100644 index 000000000..cb6b3e6cd --- /dev/null +++ b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php @@ -0,0 +1,224 @@ +<?php + +/** + * Definition for tables. The general idea is to extract out all of the + * essential bits, and then reconstruct it later. + * + * This is a bit confusing, because the DTDs and the W3C + * validators seem to disagree on the appropriate definition. The + * DTD claims: + * + * (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+) + * + * But actually, the HTML4 spec then has this to say: + * + * The TBODY start tag is always required except when the table + * contains only one table body and no table head or foot sections. + * The TBODY end tag may always be safely omitted. + * + * So the DTD is kind of wrong. The validator is, unfortunately, kind + * of on crack. + * + * The definition changed again in XHTML1.1; and in my opinion, this + * formulation makes the most sense. + * + * caption?, ( col* | colgroup* ), (( thead?, tfoot?, tbody+ ) | ( tr+ )) + * + * Essentially, we have two modes: thead/tfoot/tbody mode, and tr mode. + * If we encounter a thead, tfoot or tbody, we are placed in the former + * mode, and we *must* wrap any stray tr segments with a tbody. But if + * we don't run into any of them, just have tr tags is OK. + */ +class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef +{ + /** + * @type bool + */ + public $allow_empty = false; + + /** + * @type string + */ + public $type = 'table'; + + /** + * @type array + */ + public $elements = array( + 'tr' => true, + 'tbody' => true, + 'thead' => true, + 'tfoot' => true, + 'caption' => true, + 'colgroup' => true, + 'col' => true + ); + + public function __construct() + { + } + + /** + * @param array $children + * @param HTMLPurifier_Config $config + * @param HTMLPurifier_Context $context + * @return array + */ + public function validateChildren($children, $config, $context) + { + if (empty($children)) { + return false; + } + + // only one of these elements is allowed in a table + $caption = false; + $thead = false; + $tfoot = false; + + // whitespace + $initial_ws = array(); + $after_caption_ws = array(); + $after_thead_ws = array(); + $after_tfoot_ws = array(); + + // as many of these as you want + $cols = array(); + $content = array(); + + $tbody_mode = false; // if true, then we need to wrap any stray + // <tr>s with a <tbody>. + + $ws_accum =& $initial_ws; + + foreach ($children as $node) { + if ($node instanceof HTMLPurifier_Node_Comment) { + $ws_accum[] = $node; + continue; + } + switch ($node->name) { + case 'tbody': + $tbody_mode = true; + // fall through + case 'tr': + $content[] = $node; + $ws_accum =& $content; + break; + case 'caption': + // there can only be one caption! + if ($caption !== false) break; + $caption = $node; + $ws_accum =& $after_caption_ws; + break; + case 'thead': + $tbody_mode = true; + // XXX This breaks rendering properties with + // Firefox, which never floats a <thead> to + // the top. Ever. (Our scheme will float the + // first <thead> to the top.) So maybe + // <thead>s that are not first should be + // turned into <tbody>? Very tricky, indeed. + if ($thead === false) { + $thead = $node; + $ws_accum =& $after_thead_ws; + } else { + // Oops, there's a second one! What + // should we do? Current behavior is to + // transmutate the first and last entries into + // tbody tags, and then put into content. + // Maybe a better idea is to *attach + // it* to the existing thead or tfoot? + // We don't do this, because Firefox + // doesn't float an extra tfoot to the + // bottom like it does for the first one. + $node->name = 'tbody'; + $content[] = $node; + $ws_accum =& $content; + } + break; + case 'tfoot': + // see above for some aveats + $tbody_mode = true; + if ($tfoot === false) { + $tfoot = $node; + $ws_accum =& $after_tfoot_ws; + } else { + $node->name = 'tbody'; + $content[] = $node; + $ws_accum =& $content; + } + break; + case 'colgroup': + case 'col': + $cols[] = $node; + $ws_accum =& $cols; + break; + case '#PCDATA': + // How is whitespace handled? We treat is as sticky to + // the *end* of the previous element. So all of the + // nonsense we have worked on is to keep things + // together. + if (!empty($node->is_whitespace)) { + $ws_accum[] = $node; + } + break; + } + } + + if (empty($content)) { + return false; + } + + $ret = $initial_ws; + if ($caption !== false) { + $ret[] = $caption; + $ret = array_merge($ret, $after_caption_ws); + } + if ($cols !== false) { + $ret = array_merge($ret, $cols); + } + if ($thead !== false) { + $ret[] = $thead; + $ret = array_merge($ret, $after_thead_ws); + } + if ($tfoot !== false) { + $ret[] = $tfoot; + $ret = array_merge($ret, $after_tfoot_ws); + } + + if ($tbody_mode) { + // we have to shuffle tr into tbody + $current_tr_tbody = null; + + foreach($content as $node) { + switch ($node->name) { + case 'tbody': + $current_tr_tbody = null; + $ret[] = $node; + break; + case 'tr': + if ($current_tr_tbody === null) { + $current_tr_tbody = new HTMLPurifier_Node_Element('tbody'); + $ret[] = $current_tr_tbody; + } + $current_tr_tbody->children[] = $node; + break; + case '#PCDATA': + //assert($node->is_whitespace); + if ($current_tr_tbody === null) { + $ret[] = $node; + } else { + $current_tr_tbody->children[] = $node; + } + break; + } + } + } else { + $ret = array_merge($ret, $content); + } + + return $ret; + + } +} + +// vim: et sw=4 sts=4 |