/*
 Copyright (C) 2015-2017 Alexander Borisov
 
 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.
 
 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 Lesser General Public License for more details.
 
 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
 Author: lex.borisov@gmail.com (Alexander Borisov)
*/

#include "myhtml/rules.h"

void myhtml_insertion_fix_emit_for_text_begin_ws(myhtml_token_t* token, myhtml_token_node_t* node)
{
    myhtml_token_node_wait_for_done(token, node);
    mycore_string_crop_whitespace_from_begin(&node->str);
}

myhtml_token_node_t * myhtml_insertion_fix_split_for_text_begin_ws(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    myhtml_token_node_wait_for_done(tree->token, token);
    size_t len = mycore_string_whitespace_from_begin(&token->str);
    
    if(len == 0)
        return NULL;
    
    // create new ws token and insert
    myhtml_token_node_t* new_token = myhtml_token_node_create(tree->token, tree->mcasync_rules_token_id);
    
    if(new_token == NULL)
        return NULL;
    
    mycore_string_init(tree->mchar, tree->mchar_node_id, &new_token->str, (len + 2));
    
    mycore_string_append(&new_token->str, token->str.data, len);
    
    new_token->type |= MyHTML_TOKEN_TYPE_DONE;
    
    // and cut ws for original
    token->str.data    = mchar_async_crop_first_chars_without_cache(token->str.data, len);
    token->str.length -= len;
    
    return new_token;
}

void myhtml_insertion_fix_for_null_char_drop_all(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    myhtml_token_node_wait_for_done(tree->token, token);
    
    mycore_string_t *str = &token->str;
    size_t len = str->length;
    size_t offset = 0;
    
    for (size_t i = 0; i < len; ++i)
    {
        if (str->data[i] == '\0')
        {
            size_t next_non_null = i;
            while ((next_non_null < len) && str->data[next_non_null] == '\0') {++next_non_null;}
            
            str->length = str->length - (next_non_null - i);
            
            size_t next_null = next_non_null;
            while ((next_null < len) && str->data[next_null] != '\0') {++next_null;}
            
            memmove(&str->data[(i - offset)], &str->data[next_non_null], (next_null - next_non_null));
            
            i = next_null - 1;
            
            offset++;
        }
    }
}

bool myhtml_insertion_mode_initial(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    switch (token->tag_id)
    {
        case MyHTML_TAG__TEXT:
        {
            if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:INFO */
                return false;
            }
            
            myhtml_insertion_fix_emit_for_text_begin_ws(tree->token, token);
            
            // default, other token
            tree->compat_mode = MyHTML_TREE_COMPAT_MODE_QUIRKS;
            tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HTML;
            break;
        }
            
        case MyHTML_TAG__COMMENT:
        {
            myhtml_tree_node_insert_comment(tree, token, tree->document);
            return false;
        }
            
        case MyHTML_TAG__DOCTYPE:
        {
            myhtml_token_node_wait_for_done(tree->token, token);
            
            myhtml_token_release_and_check_doctype_attributes(tree->token, token, &tree->doctype);
            
            if((tree->parse_flags & MyHTML_TREE_PARSE_FLAGS_WITHOUT_DOCTYPE_IN_TREE) == 0)
                myhtml_tree_node_insert_doctype(tree, token);
            
            // fix for tokenizer
            if(tree->doctype.is_html == false &&
               (tree->doctype.attr_public == NULL ||
               tree->doctype.attr_system == NULL))
            {
                tree->compat_mode = MyHTML_TREE_COMPAT_MODE_QUIRKS;
            }
            
            tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HTML;
            return false;
        }
            
        default:
            tree->compat_mode = MyHTML_TREE_COMPAT_MODE_QUIRKS;
            tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HTML;
            break;
    }
    
    return true;
}

