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

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

Loading…
Cancel
Save