[CssSelector] Fix :nth-last-child() translation
This commit is contained in:
parent
c8b2e111d2
commit
2d9027d9a2
@ -45,7 +45,7 @@ class CssSelectorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('h1', "h1"),
|
array('h1', "h1"),
|
||||||
array('foo|h1', "foo:h1"),
|
array('foo|h1', "foo:h1"),
|
||||||
array('h1, h2, h3', "h1 | h2 | h3"),
|
array('h1, h2, h3', "h1 | h2 | h3"),
|
||||||
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and ((position() -1) mod 3 = 0 and position() >= 1)]"),
|
array('h1:nth-child(3n+1)', "*/*[name() = 'h1' and (position() - 1 >= 0 and (position() - 1) mod 3 = 0)]"),
|
||||||
array('h1 > p', "h1/p"),
|
array('h1 > p', "h1/p"),
|
||||||
array('h1#foo', "h1[@id = 'foo']"),
|
array('h1#foo', "h1[@id = 'foo']"),
|
||||||
array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"),
|
array('h1.foo', "h1[@class and contains(concat(' ', normalize-space(@class), ' '), ' foo ')]"),
|
||||||
|
@ -101,12 +101,11 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"),
|
array('e[foo*="bar"]', "e[@foo and contains(@foo, 'bar')]"),
|
||||||
array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"),
|
array('e[hreflang|="en"]', "e[@hreflang and (@hreflang = 'en' or starts-with(@hreflang, 'en-'))]"),
|
||||||
array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"),
|
array('e:nth-child(1)', "*/*[name() = 'e' and (position() = 1)]"),
|
||||||
array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 1)]"),
|
array('e:nth-last-child(1)', "*/*[name() = 'e' and (position() = last() - 0)]"),
|
||||||
array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and ((position() +2) mod -2 = 0 and position() < (last() -2))]"),
|
array('e:nth-last-child(2n+2)', "*/*[name() = 'e' and (last() - position() - 1 >= 0 and (last() - position() - 1) mod 2 = 0)]"),
|
||||||
array('e:nth-of-type(1)', "*/e[position() = 1]"),
|
array('e:nth-of-type(1)', "*/e[position() = 1]"),
|
||||||
array('e:nth-last-of-type(1)', "*/e[position() = last() - 1]"),
|
array('e:nth-last-of-type(1)', "*/e[position() = last() - 0]"),
|
||||||
array('e:nth-last-of-type(1)', "*/e[position() = last() - 1]"),
|
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 ')]"),
|
||||||
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 ')]"),
|
|
||||||
array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"),
|
array('e:first-child', "*/*[name() = 'e' and (position() = 1)]"),
|
||||||
array('e:last-child', "*/*[name() = 'e' and (position() = last())]"),
|
array('e:last-child', "*/*[name() = 'e' and (position() = last())]"),
|
||||||
array('e:first-of-type', "*/e[position() = 1]"),
|
array('e:first-of-type', "*/e[position() = 1]"),
|
||||||
@ -121,7 +120,7 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"),
|
array('e:ConTains(foo)', "e[contains(string(.), 'foo')]"),
|
||||||
array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"),
|
array('e.warning', "e[@class and contains(concat(' ', normalize-space(@class), ' '), ' warning ')]"),
|
||||||
array('e#myid', "e[@id = 'myid']"),
|
array('e#myid', "e[@id = 'myid']"),
|
||||||
array('e:not(:nth-child(odd))', "e[not((position() -1) mod 2 = 0 and position() >= 1)]"),
|
array('e:not(:nth-child(odd))', "e[not(position() - 1 >= 0 and (position() - 1) mod 2 = 0)]"),
|
||||||
array('e:nOT(*)', "e[0]"),
|
array('e:nOT(*)', "e[0]"),
|
||||||
array('e f', "e/descendant-or-self::*/f"),
|
array('e f', "e/descendant-or-self::*/f"),
|
||||||
array('e > f', "e/f"),
|
array('e > f', "e/f"),
|
||||||
@ -188,17 +187,32 @@ class TranslatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
|
array('li:nth-child(+2n+1)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
|
||||||
array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
|
array('li:nth-child(odd)', array('first-li', 'third-li', 'fifth-li', 'seventh-li')),
|
||||||
array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')),
|
array('li:nth-child(2n+4)', array('fourth-li', 'sixth-li')),
|
||||||
// FIXME: I'm not 100% sure this is right:
|
|
||||||
array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')),
|
array('li:nth-child(3n+1)', array('first-li', 'fourth-li', 'seventh-li')),
|
||||||
array('li:nth-last-child(0)', array('seventh-li')),
|
array('li:nth-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-child(n+3)', array('third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-child(-n)', array()),
|
||||||
|
array('li:nth-child(-n-1)', array()),
|
||||||
|
array('li:nth-child(-n+1)', array('first-li')),
|
||||||
|
array('li:nth-child(-n+3)', array('first-li', 'second-li', 'third-li')),
|
||||||
|
array('li:nth-last-child(0)', array()),
|
||||||
array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')),
|
array('li:nth-last-child(2n)', array('second-li', 'fourth-li', 'sixth-li')),
|
||||||
array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')),
|
array('li:nth-last-child(even)', array('second-li', 'fourth-li', 'sixth-li')),
|
||||||
array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li')),
|
array('li:nth-last-child(2n+2)', array('second-li', 'fourth-li', 'sixth-li')),
|
||||||
|
array('li:nth-last-child(n)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-last-child(n-1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-last-child(n-3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-last-child(n+1)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
|
array('li:nth-last-child(n+3)', array('first-li', 'second-li', 'third-li', 'fourth-li', 'fifth-li')),
|
||||||
|
array('li:nth-last-child(-n)', array()),
|
||||||
|
array('li:nth-last-child(-n-1)', array()),
|
||||||
|
array('li:nth-last-child(-n+1)', array('seventh-li')),
|
||||||
|
array('li:nth-last-child(-n+3)', array('fifth-li', 'sixth-li', 'seventh-li')),
|
||||||
array('ol:first-of-type', array('first-ol')),
|
array('ol:first-of-type', array('first-ol')),
|
||||||
array('ol:nth-child(1)', array()),
|
array('ol:nth-child(1)', array('first-ol')),
|
||||||
array('ol:nth-of-type(2)', array('second-ol')),
|
array('ol:nth-of-type(2)', array('second-ol')),
|
||||||
// FIXME: like above (1) or (2)?
|
array('ol:nth-last-of-type(1)', array('second-ol')),
|
||||||
array('ol:nth-last-of-type(1)', array('first-ol')),
|
|
||||||
array('span:only-child', array('foobar-span')),
|
array('span:only-child', array('foobar-span')),
|
||||||
array('li div:only-child', array('li-div')),
|
array('li div:only-child', array('li-div')),
|
||||||
array('div *:only-child', array('li-div', 'foobar-span')),
|
array('div *:only-child', array('li-div', 'foobar-span')),
|
||||||
|
@ -67,25 +67,38 @@ class FunctionExtension extends AbstractExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (0 === $a) {
|
if (0 === $a) {
|
||||||
return $xpath->addCondition('position() = '.($last ? 'last() - '.$b : $b));
|
return $xpath->addCondition('position() = '.($last ? 'last() - '.($b - 1) : $b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($a < 0) {
|
||||||
|
if ($b < 1) {
|
||||||
|
return $xpath->addCondition('false()');
|
||||||
|
}
|
||||||
|
|
||||||
|
$sign = '<=';
|
||||||
|
} else {
|
||||||
|
$sign = '>=';
|
||||||
|
}
|
||||||
|
|
||||||
|
$expr = 'position()';
|
||||||
|
|
||||||
if ($last) {
|
if ($last) {
|
||||||
// todo: verify if this is right
|
$expr = 'last() - '.$expr;
|
||||||
$a = - $a;
|
$b--;
|
||||||
$b = - $b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$conditions = 1 === $a
|
if (0 !== $b) {
|
||||||
? array()
|
$expr .= ' - '.$b;
|
||||||
: array(sprintf('(position() %s) mod %s = 0', $b > 0 ? (string) (- $b) : '+'.(- $b), $a));
|
|
||||||
|
|
||||||
if ($b >= 0) {
|
|
||||||
$conditions[] = 'position() >= '.$b;
|
|
||||||
} elseif ($last) {
|
|
||||||
$conditions[] = sprintf('position() < (last() %s)', $b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$conditions = array(sprintf('%s %s 0', $expr, $sign));
|
||||||
|
|
||||||
|
if (1 !== $a && -1 !== $a) {
|
||||||
|
$conditions[] = sprintf('(%s) mod %d = 0', $expr, $a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $xpath->addCondition(implode(' and ', $conditions));
|
||||||
|
|
||||||
// todo: handle an+b, odd, even
|
// todo: handle an+b, odd, even
|
||||||
// an+b means every-a, plus b, e.g., 2n+1 means odd
|
// an+b means every-a, plus b, e.g., 2n+1 means odd
|
||||||
// 0n+b means b
|
// 0n+b means b
|
||||||
@ -93,8 +106,6 @@ class FunctionExtension extends AbstractExtension
|
|||||||
// an means every a elements, i.e., 2n means even
|
// an means every a elements, i.e., 2n means even
|
||||||
// -n means -1n
|
// -n means -1n
|
||||||
// -1n+6 means elements 6 and previous
|
// -1n+6 means elements 6 and previous
|
||||||
|
|
||||||
return empty($conditions) ? $xpath : $xpath->addCondition(implode(' and ', $conditions));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user