bool myhtml_insertion_mode_before_html(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_BR:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_HEAD:
            case MyHTML_TAG_BODY:
            {
                myhtml_tree_node_insert_root(tree, NULL, MyHTML_NAMESPACE_HTML);
                
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_HTML NS:MyHTML_NAMESPACE_HTML */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HEAD;
                return true;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:WARNING */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__DOCTYPE: {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:WARNING */
                break;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, tree->document);
                break;
            }
                
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:INFO */
                    break;
                }
                
                myhtml_insertion_fix_emit_for_text_begin_ws(tree->token, token);
                
                // default, other token
                myhtml_tree_node_insert_root(tree, NULL, MyHTML_NAMESPACE_HTML);
                tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HEAD;
                return true;
            }
                
            case MyHTML_TAG_HTML:
            {
                myhtml_tree_node_insert_root(tree, token, MyHTML_NAMESPACE_HTML);
                tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HEAD;
                break;
            }
            
            default:
            {
                myhtml_tree_node_insert_root(tree, NULL, MyHTML_NAMESPACE_HTML);
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_HTML NS:MyHTML_NAMESPACE_HTML */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_BEFORE_HEAD;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_before_head(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_BR:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_HEAD:
            case MyHTML_TAG_BODY:
            {
                tree->node_head = myhtml_tree_node_insert(tree, MyHTML_TAG_HEAD, MyHTML_NAMESPACE_HTML);
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_HEAD NS:MyHTML_NAMESPACE_HTML */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:INFO */
                    break;
                }
                
                myhtml_insertion_fix_emit_for_text_begin_ws(tree->token, token);
                
                // default, other token
                tree->node_head = myhtml_tree_node_insert(tree, MyHTML_TAG_HEAD, MyHTML_NAMESPACE_HTML);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_HTML:
            {
                return myhtml_insertion_mode_in_body(tree, token);
            }
                
            case MyHTML_TAG_HEAD:
            {
                tree->node_head = myhtml_tree_node_insert_html_element(tree, token);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                break;
            }
                
            default:
            {
                tree->node_head = myhtml_tree_node_insert(tree, MyHTML_TAG_HEAD, MyHTML_NAMESPACE_HTML);
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_HEAD NS:MyHTML_NAMESPACE_HTML */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_head(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_HEAD:
            {
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_HEAD;
                break;
            }
                
            case MyHTML_TAG_BR:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_BODY:
            {
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_HEAD;
                return true;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                if(myhtml_tree_open_elements_find_by_tag_idx_reverse(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL) == NULL)
                {
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:WARNING */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_ANY NEED_TAG_ID:MyHTML_TAG_TEMPLATE NEED_NS:MyHTML_NAMESPACE_HTML */
                    
                    break;
                }
                
                // oh God...
                myhtml_tree_generate_all_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(current_node && current_node->tag_id != MyHTML_TAG_TEMPLATE) {
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED_CLOSE_BEFORE LEVEL:WARNING */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_TEMPLATE NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                break;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:WARNING */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                {
                    myhtml_tree_node_insert_text(tree, token);
                    break;
                }
                
                myhtml_token_node_t* new_token = myhtml_insertion_fix_split_for_text_begin_ws(tree, token);
                if(new_token)
                    myhtml_tree_node_insert_text(tree, new_token);
                
                // default, other token
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_HEAD;
                return true;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_HTML:
            {
                return myhtml_insertion_mode_in_body(tree, token);
            }
                
            case MyHTML_TAG_BASE:
            case MyHTML_TAG_BASEFONT:
            case MyHTML_TAG_BGSOUND:
            case MyHTML_TAG_LINK:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                break;
            }
                
            case MyHTML_TAG_META:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                // if the element has an http-equiv attribute
                break;
            }
                
            case MyHTML_TAG_TITLE:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->orig_insert_mode = tree->insert_mode;
                tree->insert_mode = MyHTML_INSERTION_MODE_TEXT;
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RCDATA;
                
                break;
            }
                
            case MyHTML_TAG_NOSCRIPT:
            {
                if(tree->flags & MyHTML_TREE_FLAGS_SCRIPT) {
                    myhtml_tree_node_insert_html_element(tree, token);
                    
                    tree->orig_insert_mode = tree->insert_mode;
                    tree->insert_mode = MyHTML_INSERTION_MODE_TEXT;
                    tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                }
                else {
                    myhtml_tree_node_insert_html_element(tree, token);
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD_NOSCRIPT;
                }
                
                break;
            }
                
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG_NOFRAMES:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->orig_insert_mode = tree->insert_mode;
                tree->insert_mode = MyHTML_INSERTION_MODE_TEXT;
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                
                break;
            }
                
            case MyHTML_TAG_SCRIPT:
            {
                // state 1
                enum myhtml_tree_insertion_mode insert_mode;
                myhtml_tree_node_t* adjusted_location = myhtml_tree_appropriate_place_inserting(tree, NULL, &insert_mode);
                
                // state 2
                myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
                
                node->tag_id      = MyHTML_TAG_SCRIPT;
                node->token        = token;
                node->ns = MyHTML_NAMESPACE_HTML;
                node->flags        = MyHTML_TREE_NODE_PARSER_INSERTED|MyHTML_TREE_NODE_BLOCKING;
                
                myhtml_tree_node_insert_by_mode(adjusted_location, node, insert_mode);
                myhtml_tree_open_elements_append(tree, node);
                
                tree->orig_insert_mode = tree->insert_mode;
                tree->insert_mode = MyHTML_INSERTION_MODE_TEXT;
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_SCRIPT_DATA;
                
                break;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append(tree, tree->myhtml->marker); // set marker
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TEMPLATE;
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_TEMPLATE);
                
                break;
            }
                
            case MyHTML_TAG_HEAD: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:WARNING */
                break;
            }
                
            default:
            {
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_HEAD;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_head_noscript(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_NOSCRIPT:
            {
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                break;
            }
                
            case MyHTML_TAG_BR:
            {
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_HTML:
            {
                return myhtml_insertion_mode_in_body(tree, token);
            }
                
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                    return myhtml_insertion_mode_in_head(tree, token);
                
                // default, other token
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
                
            case MyHTML_TAG_BASEFONT:
            case MyHTML_TAG_BGSOUND:
            case MyHTML_TAG_LINK:
            case MyHTML_TAG_META:
            case MyHTML_TAG_NOFRAMES:
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG__COMMENT:
                return myhtml_insertion_mode_in_head(tree, token);
                
            case MyHTML_TAG_HEAD:
            case MyHTML_TAG_NOSCRIPT: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:WARNING */
                break;
            }
                
            default:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_after_head(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_BR:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_BODY:
            {
                tree->node_body = myhtml_tree_node_insert(tree, MyHTML_TAG_BODY, MyHTML_NAMESPACE_HTML);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_BODY NS:MyHTML_NAMESPACE_HTML */
                return true;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                {
                    myhtml_tree_node_insert_text(tree, token);
                    break;
                }
                
                myhtml_token_node_t* new_token = myhtml_insertion_fix_split_for_text_begin_ws(tree, token);
                if(new_token)
                    myhtml_tree_node_insert_text(tree, new_token);
                
                // default, other token
                tree->node_body = myhtml_tree_node_insert(tree, MyHTML_TAG_BODY, MyHTML_NAMESPACE_HTML);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
                
            case MyHTML_TAG__COMMENT:
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_HTML:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG_BODY:
            {
                tree->node_body = myhtml_tree_node_insert_html_element(tree, token);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                break;
            }
                
            case MyHTML_TAG_FRAMESET:
                myhtml_tree_node_insert_html_element(tree, token);
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_FRAMESET;
                break;
                
            case MyHTML_TAG_BASE:
            case MyHTML_TAG_BASEFONT:
            case MyHTML_TAG_BGSOUND:
            case MyHTML_TAG_LINK:
            case MyHTML_TAG_META:
            case MyHTML_TAG_NOFRAMES:
            case MyHTML_TAG_SCRIPT:
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG_TEMPLATE:
            case MyHTML_TAG_TITLE:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_open_elements_append(tree, tree->node_head);
                myhtml_insertion_mode_in_head(tree, token);
                myhtml_tree_open_elements_remove(tree, tree->node_head);
            }
                
            case MyHTML_TAG_HEAD: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:WARNING */
                break;
            }
                
            default:
            {
                tree->node_body = myhtml_tree_node_insert(tree, MyHTML_TAG_BODY, MyHTML_NAMESPACE_HTML);
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_MISSING_NEED LEVEL:INFO TAG_ID:MyHTML_TAG_BODY NS:MyHTML_NAMESPACE_HTML */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_body_other_end_tag(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    // step 1
    size_t i = tree->open_elements->length;
    while(i) {
        i--;
        
        myhtml_tree_node_t* node = tree->open_elements->list[i];
        
        // step 2
        if(node->tag_id == token->tag_id && node->ns == MyHTML_NAMESPACE_HTML) {
            myhtml_tree_generate_implied_end_tags(tree, token->tag_id, MyHTML_NAMESPACE_HTML);
            
            myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
            if(current_node->tag_id != node->tag_id) {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:node->tag_id NEED_NS:node->ns */
            }
            
            myhtml_tree_open_elements_pop_until_by_node(tree, node, false);
            
            return false;
        }
        
        const myhtml_tag_context_t *tag_ctx = myhtml_tag_get_by_id(tree->tags, node->tag_id);
        if(tag_ctx->cats[ node->ns ] & MyHTML_TAG_CATEGORIES_SPECIAL) {
            // parse error
            /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
            break;
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_body(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            case MyHTML_TAG_BODY:
            {
                myhtml_tree_node_t* body_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_BODY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE);
                
                if(body_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                for (size_t i = 0; i < tree->open_elements->length; i++) {
                    switch (tree->open_elements->list[i]->tag_id) {
                        case MyHTML_TAG_DD:
                        case MyHTML_TAG_DT:
                        case MyHTML_TAG_LI:
                        case MyHTML_TAG_MENUITEM:
                        case MyHTML_TAG_OPTGROUP:
                        case MyHTML_TAG_OPTION:
                        case MyHTML_TAG_P:
                        case MyHTML_TAG_RB:
                        case MyHTML_TAG_RP:
                        case MyHTML_TAG_RT:
                        case MyHTML_TAG_RTC:
                        case MyHTML_TAG_TBODY:
                        case MyHTML_TAG_TD:
                        case MyHTML_TAG_TFOOT:
                        case MyHTML_TAG_TH:
                        case MyHTML_TAG_THEAD:
                        case MyHTML_TAG_TR:
                        case MyHTML_TAG_BODY:
                        case MyHTML_TAG_HTML:
                            // set parse error
                            break;
                            
                        default:
                            break;
                    }
                }
                
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_BODY;
                break;
            }
                
            case MyHTML_TAG_HTML:
            {
                myhtml_tree_node_t* body_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_BODY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE);
                
                if(body_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                for (size_t i = 0; i < tree->open_elements->length; i++) {
                    switch (tree->open_elements->list[i]->tag_id) {
                        case MyHTML_TAG_DD:
                        case MyHTML_TAG_DT:
                        case MyHTML_TAG_LI:
                        case MyHTML_TAG_MENUITEM:
                        case MyHTML_TAG_OPTGROUP:
                        case MyHTML_TAG_OPTION:
                        case MyHTML_TAG_P:
                        case MyHTML_TAG_RB:
                        case MyHTML_TAG_RP:
                        case MyHTML_TAG_RT:
                        case MyHTML_TAG_RTC:
                        case MyHTML_TAG_TBODY:
                        case MyHTML_TAG_TD:
                        case MyHTML_TAG_TFOOT:
                        case MyHTML_TAG_TH:
                        case MyHTML_TAG_THEAD:
                        case MyHTML_TAG_TR:
                        case MyHTML_TAG_BODY:
                        case MyHTML_TAG_HTML:
                            // set parse error
                            break;
                            
                        default:
                            break;
                    }
                }
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_BODY;
                
                return true;
            }
                
            case MyHTML_TAG_ADDRESS:
            case MyHTML_TAG_ARTICLE:
            case MyHTML_TAG_ASIDE:
            case MyHTML_TAG_BLOCKQUOTE:
            case MyHTML_TAG_BUTTON:
            case MyHTML_TAG_CENTER:
            case MyHTML_TAG_DETAILS:
            case MyHTML_TAG_DIALOG:
            case MyHTML_TAG_DIR:
            case MyHTML_TAG_DIV:
            case MyHTML_TAG_DL:
            case MyHTML_TAG_FIELDSET:
            case MyHTML_TAG_FIGCAPTION:
            case MyHTML_TAG_FIGURE:
            case MyHTML_TAG_FOOTER:
            case MyHTML_TAG_HEADER:
            case MyHTML_TAG_HGROUP:
            case MyHTML_TAG_LISTING:
            case MyHTML_TAG_MAIN:
            case MyHTML_TAG_MENU:
            case MyHTML_TAG_NAV:
            case MyHTML_TAG_OL:
            case MyHTML_TAG_PRE:
            case MyHTML_TAG_SECTION:
            case MyHTML_TAG_SUMMARY:
            case MyHTML_TAG_UL:
            {
                if(myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                // step 1
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                // step 2
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, token->tag_id) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                // step 3
                myhtml_tree_open_elements_pop_until(tree, token->tag_id, MyHTML_NAMESPACE_HTML, false);
                break;
            }
                
            case MyHTML_TAG_FORM:
            {
                myhtml_tree_node_t* template_node = myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL);
                
                if(template_node == NULL)
                {
                    // step 1
                    myhtml_tree_node_t* node = tree->node_form;
                    
                    // step 2
                    tree->node_form = NULL;
                    
                    // step 3
                    if(node == NULL || myhtml_tree_element_in_scope_by_node(node, MyHTML_TAG_CATEGORIES_SCOPE) == false) {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                        
                        break;
                    }
                    
                    // step 4
                    myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                    
                    // step 5
                    myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                    if(current_node != node) {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:node->tag_id NEED_NS:node->ns */
                    }
                    
                    // step 6
                    myhtml_tree_open_elements_remove(tree, node);
                }
                else {
                    // step 1
                    myhtml_tree_node_t* form_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_FORM, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE);
                    
                    if(form_node == NULL) {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                        
                        break;
                    }
                    
                    // step 2
                    myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                    
                    // step 3
                    myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                    if(myhtml_is_html_node(current_node, MyHTML_TAG_FORM) == false) {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_FORM NEED_NS:MyHTML_NAMESPACE_HTML */
                    }
                    
                    // step 4
                    myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_FORM, MyHTML_NAMESPACE_HTML, false);
                }
                
                break;
            }
                
            case MyHTML_TAG_P:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON) == NULL) {
                    // parse error
                    myhtml_tree_node_insert(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML);
                }
                
                myhtml_tree_tags_close_p(tree, token);
                break;
            }
                
            case MyHTML_TAG_LI:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_LI, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_LIST_ITEM) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                // step 1
                myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_LI, MyHTML_NAMESPACE_HTML);
                
                // step 2
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, MyHTML_TAG_LI) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_LI NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                // step 3
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_LI, MyHTML_NAMESPACE_HTML, false);
                
                break;
            }
               
            case MyHTML_TAG_DT:
            case MyHTML_TAG_DD:
            {
                if(myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                // step 1
                myhtml_tree_generate_implied_end_tags(tree, token->tag_id, MyHTML_NAMESPACE_HTML);
                
                // step 2
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, token->tag_id) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                // step 3
                myhtml_tree_open_elements_pop_until(tree, token->tag_id, MyHTML_NAMESPACE_HTML, false);
                
                break;
            }
                
            case MyHTML_TAG_H1:
            case MyHTML_TAG_H2:
            case MyHTML_TAG_H3:
            case MyHTML_TAG_H4:
            case MyHTML_TAG_H5:
            case MyHTML_TAG_H6:
            {
                myhtml_tree_node_t** list = tree->open_elements->list;
                
                myhtml_tree_node_t* node = NULL;
                size_t i = tree->open_elements->length;
                while(i) {
                    i--;
                    
                    const myhtml_tag_context_t *tag_ctx = myhtml_tag_get_by_id(tree->tags, list[i]->tag_id);
                    
                    if((list[i]->tag_id == MyHTML_TAG_H1 ||
                       list[i]->tag_id == MyHTML_TAG_H2  ||
                       list[i]->tag_id == MyHTML_TAG_H3  ||
                       list[i]->tag_id == MyHTML_TAG_H4  ||
                       list[i]->tag_id == MyHTML_TAG_H5  ||
                       list[i]->tag_id == MyHTML_TAG_H6) &&
                       list[i]->ns == MyHTML_NAMESPACE_HTML) {
                        node = list[i];
                        break;
                    }
                    else if(tag_ctx->cats[list[i]->ns] & MyHTML_TAG_CATEGORIES_SCOPE)
                        break;
                }
                
                if(node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                // step 1
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                // step 2
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, token->tag_id) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                // step 3
                while(tree->open_elements->length) {
                    tree->open_elements->length--;
                    
                    if((list[tree->open_elements->length]->tag_id == MyHTML_TAG_H1 ||
                       list[tree->open_elements->length]->tag_id == MyHTML_TAG_H2 ||
                       list[tree->open_elements->length]->tag_id == MyHTML_TAG_H3 ||
                       list[tree->open_elements->length]->tag_id == MyHTML_TAG_H4 ||
                       list[tree->open_elements->length]->tag_id == MyHTML_TAG_H5 ||
                       list[tree->open_elements->length]->tag_id == MyHTML_TAG_H6) &&
                       list[tree->open_elements->length]->ns == MyHTML_NAMESPACE_HTML)
                    {
                        break;
                    }
                }
                
                break;
            }
                
            case MyHTML_TAG_A:
            case MyHTML_TAG_B:
            case MyHTML_TAG_BIG:
            case MyHTML_TAG_CODE:
            case MyHTML_TAG_EM:
            case MyHTML_TAG_FONT:
            case MyHTML_TAG_I:
            case MyHTML_TAG_NOBR:
            case MyHTML_TAG_S:
            case MyHTML_TAG_SMALL:
            case MyHTML_TAG_STRIKE:
            case MyHTML_TAG_STRONG:
            case MyHTML_TAG_TT:
            case MyHTML_TAG_U:
            {
                myhtml_tree_adoption_agency_algorithm(tree, token, token->tag_id);
                    //myhtml_insertion_mode_in_body_other_end_tag(tree, token);
                
                break;
            }
                
            case MyHTML_TAG_APPLET:
            case MyHTML_TAG_MARQUEE:
            case MyHTML_TAG_OBJECT:
            {
                if(myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                // step 1
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                // step 2
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, token->tag_id) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                // step 3
                myhtml_tree_open_elements_pop_until(tree, token->tag_id, MyHTML_NAMESPACE_HTML, false);
                
                // step 4
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                
                break;
            }
                
            case MyHTML_TAG_BR:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES CONVERT STATUS:ELEMENT_BAD LEVEL:ERROR FROM_TAG_ID:MyHTML_TAG_BR FROM_NS:MyHTML_NAMESPACE_HTML FROM_TYPE:MyHTML_TOKEN_TYPE_CLOSE TO_TAG_ID:MyHTML_TAG_BR TO_NS:MyHTML_NAMESPACE_HTML TO_TYPE:MyHTML_TOKEN_TYPE_OPEN */
                
                if(token->attr_first) {
                    token->attr_first = NULL;
                }
                
                if(token->attr_last) {
                    token->attr_last = NULL;
                }
                
                myhtml_tree_active_formatting_reconstruction(tree);
                
                token->type = MyHTML_TOKEN_TYPE_OPEN;
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                break;
            }
                
            default:
            {
                return myhtml_insertion_mode_in_body_other_end_tag(tree, token);
            }
        }
    }
    // open elements
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:NULL_CHAR ACTION:IGNORE LEVEL:ERROR */
                    
                    myhtml_insertion_fix_for_null_char_drop_all(tree, token);
                    
                    if(token->str.length) {
                        myhtml_tree_active_formatting_reconstruction(tree);
                        myhtml_tree_node_insert_text(tree, token);
                        
                        if((token->type & MyHTML_TOKEN_TYPE_WHITESPACE) == 0)
                            tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                    }
                }
                else {
                    myhtml_tree_active_formatting_reconstruction(tree);
                    myhtml_tree_node_insert_text(tree, token);
                    
                    if((token->type & MyHTML_TOKEN_TYPE_WHITESPACE) == 0)
                        tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                }
                
                break;
            }
                
            case MyHTML_TAG__COMMENT:
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:WARNING */
                break;
            }
                
            case MyHTML_TAG_HTML:
            {
                if(myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL)) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:WARNING */
                    break;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:WARNING */
                
                if(tree->open_elements->length > 0) {
                    myhtml_tree_node_t* top_node = tree->open_elements->list[0];
                    
                    if(top_node->token) {
                        myhtml_token_node_wait_for_done(tree->token, token);
                        myhtml_token_node_wait_for_done(tree->token, top_node->token);
                        myhtml_token_node_attr_copy_with_check(tree->token, token, top_node->token, tree->mcasync_rules_attr_id);
                    }
                    else {
                        top_node->token = token;
                    }
                }
                
                break;
            }
                
            case MyHTML_TAG_BASE:
            case MyHTML_TAG_BASEFONT:
            case MyHTML_TAG_BGSOUND:
            case MyHTML_TAG_LINK:
            case MyHTML_TAG_META:
            case MyHTML_TAG_NOFRAMES:
            case MyHTML_TAG_SCRIPT:
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG_TEMPLATE:
            case MyHTML_TAG_TITLE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            case MyHTML_TAG_BODY:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:WARNING */
                
                if(tree->open_elements->length > 1)
                {
                    if(!(tree->open_elements->list[1]->tag_id == MyHTML_TAG_BODY &&
                         tree->open_elements->list[1]->ns == MyHTML_NAMESPACE_HTML) ||
                       myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL))
                    {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:WARNING */
                        
                        break;
                    }
                }
                else
                    break;
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                if(tree->open_elements->length > 1) {
                    myhtml_tree_node_t* top_node = tree->open_elements->list[1];
                    
                    if(top_node->token) {
                        myhtml_token_node_wait_for_done(tree->token, token);
                        myhtml_token_node_wait_for_done(tree->token, top_node->token);
                        myhtml_token_node_attr_copy_with_check(tree->token, token, top_node->token, tree->mcasync_rules_attr_id);
                    }
                    else {
                        top_node->token = token;
                    }
                }
                
                break;
            }
                
            case MyHTML_TAG_FRAMESET:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                if(tree->open_elements->length > 1)
                {
                    if(!(tree->open_elements->list[1]->tag_id == MyHTML_TAG_BODY &&
                         tree->open_elements->list[1]->ns == MyHTML_NAMESPACE_HTML))
                    {
                        // parse error
                        /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                        
                        break;
                    }
                }
                else
                    break;
                
                if((tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK) == 0) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_node_t* node = tree->open_elements->list[1];
                
                myhtml_tree_node_remove(node);
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_HTML, MyHTML_NAMESPACE_HTML, true);
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_FRAMESET;
                break;
            }
                
            case MyHTML_TAG__END_OF_FILE:
            {
                if(tree->template_insertion->length)
                    return myhtml_insertion_mode_in_template(tree, token);
                
                myhtml_tree_node_t** list = tree->open_elements->list;
                for(size_t i = 0; i < tree->open_elements->length; i++) {
                    if(list[i]->tag_id != MyHTML_TAG_DD       && list[i]->tag_id != MyHTML_TAG_DT       &&
                       list[i]->tag_id != MyHTML_TAG_LI       && list[i]->tag_id != MyHTML_TAG_MENUITEM &&
                       list[i]->tag_id != MyHTML_TAG_OPTGROUP && list[i]->tag_id != MyHTML_TAG_OPTION   &&
                       list[i]->tag_id != MyHTML_TAG_P        && list[i]->tag_id != MyHTML_TAG_RB       &&
                       list[i]->tag_id != MyHTML_TAG_RP       && list[i]->tag_id != MyHTML_TAG_RT       &&
                       list[i]->tag_id != MyHTML_TAG_RTC      && list[i]->tag_id != MyHTML_TAG_TBODY    &&
                       list[i]->tag_id != MyHTML_TAG_TD       && list[i]->tag_id != MyHTML_TAG_TFOOT    &&
                       list[i]->tag_id != MyHTML_TAG_TH       && list[i]->tag_id != MyHTML_TAG_THEAD    &&
                       list[i]->tag_id != MyHTML_TAG_TR       && list[i]->tag_id != MyHTML_TAG_BODY     &&
                       list[i]->tag_id != MyHTML_TAG_HTML     && list[i]->ns != MyHTML_NAMESPACE_HTML)
                    {
                        // parse error
                    }
                }
                
                myhtml_rules_stop_parsing(tree);
                break;
            }
                
            case MyHTML_TAG_ADDRESS:
            case MyHTML_TAG_ARTICLE:
            case MyHTML_TAG_ASIDE:
            case MyHTML_TAG_BLOCKQUOTE:
            case MyHTML_TAG_CENTER:
            case MyHTML_TAG_DETAILS:
            case MyHTML_TAG_DIALOG:
            case MyHTML_TAG_DIR:
            case MyHTML_TAG_DIV:
            case MyHTML_TAG_DL:
            case MyHTML_TAG_FIELDSET:
            case MyHTML_TAG_FIGCAPTION:
            case MyHTML_TAG_FIGURE:
            case MyHTML_TAG_FOOTER:
            case MyHTML_TAG_HEADER:
            case MyHTML_TAG_HGROUP:
            case MyHTML_TAG_MAIN:
            case MyHTML_TAG_NAV:
            case MyHTML_TAG_OL:
            case MyHTML_TAG_P:
            case MyHTML_TAG_SECTION:
            case MyHTML_TAG_SUMMARY:
            case MyHTML_TAG_UL:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_MENU:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_MENUITEM))
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_H1:
            case MyHTML_TAG_H2:
            case MyHTML_TAG_H3:
            case MyHTML_TAG_H4:
            case MyHTML_TAG_H5:
            case MyHTML_TAG_H6:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                switch (current_node->tag_id) {
                    case MyHTML_TAG_H1:
                        case MyHTML_TAG_H2:
                        case MyHTML_TAG_H3:
                        case MyHTML_TAG_H4:
                        case MyHTML_TAG_H5:
                        case MyHTML_TAG_H6:
                        
                        if(current_node->ns == MyHTML_NAMESPACE_HTML) {
                            // parse error
                            /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:WARNING */
                            myhtml_tree_open_elements_pop(tree);
                        }
                        
                        break;
                        
                    default:
                        break;
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_PRE:
            case MyHTML_TAG_LISTING:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                // If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move on to the next one.
                // (Newlines at the start of pre blocks are ignored as an authoring convenience.)
                // !!! see dispatcher (myhtml_rules_tree_dispatcher) for this
                tree->flags |= MyHTML_TREE_FLAGS_PARSE_FLAG|MyHTML_TREE_FLAGS_PARSE_FLAG_EMIT_NEWLINE;
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                break;
            }
                
            case MyHTML_TAG_FORM:
            {
                myhtml_tree_node_t* is_in_node = myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL);
                if(tree->node_form && is_in_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_t* current = myhtml_tree_node_insert_html_element(tree, token);
                
                if(is_in_node == NULL)
                    tree->node_form = current;
                
                break;
            }
                
            case MyHTML_TAG_LI:
            {
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                size_t oel_index = tree->open_elements->length;
                
                while (oel_index) {
                    oel_index--;
                    
                    myhtml_tree_node_t* node = tree->open_elements->list[oel_index];
                    const myhtml_tag_context_t *tag_ctx = myhtml_tag_get_by_id(tree->tags, node->tag_id);
                    
                    /* 3 */
                    if(myhtml_is_html_node(node, MyHTML_TAG_LI)) {
                        /* 3.1 */
                        myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_LI, MyHTML_NAMESPACE_HTML);
                        
                        /* 3.2 */
                        myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                        if(myhtml_is_html_node(current_node, MyHTML_TAG_LI) == false) {
                            // parse error
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_LI NEED_NS:MyHTML_NAMESPACE_HTML */
                        }
                        
                        /* 3.3 */
                        myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_LI, MyHTML_NAMESPACE_HTML, false);
                        break;
                    }
                    else if(tag_ctx->cats[node->ns] & MyHTML_TAG_CATEGORIES_SPECIAL)
                    {
                        if(!((node->tag_id == MyHTML_TAG_ADDRESS || node->tag_id == MyHTML_TAG_DIV ||
                             node->tag_id == MyHTML_TAG_P) && node->ns == MyHTML_NAMESPACE_HTML))
                                break;
                    }
                }
                
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
            
            case MyHTML_TAG_DT:
            case MyHTML_TAG_DD:
            {
                // this is copy/past
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                size_t oel_index = tree->open_elements->length;
                
                while (oel_index) {
                    oel_index--;
                    
                    myhtml_tree_node_t* node = tree->open_elements->list[oel_index];
                    const myhtml_tag_context_t *tag_ctx = myhtml_tag_get_by_id(tree->tags, node->tag_id);
                    
                    if(myhtml_is_html_node(node, MyHTML_TAG_DD)) {
                        myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_DD, MyHTML_NAMESPACE_HTML);
                        
                        /* 3.2 */
                        myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                        if(myhtml_is_html_node(current_node, MyHTML_TAG_DD)) {
                            // parse error
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_DD NEED_NS:MyHTML_NAMESPACE_HTML */
                        }
                        
                        myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_DD, MyHTML_NAMESPACE_HTML, false);
                        break;
                    }
                    else if(myhtml_is_html_node(node, MyHTML_TAG_DT)) {
                        myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_DT, MyHTML_NAMESPACE_HTML);
                        
                        /* 3.2 */
                        myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                        if(myhtml_is_html_node(current_node, MyHTML_TAG_DT)) {
                            // parse error
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                            /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_DT NEED_NS:MyHTML_NAMESPACE_HTML */
                        }
                        
                        myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_DT, MyHTML_NAMESPACE_HTML, false);
                        break;
                    }
                    else if(tag_ctx->cats[node->ns] & MyHTML_TAG_CATEGORIES_SPECIAL)
                    {
                        if(!((node->tag_id == MyHTML_TAG_ADDRESS || node->tag_id == MyHTML_TAG_DIV ||
                             node->tag_id == MyHTML_TAG_P) && node->ns == MyHTML_NAMESPACE_HTML))
                                break;
                    }
                }
                
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_PLAINTEXT:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_PLAINTEXT;
                break;
            }
                
            case MyHTML_TAG_BUTTON:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_BUTTON, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE)) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                    
                    myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                    myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_BUTTON, MyHTML_NAMESPACE_HTML, false);
                }
                
                myhtml_tree_active_formatting_reconstruction(tree);
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                break;
            }
               
            case MyHTML_TAG_A:
            {
                myhtml_tree_node_t* node = myhtml_tree_active_formatting_between_last_marker(tree, MyHTML_TAG_A, NULL);
                
                if(node) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                    
                    myhtml_tree_adoption_agency_algorithm(tree, token, MyHTML_TAG_A);
                    node = myhtml_tree_active_formatting_between_last_marker(tree, MyHTML_TAG_A, NULL);
                    
                    if(node) {
                        myhtml_tree_open_elements_remove(tree, node);
                        myhtml_tree_active_formatting_remove(tree, node);
                    }
                }
                
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_t* current = myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append_with_check(tree, current);
                break;
            }
                
            case MyHTML_TAG_B:
            case MyHTML_TAG_BIG:
            case MyHTML_TAG_CODE:
            case MyHTML_TAG_EM:
            case MyHTML_TAG_FONT:
            case MyHTML_TAG_I:
            case MyHTML_TAG_S:
            case MyHTML_TAG_SMALL:
            case MyHTML_TAG_STRIKE:
            case MyHTML_TAG_STRONG:
            case MyHTML_TAG_TT:
            case MyHTML_TAG_U:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_t* current = myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append_with_check(tree, current);
                break;
            }

            case MyHTML_TAG_NOBR:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_NOBR, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE)) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                    
                    myhtml_tree_adoption_agency_algorithm(tree, token, MyHTML_TAG_NOBR);
                    myhtml_tree_active_formatting_reconstruction(tree);
                }
                
                myhtml_tree_node_t* current = myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append_with_check(tree, current);
                break;
            }

            case MyHTML_TAG_APPLET:
            case MyHTML_TAG_MARQUEE:
            case MyHTML_TAG_OBJECT:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append(tree, tree->myhtml->marker); // marker
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                break;
            }
                
            case MyHTML_TAG_TABLE:
            {
                if((tree->compat_mode & MyHTML_TREE_COMPAT_MODE_QUIRKS) == 0 &&
                   myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON))
                {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                break;
            }
                
            case MyHTML_TAG_AREA:
            case MyHTML_TAG_BR:
            case MyHTML_TAG_EMBED:
            case MyHTML_TAG_IMG:
            case MyHTML_TAG_KEYGEN:
            case MyHTML_TAG_WBR:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                break;
            }
                
            case MyHTML_TAG_INPUT:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                myhtml_token_node_wait_for_done(tree->token, token);
                if(myhtml_token_attr_match_case(tree->token, token, "type", 4, "hidden", 6) == NULL) {
                    tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                }
                
                break;
            }
                
            case MyHTML_TAG_PARAM:
            case MyHTML_TAG_SOURCE:
            case MyHTML_TAG_TRACK:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                break;
            }
                
            case MyHTML_TAG_HR:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_MENUITEM))
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                break;
            }
                
            case MyHTML_TAG_IMAGE:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES CONVERT STATUS:ELEMENT_CONVERT LEVEL:ERROR FROM_TAG_ID:MyHTML_TAG_IMAGE FROM_NS:MyHTML_NAMESPACE_ANY FROM_TYPE:MyHTML_TOKEN_TYPE_OPEN TO_TAG_ID:MyHTML_TAG_IMG TO_NS:MyHTML_NAMESPACE_ANY TO_TYPE:MyHTML_TOKEN_TYPE_OPEN */
                
                token->tag_id = MyHTML_TAG_IMG;
                return true;
            }
                
            case MyHTML_TAG_TEXTAREA:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                
                // If the next token is a U+000A LINE FEED (LF) character token,
                // then ignore that token and move on to the next one.
                // (Newlines at the start of textarea elements are ignored as an authoring convenience.)
                // !!! see dispatcher (myhtml_rules_tree_dispatcher) for this
                tree->flags |= MyHTML_TREE_FLAGS_PARSE_FLAG|MyHTML_TREE_FLAGS_PARSE_FLAG_EMIT_NEWLINE;
                
                tree->orig_insert_mode = tree->insert_mode;
                tree->flags           ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                tree->insert_mode      = MyHTML_INSERTION_MODE_TEXT;
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RCDATA;
                
                break;
            }

            case MyHTML_TAG_XMP:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_P, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_BUTTON)) {
                    myhtml_tree_tags_close_p(tree, token);
                }
                
                myhtml_tree_active_formatting_reconstruction(tree);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                
                myhtml_tree_generic_raw_text_element_parsing_algorithm(tree, token);
                break;
            }

            case MyHTML_TAG_IFRAME:
            {
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                
                myhtml_tree_generic_raw_text_element_parsing_algorithm(tree, token);
                break;
            }
                
            case MyHTML_TAG_NOEMBED:
            {
                tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                myhtml_tree_generic_raw_text_element_parsing_algorithm(tree, token);
                break;
            }
                
            case MyHTML_TAG_NOSCRIPT:
            {
                if(tree->flags & MyHTML_TREE_FLAGS_SCRIPT) {
                    tree->state_of_builder = MyHTML_TOKENIZER_STATE_RAWTEXT;
                    myhtml_tree_generic_raw_text_element_parsing_algorithm(tree, token);
                }
                else {
                    myhtml_tree_active_formatting_reconstruction(tree);
                    myhtml_tree_node_insert_html_element(tree, token);
                }
//                else {
//                    myhtml_tree_node_insert_html_element(tree, token);
//                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_HEAD_NOSCRIPT;
//                }
                
                break;
            }
                
            case MyHTML_TAG_SELECT:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                if(tree->insert_mode == MyHTML_INSERTION_MODE_IN_TABLE ||
                   tree->insert_mode == MyHTML_INSERTION_MODE_IN_CAPTION ||
                   tree->insert_mode == MyHTML_INSERTION_MODE_IN_TABLE_BODY ||
                   tree->insert_mode == MyHTML_INSERTION_MODE_IN_ROW ||
                   tree->insert_mode == MyHTML_INSERTION_MODE_IN_CELL)
                {
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT_IN_TABLE;
                }
                else
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_SELECT;
                
                break;
            }
                
            case MyHTML_TAG_OPTGROUP:
            case MyHTML_TAG_OPTION:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_OPTION))
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_MENUITEM:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_MENUITEM))
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_RB:
            case MyHTML_TAG_RTC:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_RUBY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE)) {
                    myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                }
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(current_node->tag_id != MyHTML_TAG_RUBY) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_RUBY NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_RP:
            case MyHTML_TAG_RT:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_RUBY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE)) {
                    myhtml_tree_generate_implied_end_tags(tree, MyHTML_TAG_RTC, MyHTML_NAMESPACE_HTML);
                }
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(current_node->tag_id != MyHTML_TAG_RTC && current_node->tag_id != MyHTML_TAG_RUBY) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_RTC NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_RUBY NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_MATH:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_token_node_wait_for_done(tree->token, token);
                
                myhtml_token_adjust_mathml_attributes(token);
                myhtml_token_adjust_foreign_attributes(token);
                
                myhtml_tree_node_t* current_node = myhtml_tree_node_insert_foreign_element(tree, token);
                current_node->ns = MyHTML_NAMESPACE_MATHML;
                
                if(token->type & MyHTML_TOKEN_TYPE_CLOSE_SELF)
                    myhtml_tree_open_elements_pop(tree);
                
                break;
            }
                
            case MyHTML_TAG_SVG:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                
                myhtml_token_node_wait_for_done(tree->token, token);
                
                myhtml_token_adjust_svg_attributes(token);
                myhtml_token_adjust_foreign_attributes(token);
                
                myhtml_tree_node_t* current_node = myhtml_tree_node_insert_foreign_element(tree, token);
                current_node->ns = MyHTML_NAMESPACE_SVG;
                
                if(token->type & MyHTML_TOKEN_TYPE_CLOSE_SELF)
                    myhtml_tree_open_elements_pop(tree);
                
                break;
            }
                
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_FRAME:
            case MyHTML_TAG_HEAD:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            default:
            {
                myhtml_tree_active_formatting_reconstruction(tree);
                myhtml_tree_node_insert_html_element(tree, token);
                
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_text(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_SCRIPT:
            {
                // new document.write is not works; set back
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = tree->orig_insert_mode;
                break;
            }
                
            default:
            {
                myhtml_tree_open_elements_pop(tree);
                tree->insert_mode = tree->orig_insert_mode;
                break;
            }
        }
    }
    else {
        if(token->tag_id == MyHTML_TAG__END_OF_FILE)
        {
            // parse error
            /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:PREMATURE_TERMINATION LEVEL:ERROR */
            
            myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
            
            if(current_node->tag_id == MyHTML_TAG_SCRIPT)
                current_node->flags |= MyHTML_TREE_FLAGS_ALREADY_STARTED;
            
            myhtml_tree_open_elements_pop(tree);
            
            tree->insert_mode = tree->orig_insert_mode;
            return true;
        }
        
        myhtml_tree_node_insert_text(tree, token);
    }
    
    return false;
}

bool myhtml_insertion_mode_in_table(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TABLE:
            {
                myhtml_tree_node_t* table_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TABLE, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(table_node == NULL) {
                     // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_open_elements_pop_until_by_node(tree, table_node, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                break;
            }
                
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            default: {
                // parse error
                tree->foster_parenting = true;
                myhtml_insertion_mode_in_body(tree, token);
                tree->foster_parenting = false;
                
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if((current_node->tag_id == MyHTML_TAG_TABLE  ||
                   current_node->tag_id  == MyHTML_TAG_TBODY  ||
                   current_node->tag_id  == MyHTML_TAG_TFOOT  ||
                   current_node->tag_id  == MyHTML_TAG_THEAD  ||
                   current_node->tag_id  == MyHTML_TAG_TR)    &&
                   current_node->ns == MyHTML_NAMESPACE_HTML)
                {
                    myhtml_tree_token_list_clean(tree->token_list);
                    
                    tree->orig_insert_mode = tree->insert_mode;
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_TEXT;
                    
                    return true;
                }
                else {
                    tree->foster_parenting = true;
                    myhtml_insertion_mode_in_body(tree, token);
                    tree->foster_parenting = false;
                    
                    break;
                }
            }
            
            case MyHTML_TAG__COMMENT:
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
                
            case MyHTML_TAG__DOCTYPE: {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:WARNING */
                break;
            }
                
            case MyHTML_TAG_CAPTION:
            {
                myhtml_tree_clear_stack_back_table_context(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append(tree, tree->myhtml->marker);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_CAPTION;
                break;
            }
                
            case MyHTML_TAG_COLGROUP:
            {
                myhtml_tree_clear_stack_back_table_context(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_COLUMN_GROUP;
                break;
            }
                
            case MyHTML_TAG_COL:
            {
                myhtml_tree_clear_stack_back_table_context(tree);
                myhtml_tree_node_insert(tree, MyHTML_TAG_COLGROUP, MyHTML_NAMESPACE_HTML);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_COLUMN_GROUP;
                return true;
            }
                
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            {
                myhtml_tree_clear_stack_back_table_context(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                break;
            }
               
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_TR:
            {
                myhtml_tree_clear_stack_back_table_context(tree);
                myhtml_tree_node_insert(tree, MyHTML_TAG_TBODY, MyHTML_NAMESPACE_HTML);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                return true;
            }
                
            case MyHTML_TAG_TABLE:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_t* table_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TABLE, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(table_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_TABLE, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                return true;
            }
                
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG_SCRIPT:
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            case MyHTML_TAG_INPUT:
            {
                myhtml_token_node_wait_for_done(tree->token, token);
                
                if(myhtml_token_attr_match_case(tree->token, token, "type", 4, "hidden", 6) == NULL) {
                    tree->foster_parenting = true;
                    myhtml_insertion_mode_in_body(tree, token);
                    tree->foster_parenting = false;
                    
                    break;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                
                token->type |= MyHTML_TOKEN_TYPE_CLOSE_SELF;
                break;
            }
                
            case MyHTML_TAG_FORM:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_t* template = myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL);
                if(tree->node_form || template)
                    break;
                
                tree->node_form = myhtml_tree_node_insert_html_element(tree, token);
                
                myhtml_tree_open_elements_pop(tree);
            }
                
            case MyHTML_TAG__END_OF_FILE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            default:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                tree->foster_parenting = true;
                myhtml_insertion_mode_in_body(tree, token);
                tree->foster_parenting = false;
                
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_table_text(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    // skip NULL, we replaced earlier
    if(token->tag_id == MyHTML_TAG__TEXT)
    {
        if(token->type & MyHTML_TOKEN_TYPE_NULL) {
            // parse error
            /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:NULL_CHAR ACTION:IGNORE LEVEL:ERROR */
            
            myhtml_insertion_fix_for_null_char_drop_all(tree, token);
            
            if(token->str.length)
                myhtml_tree_token_list_append(tree->token_list, token);
        }
        else
            myhtml_tree_token_list_append(tree->token_list, token);
    }
    else {
        myhtml_tree_token_list_t* token_list = tree->token_list;
        bool is_not_ws = false;
        
        for(size_t i = 0; i < token_list->length; i++) {
            if((token_list->list[i]->type & MyHTML_TOKEN_TYPE_WHITESPACE) == 0) {
                is_not_ws = true;
                break;
            }
        }
        
        if(is_not_ws)
        {
            for(size_t i = 0; i < token_list->length; i++) {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR TOKEN:token_list->list[i] */
                
                tree->foster_parenting = true;
                myhtml_insertion_mode_in_body(tree, token_list->list[i]);
                tree->foster_parenting = false;
            }
        }
        else {
            for(size_t i = 0; i < token_list->length; i++) {
                myhtml_tree_node_insert_text(tree, token_list->list[i]);
            }
        }
        
        tree->insert_mode = tree->orig_insert_mode;
        return true;
    }
    
    return false;
}

bool myhtml_insertion_mode_in_caption(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_CAPTION:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, MyHTML_TAG_CAPTION) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_CAPTION NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                break;
            }
              
            case MyHTML_TAG_TABLE:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, MyHTML_TAG_CAPTION) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_CAPTION NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                return true;
            }
                
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            default:
                return myhtml_insertion_mode_in_body(tree, token);
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                if(myhtml_tree_element_in_scope(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE) == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                if(myhtml_is_html_node(current_node, MyHTML_TAG_CAPTION) == false) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:MyHTML_TAG_CAPTION NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_CAPTION, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_body(tree, token);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_column_group(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_COLGROUP:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node && myhtml_is_html_node(current_node, MyHTML_TAG_COLGROUP)) {
                    myhtml_tree_open_elements_pop(tree);
                    
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                    return false;
                }
                
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_COL:
            {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            default: {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node && myhtml_is_html_node(current_node, MyHTML_TAG_COLGROUP)) {
                    myhtml_tree_open_elements_pop(tree);
                    
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                    return true;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                    myhtml_tree_node_insert_text(tree, token);
                    break;
                }
                
                myhtml_token_node_t* new_token = myhtml_insertion_fix_split_for_text_begin_ws(tree, token);
                if(new_token)
                    myhtml_tree_node_insert_text(tree, new_token);
                
                /* default: */
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node && myhtml_is_html_node(current_node, MyHTML_TAG_COLGROUP)) {
                    myhtml_tree_open_elements_pop(tree);
                    
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                    return true;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, 0);
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                break;
            }
            case MyHTML_TAG_HTML:
            {
                return myhtml_insertion_mode_in_body(tree, token);
            }
                
            case MyHTML_TAG_COL:
            {
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                break;
            }
                
            case MyHTML_TAG_TEMPLATE:
            {
                return myhtml_insertion_mode_in_head(tree, token);
            }
                
            case MyHTML_TAG__END_OF_FILE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            default:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node && myhtml_is_html_node(current_node, MyHTML_TAG_COLGROUP)) {
                    myhtml_tree_open_elements_pop(tree);
                    
                    tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                    return true;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_table_body(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            {
                myhtml_tree_node_t* node = myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_body_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                break;
            }
                
            case MyHTML_TAG_TABLE:
            {
                myhtml_tree_node_t* tbody_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TBODY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                myhtml_tree_node_t* tfoot_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TFOOT, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                myhtml_tree_node_t* thead_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_THEAD, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(tbody_node == NULL && tfoot_node == NULL && thead_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_THEAD NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TBODY NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TFOOT NEED_NS:MyHTML_NAMESPACE_HTML */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_body_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                return true;
            }
               
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_TR:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            default:
                return myhtml_insertion_mode_in_table(tree, token);
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG_TR:
            {
                myhtml_tree_clear_stack_back_table_body_context(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
                break;
            }
                
            case MyHTML_TAG_TH:
            case MyHTML_TAG_TD:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_clear_stack_back_table_body_context(tree);
                
                myhtml_tree_node_insert(tree, MyHTML_TAG_TR, MyHTML_NAMESPACE_HTML);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
                return true;
            }
                
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            {
                myhtml_tree_node_t* tbody_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TBODY, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                myhtml_tree_node_t* tfoot_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TFOOT, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                myhtml_tree_node_t* thead_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_THEAD, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(tbody_node == NULL && tfoot_node == NULL && thead_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_THEAD NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TBODY NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TFOOT NEED_NS:MyHTML_NAMESPACE_HTML */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_body_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_table(tree, token);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_row(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TR:
            {
                myhtml_tree_node_t* tr_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TR, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(tr_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_row_context(tree);
                
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                break;
            }
                
            case MyHTML_TAG_TABLE:
            {
                myhtml_tree_node_t* tr_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TR, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(tr_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TR NEED_NS:MyHTML_NAMESPACE_HTML */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_row_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                return true;
            }
                
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            {
                myhtml_tree_node_t* node = myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                if(node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_node_t* tr_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TR, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                if(tr_node == NULL)
                    break;
                
                myhtml_tree_clear_stack_back_table_row_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                return true;
            }
                
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_HTML:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            default:
                return myhtml_insertion_mode_in_table(tree, token);
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG_TH:
            case MyHTML_TAG_TD:
            {
                myhtml_tree_clear_stack_back_table_row_context(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_active_formatting_append(tree, tree->myhtml->marker);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_CELL;
                break;
            }
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                myhtml_tree_node_t* tr_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TR, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(tr_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TR NEED_NS:MyHTML_NAMESPACE_HTML */
                    break;
                }
                
                myhtml_tree_clear_stack_back_table_row_context(tree);
                myhtml_tree_open_elements_pop(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_table(tree, token);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_cell(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            {
                myhtml_tree_node_t* node = myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_generate_implied_end_tags(tree, 0, MyHTML_NAMESPACE_UNDEF);
                
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, token->tag_id) == false)
                {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:NULL HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
                }
                
                myhtml_tree_open_elements_pop_until(tree, token->tag_id, MyHTML_NAMESPACE_HTML, false);
                
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
                break;
            }
                
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_HTML:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
                
            case MyHTML_TAG_TABLE:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                myhtml_tree_node_t* node = myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TD, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                if(node) {
                    myhtml_tree_close_cell(tree, node, token);
                }
                else {
                    node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TH, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                    if(node)
                        myhtml_tree_close_cell(tree, node, token);
                }
                
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_table(tree, token);
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COL:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_TH:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            {
                myhtml_tree_node_t* td_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TD, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                myhtml_tree_node_t* th_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_TH, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(td_node == NULL && th_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TD NEED_NS:MyHTML_NAMESPACE_HTML */
                    /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:NULL NEED:NULL HAVE_TAG_ID:MyHTML_TAG__UNDEF HAVE_NS:MyHTML_NAMESPACE_UNDEF NEED_TAG_ID:MyHTML_TAG_TH NEED_NS:MyHTML_NAMESPACE_HTML */
                    
                    break;
                }
                
                myhtml_tree_close_cell(tree, (td_node == NULL ? th_node : td_node), token);
                
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_body(tree, token);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_select(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_OPTGROUP:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_OPTION))
                {
                    if(tree->open_elements->length > 1) {
                        myhtml_tree_node_t *optgrp_node = tree->open_elements->list[ tree->open_elements->length - 2 ];
                        
                        if(myhtml_is_html_node(optgrp_node, MyHTML_TAG_OPTGROUP))
                        {
                            myhtml_tree_open_elements_pop(tree);
                        }
                    }
                }
                
                current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_OPTGROUP))
                    myhtml_tree_open_elements_pop(tree);
                else {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                break;
            }
                
            case MyHTML_TAG_OPTION:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, MyHTML_TAG_OPTION))
                    myhtml_tree_open_elements_pop(tree);
                else {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                break;
            }
                
            case MyHTML_TAG_SELECT:
            {
                myhtml_tree_node_t* select_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_SELECT, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_SELECT);
                
                if(select_node == NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    break;
                }
                
                myhtml_tree_open_elements_pop_until_by_node(tree, select_node, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                break;
            }
                
            case MyHTML_TAG_TEMPLATE:
                return myhtml_insertion_mode_in_head(tree, token);
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT: {
                if(token->type & MyHTML_TOKEN_TYPE_NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:NULL_CHAR ACTION:IGNORE LEVEL:ERROR */
                    
                    myhtml_insertion_fix_for_null_char_drop_all(tree, token);
                    
                    if(token->str.length)
                        myhtml_tree_node_insert_text(tree, token);
                }
                else
                    myhtml_tree_node_insert_text(tree, token);
                
                break;
            }
                
            case MyHTML_TAG__COMMENT:
                myhtml_tree_node_insert_comment(tree, token, NULL);
                break;
                
            case MyHTML_TAG__DOCTYPE: {
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
                
            case MyHTML_TAG_HTML:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG_OPTION:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(myhtml_is_html_node(current_node, token->tag_id))
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_OPTGROUP:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node->tag_id == MyHTML_TAG_OPTION &&
                   current_node->ns == MyHTML_NAMESPACE_HTML)
                    myhtml_tree_open_elements_pop(tree);
                
                current_node = myhtml_tree_current_node(tree);
                
                if(current_node->tag_id == token->tag_id &&
                   current_node->ns == MyHTML_NAMESPACE_HTML)
                    myhtml_tree_open_elements_pop(tree);
                
                myhtml_tree_node_insert_html_element(tree, token);
                break;
            }
                
            case MyHTML_TAG_SELECT:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_t* select_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_SELECT, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_SELECT);
                
                if(select_node == NULL) {
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_open_elements_pop_until_by_node(tree, select_node, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                break;
            }
                
            case MyHTML_TAG_INPUT:
            case MyHTML_TAG_KEYGEN:
            case MyHTML_TAG_TEXTAREA:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_t* select_node = myhtml_tree_element_in_scope(tree, MyHTML_TAG_SELECT, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_SELECT);
                
                if(select_node == NULL) {
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_open_elements_pop_until_by_node(tree, select_node, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                return true;
            }
                
            case MyHTML_TAG_SCRIPT:
            case MyHTML_TAG_TEMPLATE:
                return myhtml_insertion_mode_in_head(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_select_in_table(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_TABLE:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_node_t* some_node = myhtml_tree_element_in_scope(tree, token->tag_id, MyHTML_NAMESPACE_HTML, MyHTML_TAG_CATEGORIES_SCOPE_TABLE);
                
                if(some_node == NULL) {
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_OPEN_NOT_FOUND ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_SELECT, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                return true;
            }
                
            default:
                return myhtml_insertion_mode_in_select(tree, token);
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_TABLE:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
            case MyHTML_TAG_TR:
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION LEVEL:ERROR */
                
                myhtml_tree_open_elements_pop_until(tree, MyHTML_TAG_SELECT, MyHTML_NAMESPACE_HTML, false);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                return true;
            }
            
            default:
                return myhtml_insertion_mode_in_select(tree, token);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_template(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_TEMPLATE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            case MyHTML_TAG__COMMENT:
            case MyHTML_TAG__DOCTYPE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG_BASE:
            case MyHTML_TAG_BASEFONT:
            case MyHTML_TAG_BGSOUND:
            case MyHTML_TAG_LINK:
            case MyHTML_TAG_META:
            case MyHTML_TAG_NOFRAMES:
            case MyHTML_TAG_SCRIPT:
            case MyHTML_TAG_STYLE:
            case MyHTML_TAG_TEMPLATE:
            case MyHTML_TAG_TITLE:
                return myhtml_insertion_mode_in_head(tree, token);
                
            case MyHTML_TAG_CAPTION:
            case MyHTML_TAG_COLGROUP:
            case MyHTML_TAG_TBODY:
            case MyHTML_TAG_TFOOT:
            case MyHTML_TAG_THEAD:
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_TABLE);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE;
                return true;
                
            case MyHTML_TAG_COL:
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_COLUMN_GROUP);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_COLUMN_GROUP;
                return true;
                
            case MyHTML_TAG_TR:
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_TABLE_BODY);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_TABLE_BODY;
                return true;
                
            case MyHTML_TAG_TD:
            case MyHTML_TAG_TH:
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_ROW);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_ROW;
                return true;
                
            case MyHTML_TAG__END_OF_FILE:
            {
                myhtml_tree_node_t* node = myhtml_tree_open_elements_find_by_tag_idx(tree, MyHTML_TAG_TEMPLATE, MyHTML_NAMESPACE_HTML, NULL);
                
                if(node == NULL) {
                    myhtml_rules_stop_parsing(tree);
                    break;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TAG STATUS:ELEMENT_NOT_CLOSED LEVEL:ERROR TAG_ID:MyHTML_TAG_TEMPLATE NS:MyHTML_NAMESPACE_HTML */
                
                myhtml_tree_open_elements_pop_until_by_node(tree, node, false);
                myhtml_tree_active_formatting_up_to_last_marker(tree);
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_reset_insertion_mode_appropriately(tree);
                
                return true;
            }
                
             default:
                myhtml_tree_template_insertion_pop(tree);
                myhtml_tree_template_insertion_append(tree, MyHTML_INSERTION_MODE_IN_BODY);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_after_body(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_HTML:
            {
                if(tree->fragment) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_AFTER_BODY;
                break;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:ERROR */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                    return myhtml_insertion_mode_in_body(tree, token);
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                if(tree->open_elements->length == 0) {
                    MyCORE_DEBUG_ERROR("after body state; open_elements length < 1");
                    break;
                }
                
                myhtml_tree_node_t* adjusted_location = tree->open_elements->list[0];
                
                // state 2
                myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
                
                node->tag_id      = MyHTML_TAG__COMMENT;
                node->token        = token;
                node->ns = adjusted_location->ns;
                
                myhtml_tree_node_add_child(adjusted_location, node);
                
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
            case MyHTML_TAG_HTML:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
                myhtml_rules_stop_parsing(tree);
                break;
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:ERROR */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_frameset(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_FRAMESET:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node == tree->document->child) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED ACTION:IGNORE LEVEL:ERROR */
                    
                    break;
                }
                
                myhtml_tree_open_elements_pop(tree);
                
                current_node = myhtml_tree_current_node(tree);
                
                if(tree->fragment == NULL &&
                   !(current_node->tag_id == MyHTML_TAG_FRAMESET &&
                     current_node->ns == MyHTML_NAMESPACE_HTML))
                {
                    tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_FRAMESET;
                }
                
                break;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                    myhtml_tree_node_insert_text(tree, token);
                    break;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                myhtml_token_node_wait_for_done(tree->token, token);
                mycore_string_stay_only_whitespace(&token->str);
                
                if(token->str.length)
                    myhtml_tree_node_insert_text(tree, token);
                
                break;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, NULL);
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
                
            case MyHTML_TAG_HTML:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG_FRAMESET:
                myhtml_tree_node_insert_html_element(tree, token);
                break;
                
            case MyHTML_TAG_FRAME:
                myhtml_tree_node_insert_html_element(tree, token);
                myhtml_tree_open_elements_pop(tree);
                break;
                
            case MyHTML_TAG_NOFRAMES:
                return myhtml_insertion_mode_in_head(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
            {
                myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
                
                if(current_node == tree->document->child) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                }
                
                myhtml_rules_stop_parsing(tree);
                break;
            }
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_after_frameset(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        switch (token->tag_id) {
            case MyHTML_TAG_HTML:
                tree->insert_mode = MyHTML_INSERTION_MODE_AFTER_AFTER_FRAMESET;
                break;
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE) {
                    myhtml_tree_node_insert_text(tree, token);
                    break;
                }
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                myhtml_token_node_wait_for_done(tree->token, token);
                mycore_string_stay_only_whitespace(&token->str);
                
                if(token->str.length)
                    myhtml_tree_node_insert_text(tree, token);
                
                break;
            }
                
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_insert_comment(tree, token, NULL);
                break;
            }
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                break;
            }
                
            case MyHTML_TAG_HTML:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG_NOFRAMES:
                return myhtml_insertion_mode_in_head(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
                myhtml_rules_stop_parsing(tree);
                break;
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_after_after_body(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE)
    {
        tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
        return true;
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_t* adjusted_location = tree->document;
                myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
                
                node->tag_id      = MyHTML_TAG__COMMENT;
                node->token        = token;
                node->ns = adjusted_location->ns;
                
                myhtml_tree_node_add_child(adjusted_location, node);
                break;
            }
                
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                    return myhtml_insertion_mode_in_body(tree, token);
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
                
            case MyHTML_TAG_HTML:
            case MyHTML_TAG__DOCTYPE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
                myhtml_rules_stop_parsing(tree);
                break;
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                
                tree->insert_mode = MyHTML_INSERTION_MODE_IN_BODY;
                return true;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_after_after_frameset(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE) {
        // parse error
        /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:ERROR */
        
        return false;
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__COMMENT:
            {
                myhtml_tree_node_t* adjusted_location = tree->document;
                myhtml_tree_node_t* node = myhtml_tree_node_create(tree);
                
                node->tag_id      = MyHTML_TAG__COMMENT;
                node->token        = token;
                node->ns = adjusted_location->ns;
                
                myhtml_tree_node_add_child(adjusted_location, node);
                break;
            }
                
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_WHITESPACE)
                    return myhtml_insertion_mode_in_body(tree, token);
                
                myhtml_token_node_t* new_token = myhtml_insertion_fix_split_for_text_begin_ws(tree, token);
                if(new_token)
                    return myhtml_insertion_mode_in_body(tree, new_token);
                
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:ERROR */
                
                break;
            }
                
            case MyHTML_TAG_HTML:
            case MyHTML_TAG__DOCTYPE:
                return myhtml_insertion_mode_in_body(tree, token);
                
            case MyHTML_TAG__END_OF_FILE:
                myhtml_rules_stop_parsing(tree);
                break;
                
            case MyHTML_TAG_NOFRAMES:
                return myhtml_insertion_mode_in_head(tree, token);
                
            default: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_UNNECESSARY LEVEL:ERROR */
                break;
            }
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_foreign_content_end_other(myhtml_tree_t* tree, myhtml_tree_node_t* current_node, myhtml_token_node_t* token)
{
    if(current_node->tag_id != token->tag_id) {
        // parse error
        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
        /* %EXTERNAL% VALIDATOR:RULES HAVE_NEED_ADD HAVE:current_node->token NEED:token HAVE_TAG_ID:current_node->tag_id HAVE_NS:current_node->ns NEED_TAG_ID:token->tag_id NEED_NS:MyHTML_NAMESPACE_HTML */
    }
    
    if(tree->open_elements->length)
    {
        myhtml_tree_node_t** list = tree->open_elements->list;
        size_t i = tree->open_elements->length - 1;
        
        while (i)
        {
            current_node = list[i];
            
            if(current_node->tag_id == token->tag_id) {
                myhtml_tree_open_elements_pop_until_by_node(tree, current_node, false);
                return false;
            }
            
            i--;
            
            if(list[i]->ns == MyHTML_NAMESPACE_HTML)
                break;
        }
    }
    
    return tree->myhtml->insertion_func[tree->insert_mode](tree, token);
}

bool myhtml_insertion_mode_in_foreign_content_start_other(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    myhtml_tree_node_t* adjusted_node = myhtml_tree_adjusted_current_node(tree);
    
    myhtml_token_node_wait_for_done(tree->token, token);
    
    if(adjusted_node->ns == MyHTML_NAMESPACE_MATHML) {
        myhtml_token_adjust_mathml_attributes(token);
    }
    else if(adjusted_node->ns == MyHTML_NAMESPACE_SVG) {
        myhtml_token_adjust_svg_attributes(token);
    }
    
    myhtml_token_adjust_foreign_attributes(token);
    
    myhtml_tree_node_t* node = myhtml_tree_node_insert_foreign_element(tree, token);
    node->ns = adjusted_node->ns;
    
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE_SELF)
    {
        if(token->tag_id == MyHTML_TAG_SCRIPT &&
           node->ns == MyHTML_NAMESPACE_SVG)
        {
            return myhtml_insertion_mode_in_foreign_content_end_other(tree, myhtml_tree_current_node(tree), token);
        }
        else {
            myhtml_tree_open_elements_pop(tree);
        }
    }
    
    return false;
}

bool myhtml_insertion_mode_in_foreign_content(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(token->type & MyHTML_TOKEN_TYPE_CLOSE) {
        myhtml_tree_node_t* current_node = myhtml_tree_current_node(tree);
        
        if(token->tag_id == MyHTML_TAG_SCRIPT &&
           current_node->tag_id == MyHTML_TAG_SCRIPT &&
           current_node->ns == MyHTML_NAMESPACE_SVG)
        {
            myhtml_tree_open_elements_pop(tree);
            // TODO: now script is disable, skip this
            return false;
        }
        
        return myhtml_insertion_mode_in_foreign_content_end_other(tree, current_node, token);
    }
    else {
        switch (token->tag_id)
        {
            case MyHTML_TAG__TEXT:
            {
                if(token->type & MyHTML_TOKEN_TYPE_NULL) {
                    // parse error
                    /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:NULL_CHAR LEVEL:ERROR */
                    
                    myhtml_token_node_wait_for_done(tree->token, token);
                    myhtml_token_set_replacement_character_for_null_token(tree, token);
                }
                
                myhtml_tree_node_insert_text(tree, token);
                
                if((token->type & MyHTML_TOKEN_TYPE_WHITESPACE) == 0)
                    tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_FRAMESET_OK);
                
                break;
            }
                
            case MyHTML_TAG__COMMENT:
                myhtml_tree_node_insert_comment(tree, token, NULL);
                break;
                
            case MyHTML_TAG__DOCTYPE: {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_WRONG_LOCATION ACTION:IGNORE LEVEL:ERROR */
                
                break;
            }
                
            case MyHTML_TAG_B:
            case MyHTML_TAG_BIG:
            case MyHTML_TAG_BLOCKQUOTE:
            case MyHTML_TAG_BODY:
            case MyHTML_TAG_BR:
            case MyHTML_TAG_CENTER:
            case MyHTML_TAG_CODE:
            case MyHTML_TAG_DD:
            case MyHTML_TAG_DIV:
            case MyHTML_TAG_DL:
            case MyHTML_TAG_DT:
            case MyHTML_TAG_EM:
            case MyHTML_TAG_EMBED:
            case MyHTML_TAG_H1:
            case MyHTML_TAG_H2:
            case MyHTML_TAG_H3:
            case MyHTML_TAG_H4:
            case MyHTML_TAG_H5:
            case MyHTML_TAG_H6:
            case MyHTML_TAG_HEAD:
            case MyHTML_TAG_HR:
            case MyHTML_TAG_I:
            case MyHTML_TAG_IMG:
            case MyHTML_TAG_LI:
            case MyHTML_TAG_LISTING:
            case MyHTML_TAG_MENU:
            case MyHTML_TAG_META:
            case MyHTML_TAG_NOBR:
            case MyHTML_TAG_OL:
            case MyHTML_TAG_P:
            case MyHTML_TAG_PRE:
            case MyHTML_TAG_RUBY:
            case MyHTML_TAG_S:
            case MyHTML_TAG_SMALL:
            case MyHTML_TAG_SPAN:
            case MyHTML_TAG_STRONG:
            case MyHTML_TAG_STRIKE:
            case MyHTML_TAG_SUB:
            case MyHTML_TAG_SUP:
            case MyHTML_TAG_TABLE:
            case MyHTML_TAG_TT:
            case MyHTML_TAG_U:
            case MyHTML_TAG_UL:
            case MyHTML_TAG_VAR:
            case MyHTML_TAG_FONT:
            {
                // parse error
                /* %EXTERNAL% VALIDATOR:RULES TOKEN STATUS:ELEMENT_NO_EXPECTED LEVEL:ERROR */
                
                if(token->tag_id == MyHTML_TAG_FONT)
                {
                    myhtml_token_node_wait_for_done(tree->token, token);
                    
                    if(myhtml_token_attr_by_name(token, "color", 5) == NULL &&
                       myhtml_token_attr_by_name(token, "face" , 4) == NULL &&
                       myhtml_token_attr_by_name(token, "size" , 4) == NULL)
                    {
                        return myhtml_insertion_mode_in_foreign_content_start_other(tree, token);
                    }
                }
                
                if(tree->fragment == NULL) {
                    myhtml_tree_node_t* current_node;
                    
                    do {
                        myhtml_tree_open_elements_pop(tree);
                        current_node = myhtml_tree_current_node(tree);
                    }
                    while(current_node && !(myhtml_tree_is_mathml_integration_point(tree, current_node) ||
                                            myhtml_tree_is_html_integration_point(tree, current_node) ||
                                            current_node->ns == MyHTML_NAMESPACE_HTML));
                    
                    return true;
                }
            }
                
            default:
                return myhtml_insertion_mode_in_foreign_content_start_other(tree, token);
        }
    }
    
    return false;
}

