mirror of https://github.com/adamdruppe/arsd.git
:has and :not selectors from css3+ (finally!)
This commit is contained in:
parent
4c47231866
commit
bb35915c57
87
dom.d
87
dom.d
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue