Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b31769b

Browse files
committed
merged branch lazyhammer/issue-8068-2.1 (PR #8073)
This PR was merged into the 2.1 branch. Discussion ---------- [CssSelector] Fix :nth-last-child() translation | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #8068 | License | MIT | Doc PR | n/a Same as #8072, but for < 2.3 Commits ------- 1469953 [CssSelector] Fix :nth-last-child() translation
2 parents 419f0f0 + 1469953 commit b31769b

File tree

3 files changed

+32
-29
lines changed

3 files changed

+32
-29
lines changed

src/Symfony/Component/CssSelector/Node/FunctionNode.php

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -100,43 +100,46 @@ protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest =
100100
$xpath->addStarPrefix();
101101
if ($a == 0) {
102102
if ($last) {
103-
$b = sprintf('last() - %s', $b);
103+
$b = sprintf('last() - %s', $b - 1);
104104
}
105105
$xpath->addCondition(sprintf('position() = %s', $b));
106106

107107
return $xpath;
108108
}
109109

110-
if ($last) {
111-
// FIXME: I'm not sure if this is right
112-
$a = -$a;
113-
$b = -$b;
114-
}
110+
if ($a < 0) {
111+
if ($b < 1) {
112+
$xpath->addCondition('false()');
115113

116-
if ($b > 0) {
117-
$bNeg = -$b;
114+
return $xpath;
115+
}
116+
117+
$sign = '<=';
118118
} else {
119-
$bNeg = sprintf('+%s', -$b);
119+
$sign = '>=';
120120
}
121121

122-
if ($a != 1) {
123-
$expr = array(sprintf('(position() %s) mod %s = 0', $bNeg, $a));
124-
} else {
125-
$expr = array();
122+
$expr = 'position()';
123+
124+
if ($last) {
125+
$expr = 'last() - '.$expr;
126+
$b--;
126127
}
127128

128-
if ($b >= 0) {
129-
$expr[] = sprintf('position() >= %s', $b);
130-
} elseif ($b < 0 && $last) {
131-
$expr[] = sprintf('position() < (last() %s)', $b);
129+
if (0 !== $b) {
130+
$expr .= ' - '.$b;
132131
}
133-
$expr = implode($expr, ' and ');
132+
133+
$conditions = array(sprintf('%s %s 0', $expr, $sign));
134134

135-
if ($expr) {
136-
$xpath->addCondition($expr);
135+
if (1 !== $a && -1 !== $a) {
136+
$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
137137
}
138138

139+
$xpath->addCondition(implode(' and ', $conditions));
140+
139141
return $xpath;
142+
140143
/* FIXME: handle an+b, odd, even
141144
an+b means every-a, plus b, e.g., 2n+1 means odd
142145
0n+b means b

src/Symfony/Component/CssSelector/Tests/CssSelectorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function getCssSelectors()
5454
array('h1', "h1"),
5555
array('foo|h1', "foo:h1"),
5656
array('h1, h2, h3', "h1 | h2 | h3"),
57-
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]"),
57+
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"),
5858
array('h1 > p', "h1/p"),
5959
array('h1#foo', "h1[@id = 'foo']"),
6060
array('h1.foo', "h1[contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"),

src/Symfony/Component/CssSelector/Tests/Node/FunctionNodeTest.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ public function testToXpath()
3636
// h1:nth-child(odd)
3737
$element2 = new ElementNode('*', new Token('Symbol', 'odd', -1));
3838
$function = new FunctionNode($element, ':', 'nth-child', $element2);
39-
$this->assertEquals("*/*[name() = 'h1' and ((position() -1) mod 2 = 0 and position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
39+
$this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
4040

4141
// h1:nth-child(even)
4242
$element2 = new ElementNode('*', new Token('Symbol', 'even', -1));
4343
$function = new FunctionNode($element, ':', 'nth-child', $element2);
44-
$this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod 2 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
44+
$this->assertEquals("*/*[name() = 'h1' and (position() >= 0 and (position()) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
4545

4646
// h1:nth-child(n)
4747
$element2 = new ElementNode('*', new Token('Symbol', 'n', -1));
@@ -51,12 +51,12 @@ public function testToXpath()
5151
// h1:nth-child(3n+1)
5252
$element2 = new ElementNode('*', new Token('Symbol', '3n+1', -1));
5353
$function = new FunctionNode($element, ':', 'nth-child', $element2);
54-
$this->assertEquals("*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
54+
$this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
5555

5656
// h1:nth-child(n+1)
5757
$element2 = new ElementNode('*', new Token('Symbol', 'n+1', -1));
5858
$function = new FunctionNode($element, ':', 'nth-child', $element2);
59-
$this->assertEquals("*/*[name() = 'h1' and (position() >= 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
59+
$this->assertEquals("*/*[name() = 'h1' and (position() - 1 >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
6060

6161
// h1:nth-child(1)
6262
$element2 = new ElementNode('*', new Token('Symbol', '2', -1));
@@ -66,24 +66,24 @@ public function testToXpath()
6666
// h1:nth-child(2n)
6767
$element2 = new ElementNode('*', new Token('Symbol', '2n', -1));
6868
$function = new FunctionNode($element, ':', 'nth-child', $element2);
69-
$this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod 2 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
69+
$this->assertEquals("*/*[name() = 'h1' and (position() >= 0 and (position()) mod 2 = 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
7070

7171
// h1:nth-child(-n)
7272
$element2 = new ElementNode('*', new Token('Symbol', '-n', -1));
7373
$function = new FunctionNode($element, ':', 'nth-child', $element2);
74-
$this->assertEquals("*/*[name() = 'h1' and ((position() +0) mod -1 = 0 and position() >= 0)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
74+
$this->assertEquals("*/*[name() = 'h1' and (false())]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
7575

7676
// h1:nth-last-child(2)
7777
$function = new FunctionNode($element, ':', 'nth-last-child', 2);
78-
$this->assertEquals("*/*[name() = 'h1' and (position() = last() - 2)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
78+
$this->assertEquals("*/*[name() = 'h1' and (position() = last() - 1)]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
7979

8080
// h1:nth-of-type(2)
8181
$function = new FunctionNode($element, ':', 'nth-of-type', 2);
8282
$this->assertEquals("*/h1[position() = 2]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
8383

8484
// h1:nth-last-of-type(2)
8585
$function = new FunctionNode($element, ':', 'nth-last-of-type', 2);
86-
$this->assertEquals("*/h1[position() = last() - 2]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
86+
$this->assertEquals("*/h1[position() = last() - 1]", (string) $function->toXpath(), '->toXpath() returns the xpath representation of the node');
8787

8888
/*
8989
// h1:not(p)

0 commit comments

Comments
 (0)