Skip to content

Commit

Permalink
Add pseudo class "has" and more attribute options
Browse files Browse the repository at this point in the history
  • Loading branch information
Imangazaliev committed Jul 24, 2016
1 parent fd30e82 commit 656263c
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/DiDom/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@ protected static function convertAttribute($name, $value)
return $value === null ? $xpath : sprintf('%s="%s"', $xpath, $value);
}

if (substr($name, 0, 1) === '!') {
$xpath = sprintf('not(@%s)', substr($name, 1));

return $xpath;
}

switch (substr($name, -1)) {
case '^':
$xpath = sprintf('starts-with(@%s, "%s")', substr($name, 0, -1), $value);
Expand All @@ -220,6 +226,9 @@ protected static function convertAttribute($name, $value)
case '*':
$xpath = sprintf('contains(@%s, "%s")', substr($name, 0, -1), $value);
break;
case '!':
$xpath = sprintf('not(@%s="%s")', substr($name, 0, -1), $value);
break;
default:
// if specified only the attribute name
$xpath = $value === null ? '@'.$name : sprintf('@%s="%s"', $name, $value);
Expand Down Expand Up @@ -263,6 +272,9 @@ protected static function convertPseudo($pseudo, $parameters = [])

return self::convertContains($string, $caseSensetive);
break;
case 'has':
return self::cssToXpath($parameters[0], './/');
break;
}

throw new RuntimeException('Invalid selector: unknown pseudo-class');
Expand Down
14 changes: 14 additions & 0 deletions tests/DiDom/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public function compileCssTests()
['a[href^=https]', '//a[starts-with(@href, "https")]'],
['img[src$=png]', '//img[ends-with(@src, "png")]'],
['a[href*=exapmle.com]', '//a[contains(@href, "exapmle.com")]'],
['script[!src]', '//script[not(@src)]'],
['a[href!="http://foo.com/"]', '//a[not(@href="http://foo.com/")]'],
['foo bar baz', '//foo//bar//baz'],
['foo > bar > baz', '//foo/bar/baz'],
['input, textarea, select', '//input|//textarea|//select'],
Expand All @@ -153,6 +155,7 @@ public function compileCssTests()
['li:nth-child(3n-1)', '//li[(position() + 1) mod 3 = 0 and position() >= 1]'],
['li:nth-child(n+3)', '//li[(position() - 3) mod 1 = 0 and position() >= 3]'],
['li:nth-child(n-3)', '//li[(position() + 3) mod 1 = 0 and position() >= 3]'],
['ul:has(li.item)', '//ul[.//li[contains(concat(" ", normalize-space(@class), " "), " item ")]]'],
['ul li a::text', '//ul//li//a/text()'],
['ul li a::attr(href)', '//ul//li//a/@*[name() = "href"]'],
['ul li a::attr(href|title)', '//ul//li//a/@*[name() = "href" or name() = "title"]'],
Expand Down Expand Up @@ -186,6 +189,11 @@ public function buildXpathTests()
'//a[@href]',
'//a[@href="http://example.com/"]',
'//a[(@href="http://example.com/") and (@title="Example Domain")]',
'//a[(@target="_blank") and (starts-with(@href, "https"))]',
'//a[ends-with(@href, ".com")]',
'//a[contains(@href, "example")]',
'//a[not(@href="http://foo.com/")]',
'//script[not(@src)]',
'//li[position() = 1]',
'//*[(@id="id") and (contains(concat(" ", normalize-space(@class), " "), " foo ")) and (@name="value") and (position() = 1)]',
];
Expand All @@ -199,6 +207,11 @@ public function buildXpathTests()
['tag' => 'a', 'attributes' => ['href' => null]],
['tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
['tag' => 'a', 'attributes' => ['href' => 'http://example.com/', 'title' => 'Example Domain']], //
['tag' => 'a', 'attributes' => ['target' => '_blank', 'href^' => 'https']],
['tag' => 'a', 'attributes' => ['href$' => '.com']],
['tag' => 'a', 'attributes' => ['href*' => 'example']],
['tag' => 'a', 'attributes' => ['href!' => 'http://foo.com/']],
['tag' => 'script', 'attributes' => ['!src' => null]],
['tag' => 'li', 'pseudo' => 'first-child'],
['tag' => '*', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
];
Expand Down Expand Up @@ -226,6 +239,7 @@ public function getSegmentsTests()
['selector' => 'a[href=\'http://example.com/\']', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/']],
['selector' => 'a[href=http://example.com/][title=Example Domain]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/', 'title' => 'Example Domain']],
['selector' => 'a[href=http://example.com/][href=http://example.com/404]', 'tag' => 'a', 'attributes' => ['href' => 'http://example.com/404']],
['selector' => 'a[href^=https]', 'tag' => 'a', 'attributes' => ['href^' => 'https']],
['selector' => 'li:first-child', 'tag' => 'li', 'pseudo' => 'first-child'],
['selector' => 'ul >', 'tag' => 'ul', 'rel' => '>'],
['selector' => '#id.foo[name=value]:first-child >', 'tag' => '*', 'id' => 'id', 'classes' => ['foo'], 'attributes' => ['name' => 'value'], 'pseudo' => 'first-child', 'rel' => '>'],
Expand Down

0 comments on commit 656263c

Please sign in to comment.