void myhtml_rules_stop_parsing(myhtml_tree_t* tree)
{
    // THIS! IS! -(SPARTA!)- STOP PARSING
}

bool myhtml_rules_check_for_first_newline(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    if(tree->flags & MyHTML_TREE_FLAGS_PARSE_FLAG) {
        if(tree->flags &MyHTML_TREE_FLAGS_PARSE_FLAG_EMIT_NEWLINE)
        {
            if(token->tag_id == MyHTML_TAG__TEXT) {
                myhtml_token_node_wait_for_done(tree->token, token);
                
                if(token->str.length > 0) {
                    if(token->str.data[0] == '\n') {
                        token->str.data = mchar_async_crop_first_chars_without_cache(token->str.data, 1);
                        
                        token->str.length--;
                        
                        if(token->str.length == 0) {
                            tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_PARSE_FLAG);
                            return true;
                        }
                    }
                }
                else
                    return true;
            }
        }
        
        tree->flags ^= (tree->flags & MyHTML_TREE_FLAGS_PARSE_FLAG);
    }
    
    return false;
}

bool myhtml_rules_tree_dispatcher(myhtml_tree_t* tree, myhtml_token_node_t* token)
{
    // for textarea && pre && listen
    if(myhtml_rules_check_for_first_newline(tree, token))
        return false;
    
    if(tree->state_of_builder != MyHTML_TOKENIZER_STATE_DATA)
        tree->state_of_builder = MyHTML_TOKENIZER_STATE_DATA;
    
    bool reprocess = false;
    myhtml_tree_node_t* adjusted_node = myhtml_tree_adjusted_current_node(tree);
    
    if(tree->open_elements->length == 0 || adjusted_node->ns == MyHTML_NAMESPACE_HTML) {
        reprocess = tree->myhtml->insertion_func[tree->insert_mode](tree, token);
    }
    else if(myhtml_tree_is_mathml_integration_point(tree, adjusted_node) &&
            ((token->tag_id == MyHTML_TAG__TEXT ||
              (token->tag_id != MyHTML_TAG_MGLYPH && token->tag_id != MyHTML_TAG_MALIGNMARK)) &&
             (token->type & MyHTML_TOKEN_TYPE_CLOSE) == 0))
    {
            reprocess = tree->myhtml->insertion_func[tree->insert_mode](tree, token);
    }
    else if(adjusted_node->tag_id == MyHTML_TAG_ANNOTATION_XML &&
       adjusted_node->ns == MyHTML_NAMESPACE_MATHML &&
       token->tag_id == MyHTML_TAG_SVG && (token->type & MyHTML_TOKEN_TYPE_CLOSE) == 0)
    {
        reprocess = tree->myhtml->insertion_func[tree->insert_mode](tree, token);
    }
    else if(myhtml_tree_is_html_integration_point(tree, adjusted_node) &&
            ((token->type & MyHTML_TOKEN_TYPE_CLOSE) == 0 || token->tag_id == MyHTML_TAG__TEXT))
    {
        reprocess = tree->myhtml->insertion_func[tree->insert_mode](tree, token);
    }
    else if(token->tag_id == MyHTML_TAG__END_OF_FILE)
        reprocess = tree->myhtml->insertion_func[tree->insert_mode](tree, token);
    else
        reprocess = myhtml_insertion_mode_in_foreign_content(tree, token);
    
    if(reprocess == false) {
        tree->token_last_done = token;
    }
    
    return reprocess;
}

