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

Skip to content

Commit 2d9027d

Browse files
committed
[CssSelector] Fix :nth-last-child() translation
1 parent c8b2e11 commit 2d9027d

File tree

3 files changed

+51
-26
lines changed

3 files changed

+51
-26
lines changed

src/Symfony/Component/CssSelector/CssSelectorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function getCssToXPathWithoutPrefixTestData()
4545
array('h1', "h1"),
4646
array('foo|h1', "foo:h1"),
4747
array('h1, h2, h3', "h1 | h2 | h3"),
48-
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]"),
48+
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"),
4949
array('h1 > p', "h1/p"),
5050
array('h1#foo', "h1[@id = 'foo']"),
5151
array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"),

src/Symfony/Component/CssSelector/Tests/XPath/TranslatorTest.php

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,11 @@ public function getCssToXPathTestData()
101101
array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"),
102102
array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"),
103103
array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"),
104-
array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 1)]"),
105-
array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and ((position() +2) mod -2 = 0 and position() < (last() -2))]"),
104+
array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"),
105+
array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"),
106106
array('e:nth-of-type(1)', "*/e[position() = 1]"),
107-
array('e:nth-last-of-type(1)', "*/e[position() = last() - 1]"),
108-
array('e:nth-last-of-type(1)', "*/e[position() = last() - 1]"),
109-
array('div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 1]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"),
107+
array('e:nth-last-of-type(1)', "*/e[position() = last() - 0]"),
108+
array('div e:nth-last-of-type(1) .aclass', "div/descendant-or-self::*/e[position() = last() - 0]/descendant-or-self::*/*[@class and contains(concat(' ', normalize-space(@class), ' '), ' aclass ')]"),
110109
array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"),
111110
array('e:last-child', "*/*[name() = 'e' and (position() = last())]"),
112111
array('e:first-of-type', "*/e[position() = 1]"),
@@ -121,7 +120,7 @@ public function getCssToXPathTestData()
121120
array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"),
122121
array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"),
123122
array('e#myid', "e[@id = 'myid']"),
124-
array('e:not(:nth-child(odd))', "e[not((position() -1) mod 2 = 0 and position() >= 1)]"),
123+
array('e:not(:nth-child(odd))', "e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]"),
125124
array('e:nOT(*)', "e[0]"),
126125
array('e f', "e/descendant-or-self::*/f"),
127126
array('e > f', "e/f"),
@@ -188,17 +187,32 @@ public function getHtmlIdsTestData()
188187
array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
189188
array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
190189
array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')),
191-
// FIXME: I'm not 100% sure this is right:
192190
array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')),
193-
array('li:nth-last-child(0)', array('seventh-li')),
191+
array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
192+
array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
193+
array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
194+
array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
195+
array('li:nth-child(-n)', array()),
196+
array('li:nth-child(-n-1)', array()),
197+
array('li:nth-child(-n+1)', array('first-li')),
198+
array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')),
199+
array('li:nth-last-child(0)', array()),
194200
array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')),
195201
array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')),
196-
array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li')),
202+
array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')),
203+
array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
204+
array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
205+
array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
206+
array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
207+
array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')),
208+
array('li:nth-last-child(-n)', array()),
209+
array('li:nth-last-child(-n-1)', array()),
210+
array('li:nth-last-child(-n+1)', array('seventh-li')),
211+
array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')),
197212
array('ol:first-of-type', array('first-ol')),
198-
array('ol:nth-child(1)', array()),
213+
array('ol:nth-child(1)', array('first-ol')),
199214
array('ol:nth-of-type(2)', array('second-ol')),
200-
// FIXME: like above (1) or (2)?
201-
array('ol:nth-last-of-type(1)', array('first-ol')),
215+
array('ol:nth-last-of-type(1)', array('second-ol')),
202216
array('span:only-child', array('foobar-span')),
203217
array('li div:only-child', array('li-div')),
204218
array('div *:only-child', array('li-div', 'foobar-span')),

src/Symfony/Component/CssSelector/XPath/Extension/FunctionExtension.php

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,34 +67,45 @@ public function translateNthChild(XPathExpr $xpath, FunctionNode $function, $las
6767
}
6868

6969
if (0 === $a) {
70-
return $xpath->addCondition('position() = '.($last ? 'last() - '.$b : $b));
70+
return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
7171
}
7272

73+
if ($a < 0) {
74+
if ($b < 1) {
75+
return $xpath->addCondition('false()');
76+
}
77+
78+
$sign = '<=';
79+
} else {
80+
$sign = '>=';
81+
}
82+
83+
$expr = 'position()';
84+
7385
if ($last) {
74-
// todo: verify if this is right
75-
$a = - $a;
76-
$b = - $b;
86+
$expr = 'last() - '.$expr;
87+
$b--;
7788
}
7889

79-
$conditions = 1 === $a
80-
? array()
81-
: array(sprintf('(position() %s) mod %s = 0', $b > 0 ? (string) (- $b) : '+'.(- $b), $a));
90+
if (0 !== $b) {
91+
$expr .= ' - '.$b;
92+
}
93+
94+
$conditions = array(sprintf('%s %s 0', $expr, $sign));
8295

83-
if ($b >= 0) {
84-
$conditions[] = 'position() >= '.$b;
85-
} elseif ($last) {
86-
$conditions[] = sprintf('position() < (last() %s)', $b);
96+
if (1 !== $a && -1 !== $a) {
97+
$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
8798
}
8899

100+
return $xpath->addCondition(implode(' and ', $conditions));
101+
89102
// todo: handle an+b, odd, even
90103
// an+b means every-a, plus b, e.g., 2n+1 means odd
91104
// 0n+b means b
92105
// n+0 means a=1, i.e., all elements
93106
// an means every a elements, i.e., 2n means even
94107
// -n means -1n
95108
// -1n+6 means elements 6 and previous
96-
97-
return empty($conditions) ? $xpath : $xpath->addCondition(implode(' and ', $conditions));
98109
}
99110

100111
/**

0 commit comments

Comments
 (0)