#include "html.h"
#include "css_selector.h"
#include "document.h"
void litehtml::css_element_selector::parse_nth_child_params(const string& param, int& num, int& off)
{
if (param == "odd")
{
num = 2;
off = 1;
}
else if (param == "even")
{
num = 2;
off = 0;
}
else
{
string_vector tokens;
split_string(param, tokens, " n", "n");
string s_num;
string s_off;
string s_int;
for (const auto& token : tokens)
{
if (token == "n")
{
s_num = s_int;
s_int.clear();
}
else
{
s_int += token;
}
}
s_off = s_int;
num = atoi(s_num.c_str());
off = atoi(s_off.c_str());
}
}
void litehtml::css_element_selector::parse( const string& txt )
{
string::size_type el_end = txt.find_first_of(".#[:");
string tag = txt.substr(0, el_end);
litehtml::lcase(tag);
if (tag == "") tag = "*";
m_tag = _id(tag);
m_attrs.clear();
while(el_end != string::npos)
{
if(txt[el_end] == '.')
{
css_attribute_selector attribute;
attribute.type = select_class;
string::size_type pos = txt.find_first_of(".#[:", el_end + 1);
string name = txt.substr(el_end + 1, pos - el_end - 1);
litehtml::lcase(name);
attribute.name = _id(name);
m_attrs.push_back(attribute);
el_end = pos;
} else if(txt[el_end] == '#')
{
css_attribute_selector attribute;
attribute.type = select_id;
string::size_type pos = txt.find_first_of(".#[:", el_end + 1);
string name = txt.substr(el_end + 1, pos - el_end - 1);
litehtml::lcase(name);
attribute.name = _id(name);
m_attrs.push_back(attribute);
el_end = pos;
} else if(txt[el_end] == ':')
{
css_attribute_selector attribute;
if(txt[el_end + 1] == ':')
{
attribute.type = select_pseudo_element;
string::size_type pos = txt.find_first_of(".#[:", el_end + 2);
string name = txt.substr(el_end + 2, pos - el_end - 2);
litehtml::lcase(name);
attribute.name = _id(name);
m_attrs.push_back(attribute);
el_end = pos;
} else
{
string::size_type pos = txt.find_first_of(".#[:(", el_end + 1);
string name = txt.substr(el_end + 1, pos - el_end - 1);
lcase(name);
attribute.name = _id(name);
if(attribute.name == _after_ || attribute.name == _before_)
{
attribute.type = select_pseudo_element;
} else
{
attribute.type = select_pseudo_class;
}
string val;
if(pos != string::npos && txt.at(pos) == '(')
{
auto end = find_close_bracket(txt, pos);
val = txt.substr(pos + 1, end - pos - 1);
if (end != string::npos) pos = end + 1;
}
switch (attribute.name)
{
case _nth_child_:
case _nth_of_type_:
case _nth_last_child_:
case _nth_last_of_type_:
lcase(val);
parse_nth_child_params(val, attribute.a, attribute.b);
break;
case _not_:
attribute.sel = std::make_shared();
attribute.sel->parse(val);
break;
case _lang_:
trim(val);
lcase(val);
attribute.val = val;
break;
default:
break;
}
m_attrs.push_back(attribute);
el_end = pos;
}
} else if(txt[el_end] == '[')
{
css_attribute_selector attribute;
string::size_type pos = txt.find_first_of("]~=|$*^", el_end + 1);
string attr = txt.substr(el_end + 1, pos - el_end - 1);
trim(attr);
litehtml::lcase(attr);
if(pos != string::npos)
{
if(txt[pos] == ']')
{
attribute.type = select_exists;
} else if(txt[pos] == '=')
{
attribute.type = select_equal;
pos++;
} else if(txt.substr(pos, 2) == "~=")
{
attribute.type = select_contain_str;
pos += 2;
} else if(txt.substr(pos, 2) == "|=")
{
attribute.type = select_start_str;
pos += 2;
} else if(txt.substr(pos, 2) == "^=")
{
attribute.type = select_start_str;
pos += 2;
} else if(txt.substr(pos, 2) == "$=")
{
attribute.type = select_end_str;
pos += 2;
} else if(txt.substr(pos, 2) == "*=")
{
attribute.type = select_contain_str;
pos += 2;
} else
{
attribute.type = select_exists;
pos += 1;
}
pos = txt.find_first_not_of(" \t", pos);
if(pos != string::npos)
{
if(txt[pos] == '"')
{
string::size_type pos2 = txt.find_first_of('\"', pos + 1);
attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1));
pos = pos2 == string::npos ? pos2 : (pos2 + 1);
} else if(txt[pos] == '\'')
{
string::size_type pos2 = txt.find_first_of('\'', pos + 1);
attribute.val = txt.substr(pos + 1, pos2 == string::npos ? pos2 : (pos2 - pos - 1));
pos = pos2 == string::npos ? pos2 : (pos2 + 1);
} else if(txt[pos] == ']')
{
pos ++;
} else
{
string::size_type pos2 = txt.find_first_of(']', pos + 1);
attribute.val = txt.substr(pos, pos2 == string::npos ? pos2 : (pos2 - pos));
trim(attribute.val);
pos = pos2 == string::npos ? pos2 : (pos2 + 1);
}
}
} else
{
attribute.type = select_exists;
}
attribute.name = _id(attr);
m_attrs.push_back(attribute);
el_end = pos;
} else
{
el_end++;
}
el_end = txt.find_first_of(".#[:", el_end);
}
}
bool litehtml::css_selector::parse( const string& text )
{
if(text.empty())
{
return false;
}
string_vector tokens;
split_string(text, tokens, "", " \t>+~", "([");
if(tokens.empty())
{
return false;
}
string left;
string right = tokens.back();
char combinator = 0;
tokens.pop_back();
while(!tokens.empty() && (tokens.back() == " " || tokens.back() == "\t" || tokens.back() == "+" || tokens.back() == "~" || tokens.back() == ">"))
{
if(combinator == ' ' || combinator == 0)
{
combinator = tokens.back()[0];
}
tokens.pop_back();
}
for(const auto & token : tokens)
{
left += token;
}
trim(left);
trim(right);
if(right.empty())
{
return false;
}
m_right.parse(right);
switch(combinator)
{
case '>':
m_combinator = combinator_child;
break;
case '+':
m_combinator = combinator_adjacent_sibling;
break;
case '~':
m_combinator = combinator_general_sibling;
break;
default:
m_combinator = combinator_descendant;
break;
}
m_left = nullptr;
if(!left.empty())
{
m_left = std::make_shared();
if(!m_left->parse(left))
{
return false;
}
}
return true;
}
void litehtml::css_selector::calc_specificity()
{
if(m_right.m_tag != star_id)
{
m_specificity.d = 1;
}
for(const auto& attr : m_right.m_attrs)
{
if(attr.type == select_id)
{
m_specificity.b++;
} else
{
m_specificity.c++;
}
}
if(m_left)
{
m_left->calc_specificity();
m_specificity += m_left->m_specificity;
}
}
void litehtml::css_selector::add_media_to_doc( document* doc ) const
{
if(m_media_query && doc)
{
doc->add_media_list(m_media_query);
}
}