use crate::constants::{DEF_NODES_LEN, PRIORITY_PSEUDO_SELECTOR};
use crate::interface::{BoxDynElement, Elements, INodeType};
use crate::selector::pattern::Nth;
use crate::selector::rule::{Matcher, MatcherData, Rule, RuleDefItem, RuleItem};
use std::cmp::Ordering;
use std::{collections::HashMap, ops::Range};
const PRIORITY: u32 = PRIORITY_PSEUDO_SELECTOR;
fn pseudo_empty(rules: &mut Vec<RuleItem>) {
let selector = ":empty";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_: MatcherData| Matcher {
one_handle: Some(Box::new(|ele: &BoxDynElement, _| {
let child_nodes = ele.child_nodes();
if child_nodes.is_empty() {
return true;
}
let mut only_comments = true;
for node in child_nodes {
match node.node_type() {
INodeType::Comment => continue,
_ => {
only_comments = false;
break;
}
}
}
only_comments
})),
..Default::default()
}),
);
rules.push(rule.into());
}
struct SiblingsNodeData<'a> {
range: Range<usize>,
allow_indexs: Option<Vec<usize>>,
parent: Option<BoxDynElement<'a>>,
}
fn group_siblings_then_done<T, F>(eles: &Elements, allow_indexs_fn: T, mut cb: F)
where
T: Fn(usize) -> Option<Vec<usize>>,
F: FnMut(&mut SiblingsNodeData),
{
let mut data = SiblingsNodeData {
range: 0..0,
allow_indexs: None,
parent: None,
};
for (index, ele) in eles.get_ref().iter().enumerate() {
if let Some(parent) = ele.parent() {
let mut is_first = false;
let mut in_next_group = false;
if let Some(prev_parent) = &data.parent {
if parent.is(&prev_parent) {
data.range.end = index + 1;
} else {
in_next_group = true;
}
} else {
is_first = true;
}
if in_next_group {
cb(&mut data);
}
if is_first || in_next_group {
data.range.start = index;
data.range.end = index + 1;
data.parent = Some(parent.cloned());
data.allow_indexs = allow_indexs_fn(parent.children().length());
}
}
}
if !data.range.is_empty() {
cb(&mut data);
}
}
fn make_asc_or_desc_nth_child(selector: &'static str, asc: bool) -> RuleDefItem {
let handle = if asc {
|eles: &Elements,
range: &Range<usize>,
allow_indexs: &[usize],
childs: &Elements|
-> Vec<BoxDynElement> {
let childs = childs.get_ref();
let mut finded: Vec<BoxDynElement> = Vec::with_capacity(allow_indexs.len());
if range.len() == childs.len() {
for &index in allow_indexs {
finded.push(childs[index].cloned());
}
} else {
let eles = eles.get_ref();
let siblings = &eles[range.start..range.end];
let mut start_index = 0;
let mut allow_start_index = 0;
let allow_indexs_total = allow_indexs.len();
'loop_sibling: for ele in siblings {
for (index, child) in childs[start_index..].iter().enumerate() {
if child.is(ele) {
let actual_index = start_index + index;
for &allow_index in &allow_indexs[allow_start_index..] {
match allow_index.cmp(&actual_index) {
Ordering::Equal => {
finded.push(ele.cloned());
allow_start_index += 1;
break;
}
Ordering::Less => {
allow_start_index += 1;
}
Ordering::Greater => {
break;
}
}
if allow_start_index >= allow_indexs_total {
break 'loop_sibling;
}
}
start_index = actual_index + 1;
break;
}
}
}
}
finded
}
} else {
|eles: &Elements, range: &Range<usize>, allow_indexs: &[usize], childs: &Elements| {
let childs = childs.get_ref();
let total = childs.len();
let mut finded: Vec<BoxDynElement> = Vec::with_capacity(allow_indexs.len());
if range.len() == total {
for &index in allow_indexs.iter().rev() {
finded.push(childs[total - index - 1].cloned());
}
} else {
let eles = eles.get_ref();
let siblings = &eles[range.start..range.end];
let mut cur_end = range.len();
for (index, child) in childs.iter().rev().enumerate() {
let last_index = total - index - 1;
if allow_indexs.binary_search(&last_index).is_err() {
continue;
}
for (i, ele) in siblings[..cur_end].iter().rev().enumerate() {
if child.is(ele) {
cur_end -= i + 1;
finded.push(ele.cloned());
break;
}
}
if cur_end == 0 {
break;
}
}
finded.reverse();
}
finded
}
};
let name = if asc { ":nth-child" } else { ":nth-last-child" };
RuleDefItem(
name,
selector,
PRIORITY,
vec![("nth", 0)],
Box::new(move |data: MatcherData| {
let n = Rule::param(&data, ("nth", 0, "n"));
let index = Rule::param(&data, ("nth", 0, "index"));
Matcher {
all_handle: Some(Box::new(move |eles: &Elements, _| {
let mut result: Elements = Elements::with_capacity(DEF_NODES_LEN);
group_siblings_then_done(
eles,
|total: usize| Some(Nth::get_allowed_indexs(n, index, total)),
|data: &mut SiblingsNodeData| {
let allow_indexs = data.allow_indexs.as_ref().expect("allow indexs must set");
if allow_indexs.is_empty() {
return;
}
let childs = data
.parent
.as_ref()
.expect("parent must set in callback")
.children();
let finded = handle(&eles, &data.range, &allow_indexs, &childs);
if !finded.is_empty() {
result.get_mut_ref().extend(finded);
}
},
);
result
})),
..Default::default()
}
}),
)
}
fn pseudo_nth_child(rules: &mut Vec<RuleItem>) {
let rule = make_asc_or_desc_nth_child(":nth-child({spaces}{nth}{spaces})", true);
rules.push(rule.into());
}
fn pseudo_nth_last_child(rules: &mut Vec<RuleItem>) {
let rule = make_asc_or_desc_nth_child(":nth-last-child({spaces}{nth}{spaces})", false);
rules.push(rule.into());
}
fn pseudo_first_child(rules: &mut Vec<RuleItem>) {
let selector = ":first-child";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias(":nth-child(1)")),
);
rules.push(rule.into());
}
fn pseudo_last_child(rules: &mut Vec<RuleItem>) {
let selector = ":last-child";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias(":nth-last-child(1)")),
);
rules.push(rule.into());
}
type NameCounterHashMap = HashMap<String, usize>;
fn get_allowed_name_ele(
ele: &BoxDynElement,
names: &mut NameCounterHashMap,
allow_indexs: &[usize],
node_indexs: &mut Vec<usize>,
) {
let name = ele.tag_name();
if let Some(index) = names.get_mut(name) {
*index += 1;
if allow_indexs.binary_search(index).is_ok() {
node_indexs.push(ele.index())
}
} else {
let index = 0;
names.insert(String::from(name), index);
if allow_indexs[0] == 0 {
node_indexs.push(ele.index())
}
}
}
fn collect_avail_name_eles(
node_indexs: &mut Vec<usize>,
siblings: &[BoxDynElement],
finded: &mut Vec<BoxDynElement>,
) {
let av_total = node_indexs.len();
if av_total > 0 {
let mut av_index = 0;
let sib_total = siblings.len();
let mut sib_index = 0;
while av_index < av_total && sib_index < sib_total {
let cur_avail_index = node_indexs[av_index];
av_index += 1;
for ele in &siblings[sib_index..] {
let cur_sib_index = ele.index();
match cur_sib_index.cmp(&cur_avail_index) {
Ordering::Equal => {
finded.push(ele.cloned());
sib_index += 1;
break;
}
Ordering::Greater => {
break;
}
Ordering::Less => {
sib_index += 1;
}
}
}
}
}
}
fn make_asc_or_desc_nth_of_type(selector: &'static str, asc: bool) -> RuleDefItem {
let name = if asc {
":nth-of-type"
} else {
":nth-last-of-type"
};
RuleDefItem(
name,
selector,
PRIORITY,
vec![("nth", 0)],
Box::new(move |data: MatcherData| {
let n = Rule::param(&data, ("nth", 0, "n"));
let index = Rule::param(&data, ("nth", 0, "index"));
Matcher {
all_handle: Some(Box::new(move |eles: &Elements, _| {
let mut result: Elements = Elements::with_capacity(DEF_NODES_LEN);
group_siblings_then_done(
eles,
|total: usize| Some(Nth::get_allowed_indexs(n, index, total)),
|data: &mut SiblingsNodeData| {
let allow_indexs = data
.allow_indexs
.as_ref()
.expect("Nth allow indexs must have");
if allow_indexs.is_empty() {
return;
}
let childs = data
.parent
.as_ref()
.expect("parent must set in callback")
.children();
let mut names: NameCounterHashMap = HashMap::with_capacity(5);
let range = &data.range;
let eles = eles.get_ref();
let siblings = &eles[range.start..range.end];
let mut node_indexs: Vec<usize> = Vec::with_capacity(childs.length());
if asc {
for child in childs.get_ref() {
get_allowed_name_ele(child, &mut names, allow_indexs, &mut node_indexs);
}
} else {
for child in childs.get_ref().iter().rev() {
get_allowed_name_ele(child, &mut names, allow_indexs, &mut node_indexs);
}
node_indexs.reverse();
}
collect_avail_name_eles(&mut node_indexs, siblings, result.get_mut_ref());
},
);
result
})),
..Default::default()
}
}),
)
}
fn pseudo_nth_of_type(rules: &mut Vec<RuleItem>) {
let rule = make_asc_or_desc_nth_of_type(":nth-of-type({spaces}{nth}{spaces})", true);
rules.push(rule.into());
}
fn pseudo_nth_last_of_type(rules: &mut Vec<RuleItem>) {
let rule = make_asc_or_desc_nth_of_type(":nth-last-of-type({spaces}{nth}{spaces})", false);
rules.push(rule.into());
}
fn pseudo_first_of_type(rules: &mut Vec<RuleItem>) {
let selector = ":first-of-type";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias(":nth-of-type(1)")),
);
rules.push(rule.into());
}
fn pseudo_last_of_type(rules: &mut Vec<RuleItem>) {
let selector = ":last-of-type";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias(":nth-last-of-type(1)")),
);
rules.push(rule.into());
}
fn pseudo_only_child(rules: &mut Vec<RuleItem>) {
let selector = ":only-child";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(move |_| Matcher {
all_handle: Some(Box::new(|eles: &Elements, _| {
let mut result = Elements::with_capacity(DEF_NODES_LEN);
let mut prev_parent: Option<BoxDynElement> = None;
for ele in eles.get_ref() {
if let Some(parent) = &ele.parent() {
if let Some(prev_parent) = &prev_parent {
if prev_parent.is(parent) {
continue;
}
}
prev_parent = Some(parent.cloned());
let child_nodes = parent.child_nodes();
let mut count = 0;
for node in &child_nodes {
if matches!(node.node_type(), INodeType::Element) {
count += 1;
if count > 1 {
break;
}
}
}
if count == 1 {
result.push(ele.cloned());
}
}
}
result
})),
..Default::default()
}),
);
rules.push(rule.into());
}
fn pseudo_only_of_type(rules: &mut Vec<RuleItem>) {
let selector = ":only-of-type";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(move |_| Matcher {
all_handle: Some(Box::new(|eles: &Elements, _| {
let mut result = Elements::with_capacity(DEF_NODES_LEN);
group_siblings_then_done(
eles,
|_| None,
|data: &mut SiblingsNodeData| {
let childs = data
.parent
.as_ref()
.expect("parent must set in callback")
.children();
let eles = eles.get_ref();
let range = &data.range;
let siblings = &eles[range.start..range.end];
let mut only_names: Vec<(String, usize)> = Vec::with_capacity(DEF_NODES_LEN);
let mut repeated: Vec<String> = Vec::with_capacity(DEF_NODES_LEN);
for (index, child) in childs.get_ref().iter().enumerate() {
let name = String::from(child.tag_name());
if !repeated.contains(&name) {
let find_index = only_names
.iter()
.position(|(tag_name, _)| tag_name == &name);
if let Some(index) = find_index {
repeated.push(name);
only_names.remove(index);
} else {
only_names.push((name, index));
}
}
}
if !only_names.is_empty() {
let finded = result.get_mut_ref();
if siblings.len() == childs.length() {
for (_, index) in &only_names {
finded.push(siblings[*index].cloned());
}
} else {
let mut cur_index = 0;
for (name, _) in &only_names {
for (index, ele) in siblings[cur_index..].iter().enumerate() {
if ele.tag_name() == name {
cur_index += index + 1;
finded.push(ele.cloned());
break;
}
}
}
}
}
},
);
result
})),
..Default::default()
}),
);
rules.push(rule.into());
}
fn pseudo_not(rules: &mut Vec<RuleItem>) {
let name = ":not";
let selector = ":not({spaces}{selector}{spaces})";
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![("selector", 0)],
Box::new(|data: MatcherData| {
let selector = Rule::param(&data, "selector").expect("selector param must have.");
Matcher {
all_handle: Some(Box::new(move |eles: &Elements, _| eles.not(selector))),
..Default::default()
}
}),
);
rules.push(rule.into());
}
fn pseudo_contains(rules: &mut Vec<RuleItem>) {
let name = ":contains";
let selector =
r##":contains({spaces}{regexp#(?:'((?:\\?+.)*?)'|"((?:\\?+.)*?)"|([^\s'"<>/=`]*))#}{spaces})"##;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![("regexp", 0)],
Box::new(|data: MatcherData| {
let search = Rule::param(&data, ("regexp", 0, "1"))
.or_else(|| Rule::param(&data, ("regexp", 0, "2")))
.or_else(|| Rule::param(&data, ("regexp", 0, "3")))
.expect("The :contains selector must have a content");
Matcher {
one_handle: Some(Box::new(move |ele: &BoxDynElement, _| {
if search.is_empty() {
return true;
}
ele.text().contains(search)
})),
..Default::default()
}
}),
);
rules.push(rule.into());
}
fn pseudo_alias_header(rules: &mut Vec<RuleItem>) {
let selector = ":header";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias("h1,h2,h3,h4,h5,h6")),
);
rules.push(rule.into());
}
fn pseudo_alias_input(rules: &mut Vec<RuleItem>) {
let selector = ":input";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias("input,select,textarea,button")),
);
rules.push(rule.into());
}
fn pseudo_alias_submit(rules: &mut Vec<RuleItem>) {
let selector = ":submit";
let name = selector;
let rule = RuleDefItem(
name,
selector,
PRIORITY,
vec![],
Box::new(|_| Rule::make_alias("input[type='submit'],button[type='submit']")),
);
rules.push(rule.into());
}
pub fn init(rules: &mut Vec<RuleItem>) {
pseudo_empty(rules);
pseudo_first_child(rules);
pseudo_last_child(rules);
pseudo_only_child(rules);
pseudo_nth_child(rules);
pseudo_nth_last_child(rules);
pseudo_first_of_type(rules);
pseudo_last_of_type(rules);
pseudo_nth_of_type(rules);
pseudo_nth_last_of_type(rules);
pseudo_only_of_type(rules);
pseudo_not(rules);
pseudo_contains(rules);
pseudo_alias_header(rules);
pseudo_alias_input(rules);
pseudo_alias_submit(rules);
}