|
1 <?php |
|
2 |
|
3 /* |
|
4 * This file is part of the Symfony package. |
|
5 * |
|
6 * (c) Fabien Potencier <fabien@symfony.com> |
|
7 * |
|
8 * For the full copyright and license information, please view the LICENSE |
|
9 * file that was distributed with this source code. |
|
10 */ |
|
11 |
|
12 namespace Symfony\Component\CssSelector\Node; |
|
13 |
|
14 use Symfony\Component\CssSelector\Exception\ParseException; |
|
15 |
|
16 /** |
|
17 * PseudoNode represents a "selector:ident" node. |
|
18 * |
|
19 * This component is a port of the Python lxml library, |
|
20 * which is copyright Infrae and distributed under the BSD license. |
|
21 * |
|
22 * @author Fabien Potencier <fabien@symfony.com> |
|
23 */ |
|
24 class PseudoNode implements NodeInterface |
|
25 { |
|
26 static protected $unsupported = array( |
|
27 'indeterminate', 'first-line', 'first-letter', |
|
28 'selection', 'before', 'after', 'link', 'visited', |
|
29 'active', 'focus', 'hover', |
|
30 ); |
|
31 |
|
32 protected $element; |
|
33 protected $type; |
|
34 protected $ident; |
|
35 |
|
36 /** |
|
37 * Constructor. |
|
38 * |
|
39 * @param NodeInterface $element The NodeInterface element |
|
40 * @param string $type Node type |
|
41 * @param string $ident The ident |
|
42 * @throws ParseException When incorrect PseudoNode type is given |
|
43 */ |
|
44 public function __construct($element, $type, $ident) |
|
45 { |
|
46 $this->element = $element; |
|
47 |
|
48 if (!in_array($type, array(':', '::'))) { |
|
49 throw new ParseException(sprintf('The PseudoNode type can only be : or :: (%s given).', $type)); |
|
50 } |
|
51 |
|
52 $this->type = $type; |
|
53 $this->ident = $ident; |
|
54 } |
|
55 |
|
56 /** |
|
57 * {@inheritDoc} |
|
58 */ |
|
59 public function __toString() |
|
60 { |
|
61 return sprintf('%s[%s%s%s]', __CLASS__, $this->element, $this->type, $this->ident); |
|
62 } |
|
63 |
|
64 /** |
|
65 * {@inheritDoc} |
|
66 * @throws ParseException When unsupported or unknown pseudo-class is found |
|
67 */ |
|
68 public function toXpath() |
|
69 { |
|
70 $elXpath = $this->element->toXpath(); |
|
71 |
|
72 if (in_array($this->ident, self::$unsupported)) { |
|
73 throw new ParseException(sprintf('The pseudo-class %s is unsupported', $this->ident)); |
|
74 } |
|
75 $method = 'xpath_'.str_replace('-', '_', $this->ident); |
|
76 if (!method_exists($this, $method)) { |
|
77 throw new ParseException(sprintf('The pseudo-class %s is unknown', $this->ident)); |
|
78 } |
|
79 |
|
80 return $this->$method($elXpath); |
|
81 } |
|
82 |
|
83 /** |
|
84 * |
|
85 * @param XPathExpr $xpath The XPath expression |
|
86 * @return XPathExpr The modified XPath expression |
|
87 */ |
|
88 protected function xpath_checked($xpath) |
|
89 { |
|
90 // FIXME: is this really all the elements? |
|
91 $xpath->addCondition("(@selected or @checked) and (name(.) = 'input' or name(.) = 'option')"); |
|
92 |
|
93 return $xpath; |
|
94 } |
|
95 |
|
96 /** |
|
97 * @param XPathExpr $xpath The XPath expression |
|
98 * @return XPathExpr The modified XPath expression |
|
99 * @throws ParseException If this element is the root element |
|
100 */ |
|
101 protected function xpath_root($xpath) |
|
102 { |
|
103 // if this element is the root element |
|
104 throw new ParseException(); |
|
105 } |
|
106 |
|
107 /** |
|
108 * Marks this XPath expression as the first child. |
|
109 * |
|
110 * @param XPathExpr $xpath The XPath expression |
|
111 * @return XPathExpr The modified expression |
|
112 */ |
|
113 protected function xpath_first_child($xpath) |
|
114 { |
|
115 $xpath->addStarPrefix(); |
|
116 $xpath->addNameTest(); |
|
117 $xpath->addCondition('position() = 1'); |
|
118 |
|
119 return $xpath; |
|
120 } |
|
121 |
|
122 /** |
|
123 * Sets the XPath to be the last child. |
|
124 * |
|
125 * @param XPathExpr $xpath The XPath expression |
|
126 * @return XPathExpr The modified expression |
|
127 */ |
|
128 protected function xpath_last_child($xpath) |
|
129 { |
|
130 $xpath->addStarPrefix(); |
|
131 $xpath->addNameTest(); |
|
132 $xpath->addCondition('position() = last()'); |
|
133 |
|
134 return $xpath; |
|
135 } |
|
136 |
|
137 /** |
|
138 * Sets the XPath expression to be the first of type. |
|
139 * |
|
140 * @param XPathExpr $xpath The XPath expression |
|
141 * @return XPathExpr The modified expression |
|
142 */ |
|
143 protected function xpath_first_of_type($xpath) |
|
144 { |
|
145 if ($xpath->getElement() == '*') { |
|
146 throw new ParseException('*:first-of-type is not implemented'); |
|
147 } |
|
148 $xpath->addStarPrefix(); |
|
149 $xpath->addCondition('position() = 1'); |
|
150 |
|
151 return $xpath; |
|
152 } |
|
153 |
|
154 /** |
|
155 * Sets the XPath expression to be the last of type. |
|
156 * |
|
157 * @param XPathExpr $xpath The XPath expression |
|
158 * @return XPathExpr The modified expression |
|
159 * @throws ParseException Because *:last-of-type is not implemented |
|
160 */ |
|
161 protected function xpath_last_of_type($xpath) |
|
162 { |
|
163 if ($xpath->getElement() == '*') { |
|
164 throw new ParseException('*:last-of-type is not implemented'); |
|
165 } |
|
166 $xpath->addStarPrefix(); |
|
167 $xpath->addCondition('position() = last()'); |
|
168 |
|
169 return $xpath; |
|
170 } |
|
171 |
|
172 /** |
|
173 * Sets the XPath expression to be the only child. |
|
174 * |
|
175 * @param XPathExpr $xpath The XPath expression |
|
176 * @return XPathExpr The modified expression |
|
177 */ |
|
178 protected function xpath_only_child($xpath) |
|
179 { |
|
180 $xpath->addNameTest(); |
|
181 $xpath->addStarPrefix(); |
|
182 $xpath->addCondition('last() = 1'); |
|
183 |
|
184 return $xpath; |
|
185 } |
|
186 |
|
187 /** |
|
188 * Sets the XPath expression to be only of type. |
|
189 * |
|
190 * @param XPathExpr $xpath The XPath expression |
|
191 * @return XPathExpr The modified expression |
|
192 * @throws ParseException Because *:only-of-type is not implemented |
|
193 */ |
|
194 protected function xpath_only_of_type($xpath) |
|
195 { |
|
196 if ($xpath->getElement() == '*') { |
|
197 throw new ParseException('*:only-of-type is not implemented'); |
|
198 } |
|
199 $xpath->addCondition('last() = 1'); |
|
200 |
|
201 return $xpath; |
|
202 } |
|
203 |
|
204 /** |
|
205 * undocumented function |
|
206 * |
|
207 * @param XPathExpr $xpath The XPath expression |
|
208 * @return XPathExpr The modified expression |
|
209 */ |
|
210 protected function xpath_empty($xpath) |
|
211 { |
|
212 $xpath->addCondition('not(*) and not(normalize-space())'); |
|
213 |
|
214 return $xpath; |
|
215 } |
|
216 } |