<?php

namespace WPForms\Vendor;

/**
 * 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) && $thead === \false && $tfoot === \false) {
            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) {
                if (!isset($node->name)) {
                    continue;
                }
                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
