pull/5427/head
Tan Li Hau 5 years ago
parent 1469da4634
commit 3e40ce6e7f

@ -15,12 +15,8 @@ enum BlockAppliesToNode {
UnknownSelectorType
}
enum NodeExist {
Probably,
Definitely,
}
interface ElementAndExist {
element: Element;
exist: NodeExist;
Probably = 1,
Definitely = 2,
}
const whitelist_attribute_selector = new Map([
@ -212,21 +208,11 @@ function apply_selector(blocks: Block[], node: Element, to_encapsulate: any[]):
}
return false;
} else if (block.combinator.name === '+') {
const siblings = get_possible_element_siblings(node, true);
let has_match = false;
for (const possible_sibling of siblings) {
if (apply_selector(blocks.slice(), possible_sibling.element, to_encapsulate)) {
to_encapsulate.push({ node, block });
has_match = true;
}
}
return has_match;
} else if (block.combinator.name === '~') {
const siblings = get_possible_element_siblings(node, false);
} else if (block.combinator.name === '+' || block.combinator.name === '~') {
const siblings = get_possible_element_siblings(node, block.combinator.name === '+');
let has_match = false;
for (const possible_sibling of siblings) {
if (apply_selector(blocks.slice(), possible_sibling.element, to_encapsulate)) {
for (const possible_sibling of siblings.keys()) {
if (apply_selector(blocks.slice(), possible_sibling, to_encapsulate)) {
to_encapsulate.push({ node, block });
has_match = true;
}
@ -415,13 +401,13 @@ function get_element_parent(node: Element): Element | null {
return parent as Element | null;
}
function get_possible_element_siblings(node: INode, adjacent_only: boolean): ElementAndExist[] {
const result: ElementAndExist[] = [];
function get_possible_element_siblings(node: INode, adjacent_only: boolean): Map<Element, NodeExist> {
const result: Map<Element, NodeExist> = new Map();
let prev: INode = node;
while (prev = prev.prev) {
if (prev.type === 'Element') {
if (!prev.attributes.find(attr => attr.name.toLowerCase() === 'slot')) {
result.push({ element: prev as Element, exist: NodeExist.Definitely });
result.set(prev, NodeExist.Definitely);
}
if (adjacent_only) {
@ -429,8 +415,9 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele
}
} else if (prev.type === 'EachBlock' || prev.type === 'IfBlock' || prev.type === 'AwaitBlock') {
const possible_last_child = get_possible_last_child(prev, adjacent_only);
result.push(...possible_last_child);
if (adjacent_only && possible_last_child.find(child => child.exist === NodeExist.Definitely)) {
add_to_map(possible_last_child, result);
if (adjacent_only && has_definite_elements(possible_last_child)) {
return result;
}
}
@ -438,23 +425,24 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele
if (!prev || !adjacent_only) {
let parent: INode = node;
if (node.type === 'ElseBlock') {
parent = parent.parent;
}
let skip_each_for_last_child = node.type === 'ElseBlock';
while ((parent = parent.parent) && (parent.type === 'EachBlock' || parent.type === 'IfBlock' || parent.type === 'ElseBlock' || parent.type === 'AwaitBlock')) {
const possible_siblings = get_possible_element_siblings(parent, adjacent_only);
result.push(...possible_siblings);
add_to_map(possible_siblings, result);
if (parent.type === 'EachBlock') {
// first child of each block can select the last child of each block as previous sibling
for (const each_parent_last_child of get_possible_last_child(parent, adjacent_only)) {
result.push(each_parent_last_child);
if (skip_each_for_last_child) {
skip_each_for_last_child = false;
} else {
add_to_map(get_possible_last_child(parent, adjacent_only), result);
}
} else if (parent.type === 'ElseBlock') {
skip_each_for_last_child = true;
parent = parent.parent;
}
if (adjacent_only && possible_siblings.find(sibling => sibling.exist === NodeExist.Definitely)) {
if (adjacent_only && has_definite_elements(possible_siblings)) {
break;
}
}
@ -463,69 +451,95 @@ function get_possible_element_siblings(node: INode, adjacent_only: boolean): Ele
return result;
}
function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): ElementAndExist[] {
const result = [];
function get_possible_last_child(block: EachBlock | IfBlock | AwaitBlock, adjacent_only: boolean): Map<Element, NodeExist> {
const result: Map<Element, NodeExist> = new Map();
if (block.type === 'EachBlock') {
const each_result: ElementAndExist[] = loop_child(block.children, adjacent_only);
const else_result: ElementAndExist[] = block.else ? loop_child(block.else.children, adjacent_only) : [];
const each_result: Map<Element, NodeExist> = loop_child(block.children, adjacent_only);
const else_result: Map<Element, NodeExist> = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
const not_exhaustive =
else_result.length === 0 || !else_result.find(result => result.exist === NodeExist.Definitely);
const not_exhaustive = !has_definite_elements(else_result);
if (not_exhaustive) {
each_result.forEach(result => result.exist = NodeExist.Probably);
else_result.forEach(result => result.exist = NodeExist.Probably);
mark_as_probably(each_result);
mark_as_probably(else_result);
}
result.push(...each_result, ...else_result);
add_to_map(each_result, result);
add_to_map(else_result, result);
} else if (block.type === 'IfBlock') {
const if_result: ElementAndExist[] = loop_child(block.children, adjacent_only);
const else_result: ElementAndExist[] = block.else ? loop_child(block.else.children, adjacent_only) : [];
const if_result: Map<Element, NodeExist> = loop_child(block.children, adjacent_only);
const else_result: Map<Element, NodeExist> = block.else ? loop_child(block.else.children, adjacent_only) : new Map();
const not_exhaustive =
if_result.length === 0 || !if_result.find(result => result.exist === NodeExist.Definitely) ||
else_result.length === 0 || !else_result.find(result => result.exist === NodeExist.Definitely);
const not_exhaustive = !has_definite_elements(if_result) || !has_definite_elements(else_result);
if (not_exhaustive) {
if_result.forEach(result => result.exist = NodeExist.Probably);
else_result.forEach(result => result.exist = NodeExist.Probably);
mark_as_probably(if_result);
mark_as_probably(else_result);
}
result.push(...if_result, ...else_result);
add_to_map(if_result, result);
add_to_map(else_result, result);
} else if (block.type === 'AwaitBlock') {
const pending_result: ElementAndExist[] = block.pending ? loop_child(block.pending.children, adjacent_only) : [];
const then_result: ElementAndExist[] = block.then ? loop_child(block.then.children, adjacent_only) : [];
const catch_result: ElementAndExist[] = block.catch ? loop_child(block.catch.children, adjacent_only) : [];
const pending_result: Map<Element, NodeExist> = block.pending ? loop_child(block.pending.children, adjacent_only) : new Map();
const then_result: Map<Element, NodeExist> = block.then ? loop_child(block.then.children, adjacent_only) : new Map();
const catch_result: Map<Element, NodeExist> = block.catch ? loop_child(block.catch.children, adjacent_only) : new Map();
const not_exhaustive =
pending_result.length === 0 || !pending_result.find(result => result.exist === NodeExist.Definitely) ||
then_result.length === 0 || !then_result.find(result => result.exist === NodeExist.Definitely) ||
catch_result.length === 0 || !catch_result.find(result => result.exist === NodeExist.Definitely);
const not_exhaustive = !has_definite_elements(pending_result) || !has_definite_elements(then_result) || !has_definite_elements(catch_result);
if (not_exhaustive) {
pending_result.forEach(result => result.exist = NodeExist.Probably);
then_result.forEach(result => result.exist = NodeExist.Probably);
catch_result.forEach(result => result.exist = NodeExist.Probably);
mark_as_probably(pending_result);
mark_as_probably(then_result);
mark_as_probably(catch_result);
}
result.push(...pending_result,...then_result,...catch_result);
add_to_map(pending_result, result);
add_to_map(then_result, result);
add_to_map(catch_result, result);
}
return result;
}
function has_definite_elements(result: Map<Element, NodeExist>): boolean {
if (result.size === 0) return false;
for (const exist of result.values()) {
if (exist === NodeExist.Definitely) {
return true;
}
}
return false;
}
function add_to_map(from: Map<Element, NodeExist>, to: Map<Element, NodeExist>) {
from.forEach((exist, element) => {
to.set(element, higher_existance(exist, to.get(element)));
});
}
function higher_existance(exist1: NodeExist | null, exist2: NodeExist | null): NodeExist {
if (exist1 === undefined || exist2 === undefined) return exist1 || exist2;
return exist1 > exist2 ? exist1 : exist2;
}
function mark_as_probably(result: Map<Element, NodeExist>) {
for (const key of result.keys()) {
result.set(key, NodeExist.Probably);
}
}
function loop_child(children: INode[], adjacent_only: boolean) {
const result = [];
const result: Map<Element, NodeExist> = new Map();
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
if (child.type === 'Element') {
result.push({ element: child, exist: NodeExist.Definitely });
result.set(child, NodeExist.Definitely);
if (adjacent_only) {
break;
}
} else if (child.type === 'EachBlock' || child.type === 'IfBlock' || child.type === 'AwaitBlock') {
const child_result = get_possible_last_child(child, adjacent_only);
result.push(...child_result);
if (adjacent_only && child_result.find(child => child.exist === NodeExist.Definitely)) {
add_to_map(child_result, result);
if (adjacent_only && has_definite_elements(child_result)) {
break;
}
}

Loading…
Cancel
Save