:has and :not selectors from css3+ (finally!)

This commit is contained in:
Adam D. Ruppe 2016-02-25 20:48:59 -05:00
parent 4c47231866
commit bb35915c57
1 changed files with 82 additions and 5 deletions

87
dom.d
View File

@ -4730,9 +4730,10 @@ int intFromHex(string hex) {
static immutable string[] selectorTokens = [ static immutable string[] selectorTokens = [
// It is important that the 2 character possibilities go first here for accurate lexing // It is important that the 2 character possibilities go first here for accurate lexing
"~=", "*=", "|=", "^=", "$=", "!=", // "::" should be there too for full standard "~=", "*=", "|=", "^=", "$=", "!=", // "::" should be there too for full standard
"::", ">>",
"<<", // my any-parent extension (reciprocal of whitespace) "<<", // my any-parent extension (reciprocal of whitespace)
// " - ", // previous-sibling extension (whitespace required to disambiguate tag-names) // " - ", // previous-sibling extension (whitespace required to disambiguate tag-names)
".", ">", "+", "*", ":", "[", "]", "=", "\"", "#", ",", " ", "~", "<" ".", ">", "+", "*", ":", "[", "]", "=", "\"", "#", ",", " ", "~", "<", "(", ")"
]; // other is white space or a name. ]; // other is white space or a name.
///. ///.
@ -4902,10 +4903,14 @@ int intFromHex(string hex) {
string[2][] attributesInclude; /// [attr*=value] string[2][] attributesInclude; /// [attr*=value]
string[2][] attributesNotEqual; /// [attr!=value] -- extension by me string[2][] attributesNotEqual; /// [attr!=value] -- extension by me
string[] hasSelectors; /// :has(this)
string[] notSelectors; /// :not(this)
bool firstChild; ///. bool firstChild; ///.
bool lastChild; ///. bool lastChild; ///.
bool emptyElement; ///. bool emptyElement; ///.
bool whitespaceOnly; ///
bool oddChild; ///. bool oddChild; ///.
bool evenChild; ///. bool evenChild; ///.
@ -4941,9 +4946,13 @@ int intFromHex(string hex) {
foreach(a; attributesIncludesSeparatedByDashes) ret ~= "[" ~ a[0] ~ "|=\"" ~ a[1] ~ "\"]"; foreach(a; attributesIncludesSeparatedByDashes) ret ~= "[" ~ a[0] ~ "|=\"" ~ a[1] ~ "\"]";
foreach(a; attributesIncludesSeparatedBySpaces) ret ~= "[" ~ a[0] ~ "~=\"" ~ a[1] ~ "\"]"; foreach(a; attributesIncludesSeparatedBySpaces) ret ~= "[" ~ a[0] ~ "~=\"" ~ a[1] ~ "\"]";
foreach(a; notSelectors) ret ~= ":not(" ~ a ~ ")";
foreach(a; hasSelectors) ret ~= ":has(" ~ a ~ ")";
if(firstChild) ret ~= ":first-child"; if(firstChild) ret ~= ":first-child";
if(lastChild) ret ~= ":last-child"; if(lastChild) ret ~= ":last-child";
if(emptyElement) ret ~= ":empty"; if(emptyElement) ret ~= ":empty";
if(whitespaceOnly) ret ~= ":whitespace-only";
if(oddChild) ret ~= ":odd-child"; if(oddChild) ret ~= ":odd-child";
if(evenChild) ret ~= ":even-child"; if(evenChild) ret ~= ":even-child";
if(rootElement) ret ~= ":root"; if(rootElement) ret ~= ":root";
@ -4979,6 +4988,10 @@ int intFromHex(string hex) {
if(e.children.length) if(e.children.length)
return false; return false;
} }
if(whitespaceOnly) {
if(e.innerText.strip.length)
return false;
}
if(rootElement) { if(rootElement) {
if(e.parentNode !is null) if(e.parentNode !is null)
return false; return false;
@ -5033,6 +5046,17 @@ int intFromHex(string hex) {
foreach(a; attributesIncludesSeparatedByDashes) foreach(a; attributesIncludesSeparatedByDashes)
if(a[0] !in e.attributes || !matchWithSeparator(e.attributes[a[0]], a[1], "-")) if(a[0] !in e.attributes || !matchWithSeparator(e.attributes[a[0]], a[1], "-"))
return false; return false;
foreach(a; hasSelectors) {
if(e.querySelector(a) is null)
return false;
}
foreach(a; notSelectors) {
auto sels = parseSelectorString(a);
foreach(sel; sels)
foreach(part; sel.parts)
if(part.matchElement(e))
return false;
}
return true; return true;
} }
@ -5213,11 +5237,32 @@ int intFromHex(string hex) {
ReadingAttributeComparison, ReadingAttributeComparison,
ExpectingAttributeCloser, ExpectingAttributeCloser,
ReadingPseudoClass, ReadingPseudoClass,
ReadingAttributeValue ReadingAttributeValue,
SkippingFunctionalSelector,
} }
State state = State.Starting; State state = State.Starting;
string attributeName, attributeValue, attributeComparison; string attributeName, attributeValue, attributeComparison;
foreach(token; tokens) { int parensCount;
foreach(idx, token; tokens) {
string readFunctionalSelector() {
string s;
if(tokens[idx + 1] != "(")
throw new Exception("parse error");
int pc = 1;
foreach(t; tokens[idx + 2 .. $]) {
if(t == "(")
pc++;
if(t == ")")
pc--;
if(pc == 0)
break;
s ~= t;
}
return s;
}
sizediff_t tid = -1; sizediff_t tid = -1;
foreach(i, item; selectorTokens) foreach(i, item; selectorTokens)
if(token == item) { if(token == item) {
@ -5254,6 +5299,10 @@ int intFromHex(string hex) {
commit(); commit();
current.separation = 0; // tree current.separation = 0; // tree
break; break;
case ">>":
commit();
current.separation = 0; // alternate syntax for tree from html5 css
break;
case ">": case ">":
commit(); commit();
current.separation = 1; // child current.separation = 1; // child
@ -5280,6 +5329,7 @@ int intFromHex(string hex) {
state = State.ReadingId; state = State.ReadingId;
break; break;
case ":": case ":":
case "::":
state = State.ReadingPseudoClass; state = State.ReadingPseudoClass;
break; break;
@ -5312,13 +5362,23 @@ int intFromHex(string hex) {
// one with no children // one with no children
current.emptyElement = true; current.emptyElement = true;
break; break;
case "whitespace-only":
current.whitespaceOnly = true;
break;
case "link": case "link":
current.attributesPresent ~= "href"; current.attributesPresent ~= "href";
break; break;
case "root": case "root":
current.rootElement = true; current.rootElement = true;
break; break;
// FIXME: add :not() case "not":
state = State.SkippingFunctionalSelector;
current.notSelectors ~= readFunctionalSelector();
continue; // now the rest of the parser skips past the parens we just handled
case "has":
state = State.SkippingFunctionalSelector;
current.hasSelectors ~= readFunctionalSelector();
continue; // now the rest of the parser skips past the parens we just handled
// My extensions // My extensions
case "odd-child": case "odd-child":
current.oddChild = true; current.oddChild = true;
@ -5326,8 +5386,15 @@ int intFromHex(string hex) {
case "even-child": case "even-child":
current.evenChild = true; current.evenChild = true;
break; break;
// back to standards though not quite right lol
case "disabled":
current.attributesPresent ~= "disabled";
break;
case "checked":
current.attributesPresent ~= "checked";
break;
case "visited", "active", "hover", "target", "focus", "checked", "selected": case "visited", "active", "hover", "target", "focus", "selected":
current.attributesPresent ~= "nothing"; current.attributesPresent ~= "nothing";
// FIXME // FIXME
/* /*
@ -5350,6 +5417,16 @@ int intFromHex(string hex) {
} }
state = State.Starting; state = State.Starting;
break; break;
case State.SkippingFunctionalSelector:
if(token == "(") {
parensCount++;
} else if(token == ")") {
parensCount--;
}
if(parensCount == 0)
state = State.Starting;
break;
case State.ReadingAttributeSelector: case State.ReadingAttributeSelector:
attributeName = token; attributeName = token;
attributeComparison = null; attributeComparison = null;