mystatus_t myhtml_rules_init(myhtml_t* myhtml)
{
    myhtml->insertion_func = (myhtml_insertion_f*)mycore_malloc(sizeof(myhtml_insertion_f) * MyHTML_INSERTION_MODE_LAST_ENTRY);
    
    if(myhtml->insertion_func == NULL)
        return MyHTML_STATUS_RULES_ERROR_MEMORY_ALLOCATION;
    
    myhtml->insertion_func[MyHTML_INSERTION_MODE_INITIAL] = myhtml_insertion_mode_initial;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_BEFORE_HTML] = myhtml_insertion_mode_before_html;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_BEFORE_HEAD] = myhtml_insertion_mode_before_head;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_HEAD] = myhtml_insertion_mode_in_head;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_HEAD_NOSCRIPT] = myhtml_insertion_mode_in_head_noscript;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_AFTER_HEAD] = myhtml_insertion_mode_after_head;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_BODY] = myhtml_insertion_mode_in_body;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_TEXT] = myhtml_insertion_mode_text;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_TABLE] = myhtml_insertion_mode_in_table;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_TABLE_TEXT] = myhtml_insertion_mode_in_table_text;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_CAPTION] = myhtml_insertion_mode_in_caption;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_COLUMN_GROUP] = myhtml_insertion_mode_in_column_group;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_TABLE_BODY] = myhtml_insertion_mode_in_table_body;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_ROW] = myhtml_insertion_mode_in_row;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_CELL] = myhtml_insertion_mode_in_cell;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_SELECT] = myhtml_insertion_mode_in_select;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_SELECT_IN_TABLE] = myhtml_insertion_mode_in_select_in_table;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_TEMPLATE] = myhtml_insertion_mode_in_template;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_AFTER_BODY] = myhtml_insertion_mode_after_body;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_IN_FRAMESET] = myhtml_insertion_mode_in_frameset;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_AFTER_FRAMESET] = myhtml_insertion_mode_after_frameset;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_AFTER_AFTER_BODY] = myhtml_insertion_mode_after_after_body;
    myhtml->insertion_func[MyHTML_INSERTION_MODE_AFTER_AFTER_FRAMESET] = myhtml_insertion_mode_after_after_frameset;
    
    return MyHTML_STATUS_OK;
}


