Version 1
[yaffs-website] / vendor / ezyang / htmlpurifier / library / HTMLPurifier / ChildDef / Table.php
diff --git a/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php b/vendor/ezyang/htmlpurifier/library/HTMLPurifier/ChildDef/Table.php
new file mode 100644 (file)
index 0000000..cb6b3e6
--- /dev/null
@@ -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