--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/symfony/src/Symfony/Component/CssSelector/Node/FunctionNode.php Sat Sep 24 15:40:41 2011 +0200
@@ -0,0 +1,288 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\CssSelector\Node;
+
+use Symfony\Component\CssSelector\Exception\ParseException;
+use Symfony\Component\CssSelector\XPathExpr;
+
+/**
+ * FunctionNode represents a "selector:name(expr)" node.
+ *
+ * This component is a port of the Python lxml library,
+ * which is copyright Infrae and distributed under the BSD license.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class FunctionNode implements NodeInterface
+{
+ static protected $unsupported = array('target', 'lang', 'enabled', 'disabled');
+
+ protected $selector;
+ protected $type;
+ protected $name;
+ protected $expr;
+
+ /**
+ * Constructor.
+ *
+ * @param NodeInterface $selector The XPath expression
+ * @param string $type
+ * @param string $name
+ * @param XPathExpr $expr
+ */
+ public function __construct($selector, $type, $name, $expr)
+ {
+ $this->selector = $selector;
+ $this->type = $type;
+ $this->name = $name;
+ $this->expr = $expr;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function __toString()
+ {
+ return sprintf('%s[%s%s%s(%s)]', __CLASS__, $this->selector, $this->type, $this->name, $this->expr);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws ParseException When unsupported or unknown pseudo-class is found
+ */
+ public function toXpath()
+ {
+ $selPath = $this->selector->toXpath();
+ if (in_array($this->name, self::$unsupported)) {
+ throw new ParseException(sprintf('The pseudo-class %s is not supported', $this->name));
+ }
+ $method = '_xpath_'.str_replace('-', '_', $this->name);
+ if (!method_exists($this, $method)) {
+ throw new ParseException(sprintf('The pseudo-class %s is unknown', $this->name));
+ }
+
+ return $this->$method($selPath, $this->expr);
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param mixed $expr
+ * @param Boolean $last
+ * @param Boolean $addNameTest
+ * @return XPathExpr
+ */
+ protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest = true)
+ {
+ list($a, $b) = $this->parseSeries($expr);
+ if (!$a && !$b && !$last) {
+ // a=0 means nothing is returned...
+ $xpath->addCondition('false() and position() = 0');
+
+ return $xpath;
+ }
+
+ if ($addNameTest) {
+ $xpath->addNameTest();
+ }
+
+ $xpath->addStarPrefix();
+ if ($a == 0) {
+ if ($last) {
+ $b = sprintf('last() - %s', $b);
+ }
+ $xpath->addCondition(sprintf('position() = %s', $b));
+
+ return $xpath;
+ }
+
+ if ($last) {
+ // FIXME: I'm not sure if this is right
+ $a = -$a;
+ $b = -$b;
+ }
+
+ if ($b > 0) {
+ $bNeg = -$b;
+ } else {
+ $bNeg = sprintf('+%s', -$b);
+ }
+
+ if ($a != 1) {
+ $expr = array(sprintf('(position() %s) mod %s = 0', $bNeg, $a));
+ } else {
+ $expr = array();
+ }
+
+ if ($b >= 0) {
+ $expr[] = sprintf('position() >= %s', $b);
+ } elseif ($b < 0 && $last) {
+ $expr[] = sprintf('position() < (last() %s)', $b);
+ }
+ $expr = implode($expr, ' and ');
+
+ if ($expr) {
+ $xpath->addCondition($expr);
+ }
+
+ return $xpath;
+ /* FIXME: handle an+b, odd, even
+ an+b means every-a, plus b, e.g., 2n+1 means odd
+ 0n+b means b
+ n+0 means a=1, i.e., all elements
+ an means every a elements, i.e., 2n means even
+ -n means -1n
+ -1n+6 means elements 6 and previous */
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param XPathExpr $expr
+ * @return XPathExpr
+ */
+ protected function _xpath_nth_last_child($xpath, $expr)
+ {
+ return $this->_xpath_nth_child($xpath, $expr, true);
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param XPathExpr $expr
+ * @return XPathExpr
+ */
+ protected function _xpath_nth_of_type($xpath, $expr)
+ {
+ if ($xpath->getElement() == '*') {
+ throw new ParseException('*:nth-of-type() is not implemented');
+ }
+
+ return $this->_xpath_nth_child($xpath, $expr, false, false);
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param XPathExpr $expr
+ * @return XPathExpr
+ */
+ protected function _xpath_nth_last_of_type($xpath, $expr)
+ {
+ return $this->_xpath_nth_child($xpath, $expr, true, false);
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param XPathExpr $expr
+ * @return XPathExpr
+ */
+ protected function _xpath_contains($xpath, $expr)
+ {
+ // text content, minus tags, must contain expr
+ if ($expr instanceof ElementNode) {
+ $expr = $expr->formatElement();
+ }
+
+ // FIXME: lower-case is only available with XPath 2
+ //$xpath->addCondition(sprintf('contains(lower-case(string(.)), %s)', XPathExpr::xpathLiteral(strtolower($expr))));
+ $xpath->addCondition(sprintf('contains(string(.), %s)', XPathExpr::xpathLiteral($expr)));
+
+ // FIXME: Currently case insensitive matching doesn't seem to be happening
+
+ return $xpath;
+ }
+
+ /**
+ * undocumented function
+ *
+ * @param XPathExpr $xpath
+ * @param XPathExpr $expr
+ * @return XPathExpr
+ */
+ protected function _xpath_not($xpath, $expr)
+ {
+ // everything for which not expr applies
+ $expr = $expr->toXpath();
+ $cond = $expr->getCondition();
+ // FIXME: should I do something about element_path?
+ $xpath->addCondition(sprintf('not(%s)', $cond));
+
+ return $xpath;
+ }
+
+ /**
+ * Parses things like '1n+2', or 'an+b' generally, returning (a, b)
+ *
+ * @param mixed $s
+ * @return array
+ */
+ protected function parseSeries($s)
+ {
+ if ($s instanceof ElementNode) {
+ $s = $s->formatElement();
+ }
+
+ if (!$s || '*' == $s) {
+ // Happens when there's nothing, which the CSS parser thinks of as *
+ return array(0, 0);
+ }
+
+ if (is_string($s)) {
+ // Happens when you just get a number
+ return array(0, $s);
+ }
+
+ if ('odd' == $s) {
+ return array(2, 1);
+ }
+
+ if ('even' == $s) {
+ return array(2, 0);
+ }
+
+ if ('n' == $s) {
+ return array(1, 0);
+ }
+
+ if (false === strpos($s, 'n')) {
+ // Just a b
+
+ return array(0, intval((string) $s));
+ }
+
+ list($a, $b) = explode('n', $s);
+ if (!$a) {
+ $a = 1;
+ } elseif ('-' == $a || '+' == $a) {
+ $a = intval($a.'1');
+ } else {
+ $a = intval($a);
+ }
+
+ if (!$b) {
+ $b = 0;
+ } elseif ('-' == $b || '+' == $b) {
+ $b = intval($b.'1');
+ } else {
+ $b = intval($b);
+ }
+
+ return array($a, $b);
+ }
+}