|
1
|
|
|
<?php |
|
2
|
|
|
namespace PhpQuery; |
|
3
|
|
|
|
|
4
|
|
|
use Sabberworm\CSS\Parser as CssParser; |
|
5
|
|
|
|
|
6
|
|
|
/** |
|
7
|
|
|
* Class representing PhpQuery objects. |
|
8
|
|
|
* |
|
9
|
|
|
* @author Tobiasz Cudnik <tobiasz.cudnik/gmail.com> |
|
10
|
|
|
* @package PhpQuery |
|
11
|
|
|
* @method PhpQueryObject clone() clone() |
|
12
|
|
|
* @method PhpQueryObject empty() empty() |
|
13
|
|
|
* @property Int $length |
|
14
|
|
|
*/ |
|
15
|
|
|
class PhpQueryObject implements \Iterator, \Countable, \ArrayAccess |
|
16
|
|
|
{ |
|
17
|
|
|
public $documentID = null; |
|
18
|
|
|
/** |
|
19
|
|
|
* \DOMDocument class. |
|
20
|
|
|
* |
|
21
|
|
|
* @var \DOMDocument |
|
22
|
|
|
*/ |
|
23
|
|
|
public $document = null; |
|
24
|
|
|
public $charset = null; |
|
25
|
|
|
/** |
|
26
|
|
|
* |
|
27
|
|
|
* @var Dom\DOMDocumentWrapper |
|
28
|
|
|
*/ |
|
29
|
|
|
public $documentWrapper = null; |
|
30
|
|
|
/** |
|
31
|
|
|
* XPath interface. |
|
32
|
|
|
* |
|
33
|
|
|
* @var \DOMXPath |
|
34
|
|
|
*/ |
|
35
|
|
|
public $xpath = null; |
|
36
|
|
|
/** |
|
37
|
|
|
* Stack of selected elements. |
|
38
|
|
|
* @TODO refactor to ->nodes |
|
39
|
|
|
* @var array |
|
40
|
|
|
*/ |
|
41
|
|
|
public $elements = array(); |
|
42
|
|
|
/** |
|
43
|
|
|
* @access private |
|
44
|
|
|
*/ |
|
45
|
|
|
protected $elementsBackup = array(); |
|
46
|
|
|
/** |
|
47
|
|
|
* @access private |
|
48
|
|
|
*/ |
|
49
|
|
|
protected $previous = null; |
|
50
|
|
|
/** |
|
51
|
|
|
* @access private |
|
52
|
|
|
* @TODO deprecate |
|
53
|
|
|
*/ |
|
54
|
|
|
protected $root = array(); |
|
55
|
|
|
/** |
|
56
|
|
|
* Indicated if doument is just a fragment (no <html> tag). |
|
57
|
|
|
* |
|
58
|
|
|
* Every document is realy a full document, so even documentFragments can |
|
59
|
|
|
* be queried against <html>, but getDocument(id)->htmlOuter() will return |
|
60
|
|
|
* only contents of <body>. |
|
61
|
|
|
* |
|
62
|
|
|
* @var bool |
|
63
|
|
|
*/ |
|
64
|
|
|
public $documentFragment = true; |
|
65
|
|
|
/** |
|
66
|
|
|
* Iterator interface helper |
|
67
|
|
|
* @access private |
|
68
|
|
|
*/ |
|
69
|
|
|
protected $elementsInterator = array(); |
|
70
|
|
|
/** |
|
71
|
|
|
* Iterator interface helper |
|
72
|
|
|
* @access private |
|
73
|
|
|
*/ |
|
74
|
|
|
protected $valid = false; |
|
75
|
|
|
/** |
|
76
|
|
|
* Iterator interface helper |
|
77
|
|
|
* @access private |
|
78
|
|
|
*/ |
|
79
|
|
|
protected $current = null; |
|
80
|
|
|
|
|
81
|
|
|
/** |
|
82
|
|
|
* Indicates whether CSS has been parsed or not. We only parse CSS if needed. |
|
83
|
|
|
* @access private |
|
84
|
|
|
*/ |
|
85
|
|
|
protected $cssIsParsed = array(); |
|
86
|
|
|
/** |
|
87
|
|
|
* A collection of complete CSS selector strings. |
|
88
|
|
|
* @access private; |
|
89
|
|
|
*/ |
|
90
|
|
|
protected $cssString = array(); |
|
91
|
|
|
/** |
|
92
|
|
|
* Enter description here... |
|
93
|
|
|
* |
|
94
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
95
|
|
|
*/ |
|
96
|
|
|
|
|
97
|
|
|
protected $attribute_css_mapping = array( |
|
98
|
|
|
'bgcolor' => 'background-color', |
|
99
|
|
|
'text' => 'color', |
|
100
|
|
|
'width' => 'width', |
|
101
|
|
|
'height' => 'height' |
|
102
|
|
|
); |
|
103
|
|
|
|
|
104
|
|
|
public function __construct($documentID) |
|
105
|
|
|
{ |
|
106
|
|
|
// if ($documentID instanceof self) |
|
|
|
|
|
|
107
|
|
|
// var_dump($documentID->getDocumentID()); |
|
|
|
|
|
|
108
|
|
|
$id = $documentID instanceof self ? $documentID->getDocumentID() |
|
109
|
|
|
: $documentID; |
|
110
|
|
|
// var_dump($id); |
|
|
|
|
|
|
111
|
|
|
if (!isset(PhpQuery::$documents[$id])) { |
|
112
|
|
|
// var_dump(PhpQuery::$documents); |
|
|
|
|
|
|
113
|
|
|
throw new \Exception("Document with ID '{$id}' isn't loaded. Use PhpQuery::newDocument(\$html) or PhpQuery::newDocumentFile(\$file) first."); |
|
114
|
|
|
} |
|
115
|
|
|
$this->documentID = $id; |
|
116
|
|
|
$this->documentWrapper = & PhpQuery::$documents[$id]; |
|
117
|
|
|
$this->document = & $this->documentWrapper->document; |
|
118
|
|
|
$this->xpath = & $this->documentWrapper->xpath; |
|
119
|
|
|
$this->charset = & $this->documentWrapper->charset; |
|
120
|
|
|
$this->documentFragment = & $this->documentWrapper->isDocumentFragment; |
|
121
|
|
|
// TODO check $this->DOM->documentElement; |
|
|
|
|
|
|
122
|
|
|
// $this->root = $this->document->documentElement; |
|
|
|
|
|
|
123
|
|
|
$this->root = & $this->documentWrapper->root; |
|
124
|
|
|
// $this->toRoot(); |
|
|
|
|
|
|
125
|
|
|
$this->elements = array( |
|
126
|
|
|
$this->root |
|
127
|
|
|
); |
|
128
|
|
|
} |
|
129
|
|
|
|
|
130
|
|
|
/** |
|
131
|
|
|
* |
|
132
|
|
|
* @access private |
|
133
|
|
|
* @param $attr |
|
134
|
|
|
* @return \PhpQuery\PhpQueryObject|\PhpQuery\QueryTemplatesParse|\PhpQuery\QueryTemplatesSource|\PhpQuery\QueryTemplatesSourceQuery |
|
135
|
|
|
*/ |
|
136
|
|
|
public function __get($attr) |
|
137
|
|
|
{ |
|
138
|
|
|
switch ($attr) { |
|
139
|
|
|
// FIXME doesnt work at all ? |
|
140
|
|
|
case 'length': |
|
141
|
|
|
return $this->size(); |
|
|
|
|
|
|
142
|
|
|
break; |
|
|
|
|
|
|
143
|
|
|
default: |
|
144
|
|
|
return $this->$attr; |
|
145
|
|
|
} |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
/** |
|
149
|
|
|
* Saves actual object to $var by reference. |
|
150
|
|
|
* Useful when need to break chain. |
|
151
|
|
|
* @param PhpQueryObject $var |
|
152
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
153
|
|
|
*/ |
|
154
|
|
|
public function toReference(&$var) |
|
155
|
|
|
{ |
|
156
|
|
|
return $var = $this; |
|
157
|
|
|
} |
|
158
|
|
|
|
|
159
|
|
|
public function documentFragment($state = null) |
|
160
|
|
|
{ |
|
161
|
|
|
if ($state) { |
|
162
|
|
|
PhpQuery::$documents[$this->getDocumentID()]['documentFragment'] = $state; |
|
163
|
|
|
return $this; |
|
164
|
|
|
} |
|
165
|
|
|
return $this->documentFragment; |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* @access private |
|
170
|
|
|
* @TODO documentWrapper |
|
171
|
|
|
*/ |
|
172
|
|
|
protected function isRoot($node) |
|
173
|
|
|
{ |
|
174
|
|
|
// return $node instanceof \DOMDocument || $node->tagName == 'html'; |
|
|
|
|
|
|
175
|
|
|
return $node instanceof \DOMDocument |
|
176
|
|
|
|| ($node instanceof \DOMElement && $node->tagName == 'html') |
|
177
|
|
|
|| $this->root->isSameNode($node); |
|
|
|
|
|
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
/** |
|
181
|
|
|
* @access private |
|
182
|
|
|
*/ |
|
183
|
|
|
protected function stackIsRoot() |
|
184
|
|
|
{ |
|
185
|
|
|
return $this->size() == 1 && $this->isRoot($this->elements[0]); |
|
186
|
|
|
} |
|
187
|
|
|
|
|
188
|
|
|
/** |
|
189
|
|
|
* Enter description here... |
|
190
|
|
|
* NON JQUERY METHOD |
|
191
|
|
|
* |
|
192
|
|
|
* Watch out, it doesn't creates new instance, can be reverted with end(). |
|
193
|
|
|
* |
|
194
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
195
|
|
|
*/ |
|
196
|
|
|
public function toRoot() |
|
197
|
|
|
{ |
|
198
|
|
|
$this->elements = array( |
|
199
|
|
|
$this->root |
|
200
|
|
|
); |
|
201
|
|
|
return $this; |
|
202
|
|
|
// return $this->newInstance(array($this->root)); |
|
|
|
|
|
|
203
|
|
|
} |
|
204
|
|
|
|
|
205
|
|
|
/** |
|
206
|
|
|
* Saves object's DocumentID to $var by reference. |
|
207
|
|
|
* <code> |
|
208
|
|
|
* $myDocumentId; |
|
209
|
|
|
* PhpQuery::newDocument('<div/>') |
|
210
|
|
|
* ->getDocumentIDRef($myDocumentId) |
|
211
|
|
|
* ->find('div')->... |
|
212
|
|
|
* </code> |
|
213
|
|
|
* |
|
214
|
|
|
* @param $documentID |
|
215
|
|
|
* @internal param string $domId |
|
216
|
|
|
* @see PhpQuery::newDocument |
|
217
|
|
|
* @see PhpQuery::newDocumentFile |
|
218
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
219
|
|
|
*/ |
|
220
|
|
|
public function getDocumentIDRef(&$documentID) |
|
221
|
|
|
{ |
|
222
|
|
|
$documentID = $this->getDocumentID(); |
|
223
|
|
|
return $this; |
|
224
|
|
|
} |
|
225
|
|
|
|
|
226
|
|
|
/** |
|
227
|
|
|
* Returns object with stack set to document root. |
|
228
|
|
|
* |
|
229
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
230
|
|
|
*/ |
|
231
|
|
|
public function getDocument() |
|
232
|
|
|
{ |
|
233
|
|
|
return PhpQuery::getDocument($this->getDocumentID()); |
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
/** |
|
237
|
|
|
* |
|
238
|
|
|
* @return \DOMDocument |
|
239
|
|
|
*/ |
|
240
|
|
|
public function getDOMDocument() |
|
241
|
|
|
{ |
|
242
|
|
|
return $this->document; |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
/** |
|
246
|
|
|
* Get object's Document ID. |
|
247
|
|
|
* |
|
248
|
|
|
* @return string |
|
249
|
|
|
*/ |
|
250
|
|
|
public function getDocumentID() |
|
251
|
|
|
{ |
|
252
|
|
|
return $this->documentID; |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
/** |
|
256
|
|
|
* Unloads whole document from memory. |
|
257
|
|
|
* CAUTION! None further operations will be possible on this document. |
|
258
|
|
|
* All objects refering to it will be useless. |
|
259
|
|
|
* |
|
260
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
261
|
|
|
*/ |
|
262
|
|
|
public function unloadDocument() |
|
263
|
|
|
{ |
|
264
|
|
|
PhpQuery::unloadDocuments($this->getDocumentID()); |
|
265
|
|
|
} |
|
266
|
|
|
|
|
267
|
|
|
public function isHTML() |
|
268
|
|
|
{ |
|
269
|
|
|
return $this->documentWrapper->isHTML; |
|
270
|
|
|
} |
|
271
|
|
|
|
|
272
|
|
|
public function isXHTML() |
|
273
|
|
|
{ |
|
274
|
|
|
return $this->documentWrapper->isXHTML; |
|
275
|
|
|
} |
|
276
|
|
|
|
|
277
|
|
|
public function isXML() |
|
278
|
|
|
{ |
|
279
|
|
|
return $this->documentWrapper->isXML; |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
/** |
|
283
|
|
|
* Enter description here... |
|
284
|
|
|
* |
|
285
|
|
|
* @link http://docs.jquery.com/Ajax/serialize |
|
286
|
|
|
* @return string |
|
287
|
|
|
*/ |
|
288
|
|
|
public function serialize() |
|
289
|
|
|
{ |
|
290
|
|
|
return PhpQuery::param($this->serializeArray()); |
|
291
|
|
|
} |
|
292
|
|
|
|
|
293
|
|
|
/** |
|
294
|
|
|
* Enter description here... |
|
295
|
|
|
* |
|
296
|
|
|
* @link http://docs.jquery.com/Ajax/serializeArray |
|
297
|
|
|
* @return array |
|
298
|
|
|
*/ |
|
299
|
|
|
public function serializeArray($submit = null) |
|
300
|
|
|
{ |
|
301
|
|
|
$source = $this->filter('form, input, select, textarea')->find('input, select, textarea')->andSelf()->not( |
|
302
|
|
|
'form' |
|
303
|
|
|
); |
|
304
|
|
|
$return = array(); |
|
305
|
|
|
// $source->dumpDie(); |
|
|
|
|
|
|
306
|
|
|
foreach ($source as $input) { |
|
307
|
|
|
$input = PhpQuery::pq($input); |
|
308
|
|
|
if ($input->is('[disabled]')) { |
|
309
|
|
|
continue; |
|
310
|
|
|
} |
|
311
|
|
|
if (!$input->is('[name]')) { |
|
312
|
|
|
continue; |
|
313
|
|
|
} |
|
314
|
|
|
if ($input->is('[type=checkbox]') && !$input->is('[checked]')) { |
|
315
|
|
|
continue; |
|
316
|
|
|
} |
|
317
|
|
|
// jquery diff |
|
318
|
|
|
if ($submit && $input->is('[type=submit]')) { |
|
319
|
|
|
if ($submit instanceof \DOMElement |
|
320
|
|
|
&& !$input->elements[0]->isSameNode($submit) |
|
321
|
|
|
) { |
|
322
|
|
|
continue; |
|
323
|
|
|
} else { |
|
324
|
|
|
if (is_string($submit) && $input->attr('name') != $submit) { |
|
325
|
|
|
continue; |
|
326
|
|
|
} |
|
327
|
|
|
} |
|
328
|
|
|
} |
|
329
|
|
|
$return[] = array( |
|
330
|
|
|
'name' => $input->attr('name'), |
|
331
|
|
|
'value' => $input->val(), |
|
332
|
|
|
); |
|
333
|
|
|
} |
|
334
|
|
|
return $return; |
|
335
|
|
|
} |
|
336
|
|
|
|
|
337
|
|
|
/** |
|
338
|
|
|
* @access private |
|
339
|
|
|
*/ |
|
340
|
|
|
protected function debug($in) |
|
341
|
|
|
{ |
|
342
|
|
|
if (!PhpQuery::$debug) { |
|
343
|
|
|
return; |
|
344
|
|
|
} |
|
345
|
|
|
print('<pre>'); |
|
346
|
|
|
print_r($in); |
|
347
|
|
|
// file debug |
|
348
|
|
|
// file_put_contents(dirname(__FILE__).'/PhpQuery.log', print_r($in, true)."\n", FILE_APPEND); |
|
|
|
|
|
|
349
|
|
|
// quite handy debug trace |
|
350
|
|
|
// if ( is_array($in)) |
|
|
|
|
|
|
351
|
|
|
// print_r(array_slice(debug_backtrace(), 3)); |
|
|
|
|
|
|
352
|
|
|
print("</pre>\n"); |
|
353
|
|
|
} |
|
354
|
|
|
|
|
355
|
|
|
/** |
|
356
|
|
|
* @access private |
|
357
|
|
|
*/ |
|
358
|
|
|
protected function isRegexp($pattern) |
|
359
|
|
|
{ |
|
360
|
|
|
return in_array( |
|
361
|
|
|
$pattern[mb_strlen($pattern) - 1], |
|
362
|
|
|
array( |
|
363
|
|
|
'^', |
|
364
|
|
|
'*', |
|
365
|
|
|
'$' |
|
366
|
|
|
) |
|
367
|
|
|
); |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
/** |
|
371
|
|
|
* Determines if $char is really a char. |
|
372
|
|
|
* |
|
373
|
|
|
* @param string $char |
|
374
|
|
|
* @return bool |
|
375
|
|
|
* @todo rewrite me to charcode range ! ;) |
|
376
|
|
|
* @access private |
|
377
|
|
|
*/ |
|
378
|
|
|
protected function isChar($char) |
|
379
|
|
|
{ |
|
380
|
|
|
return extension_loaded('mbstring') && PhpQuery::$mbstringSupport ? mb_eregi('\w', $char) |
|
381
|
|
|
: preg_match('@\w@', $char); |
|
382
|
|
|
} |
|
383
|
|
|
|
|
384
|
|
|
/** |
|
385
|
|
|
* @access private |
|
386
|
|
|
*/ |
|
387
|
|
|
protected function parseSelector($query) |
|
388
|
|
|
{ |
|
389
|
|
|
// clean spaces |
|
390
|
|
|
// TODO include this inside parsing ? |
|
391
|
|
|
$query = trim(preg_replace('@\s+@', ' ', preg_replace('@\s*(>|\\+|~)\s*@', '\\1', (string) $query))); |
|
392
|
|
|
$queries = array( |
|
393
|
|
|
array() |
|
394
|
|
|
); |
|
395
|
|
|
if (!$query) { |
|
396
|
|
|
return $queries; |
|
397
|
|
|
} |
|
398
|
|
|
$return = & $queries[0]; |
|
399
|
|
|
$specialChars = array( |
|
400
|
|
|
'>', |
|
401
|
|
|
' ' |
|
402
|
|
|
); |
|
403
|
|
|
// $specialCharsMapping = array('/' => '>'); |
|
|
|
|
|
|
404
|
|
|
$specialCharsMapping = array(); |
|
405
|
|
|
$strlen = mb_strlen($query); |
|
406
|
|
|
$classChars = array( |
|
407
|
|
|
'.', |
|
408
|
|
|
'-' |
|
409
|
|
|
); |
|
410
|
|
|
$pseudoChars = array( |
|
411
|
|
|
'-' |
|
412
|
|
|
); |
|
413
|
|
|
$tagChars = array( |
|
414
|
|
|
'*', |
|
415
|
|
|
'|', |
|
416
|
|
|
'-' |
|
417
|
|
|
); |
|
418
|
|
|
// split multibyte string |
|
419
|
|
|
// http://code.google.com/p/phpquery/issues/detail?id=76 |
|
420
|
|
|
$_query = array(); |
|
421
|
|
|
for ($i = 0; $i < $strlen; $i++) { |
|
422
|
|
|
$_query[] = mb_substr($query, $i, 1); |
|
423
|
|
|
} |
|
424
|
|
|
$query = $_query; |
|
425
|
|
|
// it works, but i dont like it... |
|
426
|
|
|
$i = 0; |
|
427
|
|
|
while ($i < $strlen) { |
|
428
|
|
|
$c = $query[$i]; |
|
429
|
|
|
$tmp = ''; |
|
430
|
|
|
// TAG |
|
431
|
|
|
if ($this->isChar($c) || in_array($c, $tagChars)) { |
|
432
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
433
|
|
|
&& ($this->isChar($query[$i]) || in_array($query[$i], $tagChars))) { |
|
434
|
|
|
$tmp .= $query[$i]; |
|
435
|
|
|
$i++; |
|
436
|
|
|
} |
|
437
|
|
|
$return[] = $tmp; |
|
438
|
|
|
// IDs |
|
439
|
|
|
} else if ($c == '#') { |
|
440
|
|
|
$i++; |
|
441
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
442
|
|
|
&& ($this->isChar($query[$i]) || $query[$i] == '-')) { |
|
443
|
|
|
$tmp .= $query[$i]; |
|
444
|
|
|
$i++; |
|
445
|
|
|
} |
|
446
|
|
|
$return[] = '#' . $tmp; |
|
447
|
|
|
// SPECIAL CHARS |
|
448
|
|
|
} else if (in_array($c, $specialChars)) { |
|
449
|
|
|
$return[] = $c; |
|
450
|
|
|
$i++; |
|
451
|
|
|
// MAPPED SPECIAL MULTICHARS |
|
452
|
|
|
// } else if ( $c.$query[$i+1] == '//') { |
|
|
|
|
|
|
453
|
|
|
// $return[] = ' '; |
|
|
|
|
|
|
454
|
|
|
// $i = $i+2; |
|
|
|
|
|
|
455
|
|
|
// MAPPED SPECIAL CHARS |
|
456
|
|
|
} else if (isset($specialCharsMapping[$c])) { |
|
457
|
|
|
$return[] = $specialCharsMapping[$c]; |
|
458
|
|
|
$i++; |
|
459
|
|
|
// COMMA |
|
460
|
|
|
} else if ($c == ',') { |
|
461
|
|
|
$queries[] = array(); |
|
462
|
|
|
$return = & $queries[count($queries) - 1]; |
|
463
|
|
|
$i++; |
|
464
|
|
|
while (isset($query[$i]) && $query[$i] == ' ') { |
|
465
|
|
|
$i++; |
|
466
|
|
|
} |
|
467
|
|
|
// CLASSES |
|
468
|
|
|
} else if ($c == '.') { |
|
469
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
470
|
|
|
&& ($this->isChar($query[$i]) || in_array($query[$i], $classChars))) { |
|
471
|
|
|
$tmp .= $query[$i]; |
|
472
|
|
|
$i++; |
|
473
|
|
|
} |
|
474
|
|
|
$return[] = $tmp; |
|
475
|
|
|
// ~ General Sibling Selector |
|
476
|
|
|
} else if ($c == '~') { |
|
477
|
|
|
$spaceAllowed = true; |
|
478
|
|
|
$tmp .= $query[$i++]; |
|
479
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
480
|
|
|
&& ($this->isChar($query[$i]) || in_array($query[$i], $classChars) |
|
481
|
|
|
|| $query[$i] == '*' || ($query[$i] == ' ' && $spaceAllowed))) { |
|
482
|
|
|
if ($query[$i] != ' ') { |
|
483
|
|
|
$spaceAllowed = false; |
|
484
|
|
|
} |
|
485
|
|
|
$tmp .= $query[$i]; |
|
486
|
|
|
$i++; |
|
487
|
|
|
} |
|
488
|
|
|
$return[] = $tmp; |
|
489
|
|
|
// + Adjacent sibling selectors |
|
490
|
|
|
} else { |
|
491
|
|
|
if ($c == '+') { |
|
492
|
|
|
$spaceAllowed = true; |
|
493
|
|
|
$tmp .= $query[$i++]; |
|
494
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
495
|
|
|
&& ($this->isChar($query[$i]) || in_array($query[$i], $classChars) |
|
496
|
|
|
|| $query[$i] == '*' || ($spaceAllowed && $query[$i] == ' '))) { |
|
497
|
|
|
if ($query[$i] != ' ') { |
|
498
|
|
|
$spaceAllowed = false; |
|
499
|
|
|
} |
|
500
|
|
|
$tmp .= $query[$i]; |
|
501
|
|
|
$i++; |
|
502
|
|
|
} |
|
503
|
|
|
$return[] = $tmp; |
|
504
|
|
|
// ATTRS |
|
505
|
|
|
} else { |
|
506
|
|
|
if ($c == '[') { |
|
507
|
|
|
$stack = 1; |
|
508
|
|
|
$tmp .= $c; |
|
509
|
|
View Code Duplication |
while (isset($query[++$i])) { |
|
|
|
|
|
|
510
|
|
|
$tmp .= $query[$i]; |
|
511
|
|
|
if ($query[$i] == '[') { |
|
512
|
|
|
$stack++; |
|
513
|
|
|
} else { |
|
514
|
|
|
if ($query[$i] == ']') { |
|
515
|
|
|
$stack--; |
|
516
|
|
|
if (!$stack) { |
|
517
|
|
|
break; |
|
518
|
|
|
} |
|
519
|
|
|
} |
|
520
|
|
|
} |
|
521
|
|
|
} |
|
522
|
|
|
$return[] = $tmp; |
|
523
|
|
|
$i++; |
|
524
|
|
|
// PSEUDO CLASSES |
|
525
|
|
|
} else { |
|
526
|
|
|
if ($c == ':') { |
|
527
|
|
|
$stack = 1; |
|
|
|
|
|
|
528
|
|
|
$tmp .= $query[$i++]; |
|
529
|
|
View Code Duplication |
while (isset($query[$i]) |
|
|
|
|
|
|
530
|
|
|
&& ($this->isChar($query[$i]) || in_array($query[$i], $pseudoChars))) { |
|
531
|
|
|
$tmp .= $query[$i]; |
|
532
|
|
|
$i++; |
|
533
|
|
|
} |
|
534
|
|
|
// with arguments ? |
|
535
|
|
|
if (isset($query[$i]) && $query[$i] == '(') { |
|
536
|
|
|
$tmp .= $query[$i]; |
|
537
|
|
|
$stack = 1; |
|
538
|
|
View Code Duplication |
while (isset($query[++$i])) { |
|
|
|
|
|
|
539
|
|
|
$tmp .= $query[$i]; |
|
540
|
|
|
if ($query[$i] == '(') { |
|
541
|
|
|
$stack++; |
|
542
|
|
|
} else { |
|
543
|
|
|
if ($query[$i] == ')') { |
|
544
|
|
|
$stack--; |
|
545
|
|
|
if (!$stack) { |
|
546
|
|
|
break; |
|
547
|
|
|
} |
|
548
|
|
|
} |
|
549
|
|
|
} |
|
550
|
|
|
} |
|
551
|
|
|
$return[] = $tmp; |
|
552
|
|
|
$i++; |
|
553
|
|
|
} else { |
|
554
|
|
|
$return[] = $tmp; |
|
555
|
|
|
} |
|
556
|
|
|
} else { |
|
557
|
|
|
$i++; |
|
558
|
|
|
} |
|
559
|
|
|
} |
|
560
|
|
|
} |
|
561
|
|
|
} |
|
562
|
|
|
} |
|
563
|
|
|
foreach ($queries as $k => $q) { |
|
564
|
|
|
if (isset($q[0])) { |
|
565
|
|
|
if (isset($q[0][0]) && $q[0][0] == ':') |
|
566
|
|
|
array_unshift($queries[$k], '*'); |
|
567
|
|
|
if ($q[0] != '>') |
|
568
|
|
|
array_unshift($queries[$k], ' '); |
|
569
|
|
|
} |
|
570
|
|
|
} |
|
571
|
|
|
return $queries; |
|
572
|
|
|
} |
|
573
|
|
|
|
|
574
|
|
|
/** |
|
575
|
|
|
* Return matched DOM nodes. |
|
576
|
|
|
* |
|
577
|
|
|
* @param int $index |
|
578
|
|
|
* @return array|\DOMElement Single \DOMElement or array of \DOMElement. |
|
579
|
|
|
*/ |
|
580
|
|
|
public function get($index = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
581
|
|
|
{ |
|
582
|
|
|
$return = isset($index) ? (isset($this->elements[$index]) ? $this->elements[$index] |
|
583
|
|
|
: null) : $this->elements; |
|
584
|
|
|
// pass thou callbacks |
|
585
|
|
|
$args = func_get_args(); |
|
586
|
|
|
$args = array_slice($args, 1); |
|
587
|
|
View Code Duplication |
foreach ($args as $callback) { |
|
|
|
|
|
|
588
|
|
|
if (is_array($return)) |
|
589
|
|
|
foreach ($return as $k => $v) |
|
590
|
|
|
$return[$k] = PhpQuery::callbackRun( |
|
591
|
|
|
$callback, |
|
592
|
|
|
array( |
|
593
|
|
|
$v |
|
594
|
|
|
) |
|
595
|
|
|
); |
|
596
|
|
|
else |
|
597
|
|
|
$return = PhpQuery::callbackRun( |
|
598
|
|
|
$callback, |
|
599
|
|
|
array( |
|
600
|
|
|
$return |
|
601
|
|
|
) |
|
602
|
|
|
); |
|
603
|
|
|
} |
|
604
|
|
|
return $return; |
|
605
|
|
|
} |
|
606
|
|
|
|
|
607
|
|
|
/** |
|
608
|
|
|
* Return matched DOM nodes. |
|
609
|
|
|
* jQuery difference. |
|
610
|
|
|
* |
|
611
|
|
|
* @param int $index |
|
612
|
|
|
* @param null $callback1 |
|
613
|
|
|
* @param null $callback2 |
|
614
|
|
|
* @param null $callback3 |
|
615
|
|
|
* @return array|string Returns string if $index != null |
|
616
|
|
|
* @todo implement callbacks |
|
617
|
|
|
* @todo return only arrays ? |
|
618
|
|
|
* @todo maybe other name... |
|
619
|
|
|
*/ |
|
620
|
|
|
public function getString($index = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
621
|
|
|
{ |
|
622
|
|
|
if ($index) |
|
|
|
|
|
|
623
|
|
|
$return = $this->eq($index)->text(); |
|
624
|
|
|
else { |
|
625
|
|
|
$return = array(); |
|
626
|
|
View Code Duplication |
for ($i = 0; $i < $this->size(); $i++) { |
|
|
|
|
|
|
627
|
|
|
$return[] = $this->eq($i)->text(); |
|
628
|
|
|
} |
|
629
|
|
|
} |
|
630
|
|
|
// pass thou callbacks |
|
631
|
|
|
$args = func_get_args(); |
|
632
|
|
|
$args = array_slice($args, 1); |
|
633
|
|
|
foreach ($args as $callback) { |
|
634
|
|
|
$return = PhpQuery::callbackRun( |
|
635
|
|
|
$callback, |
|
636
|
|
|
array( |
|
637
|
|
|
$return |
|
638
|
|
|
) |
|
639
|
|
|
); |
|
640
|
|
|
} |
|
641
|
|
|
return $return; |
|
642
|
|
|
} |
|
643
|
|
|
|
|
644
|
|
|
/** |
|
645
|
|
|
* Return matched DOM nodes. |
|
646
|
|
|
* jQuery difference. |
|
647
|
|
|
* |
|
648
|
|
|
* @param int $index |
|
649
|
|
|
* @param null $callback1 |
|
650
|
|
|
* @param null $callback2 |
|
651
|
|
|
* @param null $callback3 |
|
652
|
|
|
* @return array|string Returns string if $index != null |
|
653
|
|
|
* @todo implement callbacks |
|
654
|
|
|
* @todo return only arrays ? |
|
655
|
|
|
* @todo maybe other name... |
|
656
|
|
|
*/ |
|
657
|
|
|
public function getStrings($index = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
658
|
|
|
{ |
|
659
|
|
|
if ($index) |
|
|
|
|
|
|
660
|
|
|
$return = $this->eq($index)->text(); |
|
661
|
|
|
else { |
|
662
|
|
|
$return = array(); |
|
663
|
|
View Code Duplication |
for ($i = 0; $i < $this->size(); $i++) { |
|
|
|
|
|
|
664
|
|
|
$return[] = $this->eq($i)->text(); |
|
665
|
|
|
} |
|
666
|
|
|
// pass thou callbacks |
|
667
|
|
|
$args = func_get_args(); |
|
668
|
|
|
$args = array_slice($args, 1); |
|
669
|
|
|
} |
|
670
|
|
View Code Duplication |
foreach ($args as $callback) { |
|
|
|
|
|
|
671
|
|
|
if (is_array($return)) |
|
672
|
|
|
foreach ($return as $k => $v) |
|
673
|
|
|
$return[$k] = PhpQuery::callbackRun( |
|
674
|
|
|
$callback, |
|
675
|
|
|
array( |
|
676
|
|
|
$v |
|
677
|
|
|
) |
|
678
|
|
|
); |
|
679
|
|
|
else |
|
680
|
|
|
$return = PhpQuery::callbackRun( |
|
681
|
|
|
$callback, |
|
682
|
|
|
array( |
|
683
|
|
|
$return |
|
684
|
|
|
) |
|
685
|
|
|
); |
|
686
|
|
|
} |
|
687
|
|
|
return $return; |
|
688
|
|
|
} |
|
689
|
|
|
|
|
690
|
|
|
/** |
|
691
|
|
|
* Returns new instance of actual class. |
|
692
|
|
|
* |
|
693
|
|
|
* @param array $newStack Optional. Will replace old stack with new and move old one to history.c |
|
694
|
|
|
* @return \PhpQuery\PhpQueryObject |
|
695
|
|
|
*/ |
|
696
|
|
|
public function newInstance($newStack = null) |
|
697
|
|
|
{ |
|
698
|
|
|
$class = get_class($this); |
|
699
|
|
|
// support inheritance by passing old object to overloaded constructor |
|
700
|
|
|
$new = $class != 'PhpQuery' ? new $class($this, $this->getDocumentID()) |
|
701
|
|
|
: new PhpQueryObject($this->getDocumentID()); |
|
702
|
|
|
$new->previous = $this; |
|
703
|
|
|
if (is_null($newStack)) { |
|
704
|
|
|
$new->elements = $this->elements; |
|
705
|
|
|
if ($this->elementsBackup) |
|
|
|
|
|
|
706
|
|
|
$this->elements = $this->elementsBackup; |
|
707
|
|
|
} else if (is_string($newStack)) { |
|
708
|
|
|
$new->elements = PhpQuery::pq($newStack, $this->getDocumentID())->stack(); |
|
709
|
|
|
} else { |
|
710
|
|
|
$new->elements = $newStack; |
|
711
|
|
|
} |
|
712
|
|
|
return $new; |
|
713
|
|
|
} |
|
714
|
|
|
|
|
715
|
|
|
/** |
|
716
|
|
|
* Enter description here... |
|
717
|
|
|
* |
|
718
|
|
|
* In the future, when PHP will support XLS 2.0, then we would do that this way: |
|
719
|
|
|
* contains(tokenize(@class, '\s'), "something") |
|
720
|
|
|
* @param string $class |
|
721
|
|
|
* @param string $node |
|
722
|
|
|
* @return boolean |
|
723
|
|
|
* @access private |
|
724
|
|
|
*/ |
|
725
|
|
|
protected function matchClasses($class, $node) |
|
726
|
|
|
{ |
|
727
|
|
|
// multi-class |
|
728
|
|
|
if (mb_strpos($class, '.', 1)) { |
|
729
|
|
|
$classes = explode('.', substr($class, 1)); |
|
730
|
|
|
$classesCount = count($classes); |
|
731
|
|
|
$nodeClasses = explode(' ', $node->getAttribute('class')); |
|
|
|
|
|
|
732
|
|
|
$nodeClassesCount = count($nodeClasses); |
|
733
|
|
|
if ($classesCount > $nodeClassesCount) |
|
734
|
|
|
return false; |
|
735
|
|
|
$diff = count(array_diff($classes, $nodeClasses)); |
|
736
|
|
|
if (!$diff) |
|
737
|
|
|
return true; |
|
738
|
|
|
// single-class |
|
739
|
|
|
} else { |
|
740
|
|
|
return in_array( |
|
741
|
|
|
// strip leading dot from class name |
|
742
|
|
|
substr($class, 1), |
|
743
|
|
|
// get classes for element as array |
|
744
|
|
|
explode(' ', $node->getAttribute('class')) |
|
|
|
|
|
|
745
|
|
|
); |
|
746
|
|
|
} |
|
747
|
|
|
} |
|
748
|
|
|
|
|
749
|
|
|
/** |
|
750
|
|
|
* @access private |
|
751
|
|
|
*/ |
|
752
|
|
|
protected function runQuery($XQuery, $selector = null, $compare = null) |
|
753
|
|
|
{ |
|
754
|
|
|
if ($compare && !method_exists($this, $compare)) |
|
755
|
|
|
return false; |
|
756
|
|
|
$stack = array(); |
|
757
|
|
|
if (!$this->elements) |
|
|
|
|
|
|
758
|
|
|
$this->debug('Stack empty, skipping...'); |
|
759
|
|
|
// var_dump($this->elements[0]->nodeType); |
|
|
|
|
|
|
760
|
|
|
// element, document |
|
761
|
|
|
foreach ($this->stack( |
|
762
|
|
|
array( |
|
763
|
|
|
1, |
|
764
|
|
|
9, |
|
765
|
|
|
13 |
|
766
|
|
|
) |
|
767
|
|
|
) as $k => $stackNode) { |
|
768
|
|
|
$detachAfter = false; |
|
769
|
|
|
// to work on detached nodes we need temporary place them somewhere |
|
770
|
|
|
// thats because context xpath queries sucks ;] |
|
771
|
|
|
$testNode = $stackNode; |
|
772
|
|
|
while ($testNode) { |
|
773
|
|
|
if (!$testNode->parentNode && !$this->isRoot($testNode)) { |
|
774
|
|
|
$this->root->appendChild($testNode); |
|
|
|
|
|
|
775
|
|
|
$detachAfter = $testNode; |
|
776
|
|
|
break; |
|
777
|
|
|
} |
|
778
|
|
|
$testNode = isset($testNode->parentNode) ? $testNode->parentNode : null; |
|
779
|
|
|
} |
|
780
|
|
|
// XXX tmp ? |
|
781
|
|
|
$xpath = $this->documentWrapper->isXHTML ? $this->getNodeXpath($stackNode, 'html') |
|
782
|
|
|
: $this->getNodeXpath($stackNode); |
|
783
|
|
|
// FIXME pseudoclasses-only query, support XML |
|
784
|
|
|
$query = $XQuery == '//' && $xpath == '/html[1]' ? '//*' |
|
785
|
|
|
: $xpath . $XQuery; |
|
786
|
|
|
$this->debug("XPATH: {$query}"); |
|
787
|
|
|
// run query, get elements |
|
788
|
|
|
$nodes = $this->xpath->query($query); |
|
789
|
|
|
$this->debug("QUERY FETCHED"); |
|
790
|
|
|
if (!$nodes->length) |
|
791
|
|
|
$this->debug('Nothing found'); |
|
792
|
|
|
$debug = array(); |
|
793
|
|
|
foreach ($nodes as $node) { |
|
794
|
|
|
$matched = false; |
|
795
|
|
|
if ($compare) { |
|
796
|
|
|
PhpQuery::$debug ? $this->debug( |
|
797
|
|
|
"Found: " . $this->whois($node) |
|
798
|
|
|
. ", comparing with {$compare}()" |
|
799
|
|
|
) : null; |
|
800
|
|
|
$PhpQueryDebug = PhpQuery::$debug; |
|
801
|
|
|
PhpQuery::$debug = false; |
|
802
|
|
|
// TODO ??? use PhpQuery::callbackRun() |
|
|
|
|
|
|
803
|
|
|
if (call_user_func_array( |
|
804
|
|
|
array( |
|
805
|
|
|
$this, |
|
806
|
|
|
$compare |
|
807
|
|
|
), |
|
808
|
|
|
array( |
|
809
|
|
|
$selector, |
|
810
|
|
|
$node |
|
811
|
|
|
) |
|
812
|
|
|
) |
|
813
|
|
|
) |
|
814
|
|
|
$matched = true; |
|
815
|
|
|
PhpQuery::$debug = $PhpQueryDebug; |
|
816
|
|
|
} else { |
|
817
|
|
|
$matched = true; |
|
818
|
|
|
} |
|
819
|
|
|
if ($matched) { |
|
820
|
|
|
if (PhpQuery::$debug) |
|
821
|
|
|
$debug[] = $this->whois($node); |
|
822
|
|
|
$stack[] = $node; |
|
823
|
|
|
} |
|
824
|
|
|
} |
|
825
|
|
|
if (PhpQuery::$debug) { |
|
826
|
|
|
$this->debug("Matched " . count($debug) . ": " . implode(', ', $debug)); |
|
827
|
|
|
} |
|
828
|
|
|
if ($detachAfter) |
|
829
|
|
|
$this->root->removeChild($detachAfter); |
|
|
|
|
|
|
830
|
|
|
} |
|
831
|
|
|
$this->elements = $stack; |
|
832
|
|
|
} |
|
833
|
|
|
|
|
834
|
|
|
/** |
|
835
|
|
|
* Enter description here... |
|
836
|
|
|
* |
|
837
|
|
|
* @param $selectors |
|
838
|
|
|
* @param null $context |
|
839
|
|
|
* @param bool $noHistory |
|
840
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
841
|
|
|
*/ |
|
842
|
|
|
public function find($selectors, $context = null, $noHistory = false) |
|
843
|
|
|
{ |
|
844
|
|
|
if (!$noHistory) |
|
845
|
|
|
// backup last stack /for end()/ |
|
846
|
|
|
$this->elementsBackup = $this->elements; |
|
847
|
|
|
// allow to define context |
|
848
|
|
|
// TODO combine code below with PhpQuery::pq() context guessing code |
|
849
|
|
|
// as generic function |
|
850
|
|
|
if (isset($context)) { |
|
851
|
|
|
if (!is_array($context) && $context instanceof \DOMElement) { |
|
852
|
|
|
$this->elements = array( |
|
853
|
|
|
$context |
|
854
|
|
|
); |
|
855
|
|
|
} elseif (is_array($context)) { |
|
856
|
|
|
$this->elements = array(); |
|
857
|
|
|
foreach ($context as $c) |
|
858
|
|
|
if ($c instanceof \DOMElement) |
|
859
|
|
|
$this->elements[] = $c; |
|
860
|
|
|
} elseif ($context instanceof PhpQueryObject) { |
|
861
|
|
|
$this->elements = $context->elements; |
|
862
|
|
|
} |
|
863
|
|
|
} |
|
864
|
|
|
|
|
865
|
|
|
$queries = $this->parseSelector($selectors); |
|
866
|
|
|
|
|
867
|
|
|
$this->debug( |
|
868
|
|
|
array( |
|
869
|
|
|
'FIND', |
|
870
|
|
|
$selectors, |
|
871
|
|
|
$queries |
|
872
|
|
|
) |
|
873
|
|
|
); |
|
874
|
|
|
$XQuery = ''; |
|
875
|
|
|
// remember stack state because of multi-queries |
|
876
|
|
|
$oldStack = $this->elements; |
|
877
|
|
|
// here we will be keeping found elements |
|
878
|
|
|
$stack = array(); |
|
879
|
|
|
foreach ($queries as $selector) { |
|
880
|
|
|
$this->elements = $oldStack; |
|
881
|
|
|
$delimiterBefore = false; |
|
882
|
|
|
foreach ($selector as $s) { |
|
883
|
|
|
// TAG |
|
884
|
|
|
$isTag = extension_loaded('mbstring') && PhpQuery::$mbstringSupport ? mb_ereg_match('^[\w|\||-]+$', $s) |
|
885
|
|
|
|| $s == '*' : preg_match('@^[\w|\||-]+$@', $s) || $s == '*'; |
|
886
|
|
|
if ($isTag) { |
|
887
|
|
|
if ($this->isXML()) { |
|
888
|
|
|
// namespace support |
|
889
|
|
|
if (mb_strpos($s, '|') !== false) { |
|
890
|
|
|
$ns = $tag = null; |
|
891
|
|
|
list($ns, $tag) = explode('|', $s); |
|
892
|
|
|
$XQuery .= "$ns:$tag"; |
|
893
|
|
|
} else if ($s == '*') { |
|
894
|
|
|
$XQuery .= "*"; |
|
895
|
|
|
} else { |
|
896
|
|
|
$XQuery .= "*[local-name()='$s']"; |
|
897
|
|
|
} |
|
898
|
|
|
} else { |
|
899
|
|
|
$XQuery .= $s; |
|
900
|
|
|
} |
|
901
|
|
|
// ID |
|
902
|
|
|
} else if ($s[0] == '#') { |
|
903
|
|
|
if ($delimiterBefore) |
|
904
|
|
|
$XQuery .= '*'; |
|
905
|
|
|
$XQuery .= "[@id='" . substr($s, 1) . "']"; |
|
906
|
|
|
// ATTRIBUTES |
|
907
|
|
|
} else if ($s[0] == '[') { |
|
908
|
|
|
if ($delimiterBefore) |
|
909
|
|
|
$XQuery .= '*'; |
|
910
|
|
|
// strip side brackets |
|
911
|
|
|
$attr = trim($s, ']['); |
|
912
|
|
|
$execute = false; |
|
913
|
|
|
// attr with specifed value |
|
914
|
|
|
if (mb_strpos($s, '=')) { |
|
915
|
|
|
$value = null; |
|
916
|
|
|
list($attr, $value) = explode('=', $attr); |
|
917
|
|
|
$value = trim($value, "'\""); |
|
918
|
|
|
if ($this->isRegexp($attr)) { |
|
919
|
|
|
// cut regexp character |
|
920
|
|
|
$attr = substr($attr, 0, -1); |
|
921
|
|
|
$execute = true; |
|
922
|
|
|
$XQuery .= "[@{$attr}]"; |
|
923
|
|
|
} else { |
|
924
|
|
|
$XQuery .= "[@{$attr}='{$value}']"; |
|
925
|
|
|
} |
|
926
|
|
|
// attr without specified value |
|
927
|
|
|
} else { |
|
928
|
|
|
$XQuery .= "[@{$attr}]"; |
|
929
|
|
|
} |
|
930
|
|
|
if ($execute) { |
|
931
|
|
|
$this->runQuery($XQuery, $s, 'is'); |
|
932
|
|
|
$XQuery = ''; |
|
933
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
934
|
|
|
break; |
|
935
|
|
|
} |
|
936
|
|
|
// CLASSES |
|
937
|
|
|
} else if ($s[0] == '.') { |
|
938
|
|
|
// TODO use return $this->find("./self::*[contains(concat(\" \",@class,\" \"), \" $class \")]"); |
|
|
|
|
|
|
939
|
|
|
// thx wizDom ;) |
|
940
|
|
|
if ($delimiterBefore) |
|
941
|
|
|
$XQuery .= '*'; |
|
942
|
|
|
$XQuery .= '[@class]'; |
|
943
|
|
|
$this->runQuery($XQuery, $s, 'matchClasses'); |
|
944
|
|
|
$XQuery = ''; |
|
945
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
946
|
|
|
break; |
|
947
|
|
|
// ~ General Sibling Selector |
|
948
|
|
|
} else if ($s[0] == '~') { |
|
949
|
|
|
$this->runQuery($XQuery); |
|
950
|
|
|
$XQuery = ''; |
|
951
|
|
|
$this->elements = $this->siblings(substr($s, 1))->elements; |
|
952
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
953
|
|
|
break; |
|
954
|
|
|
// + Adjacent sibling selectors |
|
955
|
|
|
} else if ($s[0] == '+') { |
|
956
|
|
|
// TODO /following-sibling:: |
|
957
|
|
|
$this->runQuery($XQuery); |
|
958
|
|
|
$XQuery = ''; |
|
959
|
|
|
$subSelector = substr($s, 1); |
|
960
|
|
|
$subElements = $this->elements; |
|
961
|
|
|
$this->elements = array(); |
|
962
|
|
|
foreach ($subElements as $node) { |
|
963
|
|
|
// search first \DOMElement sibling |
|
964
|
|
|
$test = $node->nextSibling; |
|
965
|
|
|
while ($test && !($test instanceof \DOMElement)) |
|
966
|
|
|
$test = $test->nextSibling; |
|
967
|
|
|
if ($test && $this->is($subSelector, $test)) |
|
968
|
|
|
$this->elements[] = $test; |
|
969
|
|
|
} |
|
970
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
971
|
|
|
break; |
|
972
|
|
|
// PSEUDO CLASSES |
|
973
|
|
|
} else if ($s[0] == ':') { |
|
974
|
|
|
// TODO optimization for :first :last |
|
975
|
|
|
if ($XQuery) { |
|
976
|
|
|
$this->runQuery($XQuery); |
|
977
|
|
|
$XQuery = ''; |
|
978
|
|
|
} |
|
979
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
980
|
|
|
break; |
|
981
|
|
|
$this->pseudoClasses($s); |
|
982
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
983
|
|
|
break; |
|
984
|
|
|
// DIRECT DESCENDANDS |
|
985
|
|
|
} else if ($s == '>') { |
|
986
|
|
|
$XQuery .= '/'; |
|
987
|
|
|
$delimiterBefore = 2; |
|
988
|
|
|
// ALL DESCENDANDS |
|
989
|
|
|
} else if ($s == ' ') { |
|
990
|
|
|
$XQuery .= '//'; |
|
991
|
|
|
$delimiterBefore = 2; |
|
992
|
|
|
// ERRORS |
|
993
|
|
|
} else { |
|
994
|
|
|
PhpQuery::debug("Unrecognized token '$s'"); |
|
995
|
|
|
} |
|
996
|
|
|
$delimiterBefore = $delimiterBefore === 2; |
|
997
|
|
|
} |
|
998
|
|
|
// run query if any |
|
999
|
|
|
if ($XQuery && $XQuery != '//') { |
|
1000
|
|
|
$this->runQuery($XQuery); |
|
1001
|
|
|
$XQuery = ''; |
|
1002
|
|
|
} |
|
1003
|
|
|
foreach ($this->elements as $node) |
|
1004
|
|
|
if (!$this->elementsContainsNode($node, $stack)) |
|
1005
|
|
|
$stack[] = $node; |
|
1006
|
|
|
} |
|
1007
|
|
|
$this->elements = $stack; |
|
1008
|
|
|
return $this->newInstance(); |
|
1009
|
|
|
} |
|
1010
|
|
|
|
|
1011
|
|
|
/** |
|
1012
|
|
|
* @todo create API for classes with pseudoselectors |
|
1013
|
|
|
* @access private |
|
1014
|
|
|
*/ |
|
1015
|
|
|
protected function pseudoClasses($class) |
|
1016
|
|
|
{ |
|
1017
|
|
|
// TODO clean args parsing ? |
|
1018
|
|
|
$class = ltrim($class, ':'); |
|
1019
|
|
|
$haveArgs = mb_strpos($class, '('); |
|
1020
|
|
|
if ($haveArgs !== false) { |
|
1021
|
|
|
$args = substr($class, $haveArgs + 1, -1); |
|
1022
|
|
|
$class = substr($class, 0, $haveArgs); |
|
1023
|
|
|
} |
|
1024
|
|
|
switch ($class) { |
|
1025
|
|
|
case 'even': |
|
1026
|
|
|
case 'odd': |
|
1027
|
|
|
$stack = array(); |
|
1028
|
|
|
foreach ($this->elements as $i => $node) { |
|
1029
|
|
|
if ($class == 'even' && ($i % 2) == 0) |
|
1030
|
|
|
$stack[] = $node; |
|
1031
|
|
|
else if ($class == 'odd' && $i % 2) |
|
1032
|
|
|
$stack[] = $node; |
|
1033
|
|
|
} |
|
1034
|
|
|
$this->elements = $stack; |
|
1035
|
|
|
break; |
|
1036
|
|
|
case 'eq': |
|
1037
|
|
|
$k = intval($args); |
|
|
|
|
|
|
1038
|
|
|
$this->elements = isset($this->elements[$k]) ? array( |
|
1039
|
|
|
$this->elements[$k] |
|
1040
|
|
|
) : array(); |
|
1041
|
|
|
break; |
|
1042
|
|
|
case 'gt': |
|
1043
|
|
|
$this->elements = array_slice($this->elements, $args + 1); |
|
1044
|
|
|
break; |
|
1045
|
|
|
case 'lt': |
|
1046
|
|
|
$this->elements = array_slice($this->elements, 0, $args + 1); |
|
1047
|
|
|
break; |
|
1048
|
|
|
case 'first': |
|
1049
|
|
|
if (isset($this->elements[0])) |
|
1050
|
|
|
$this->elements = array( |
|
1051
|
|
|
$this->elements[0] |
|
1052
|
|
|
); |
|
1053
|
|
|
break; |
|
1054
|
|
|
case 'last': |
|
1055
|
|
|
if ($this->elements) |
|
|
|
|
|
|
1056
|
|
|
$this->elements = array( |
|
1057
|
|
|
$this->elements[count($this->elements) - 1] |
|
1058
|
|
|
); |
|
1059
|
|
|
break; |
|
1060
|
|
|
/*case 'parent': |
|
|
|
|
|
|
1061
|
|
|
$stack = array(); |
|
1062
|
|
|
foreach($this->elements as $node) { |
|
1063
|
|
|
if ( $node->childNodes->length ) |
|
1064
|
|
|
$stack[] = $node; |
|
1065
|
|
|
} |
|
1066
|
|
|
$this->elements = $stack; |
|
1067
|
|
|
break;*/ |
|
1068
|
|
|
case 'contains': |
|
1069
|
|
|
$text = trim($args, "\"'"); |
|
1070
|
|
|
$stack = array(); |
|
1071
|
|
|
foreach ($this->elements as $node) { |
|
1072
|
|
|
if (mb_stripos($node->textContent, $text) === false) |
|
1073
|
|
|
continue; |
|
1074
|
|
|
$stack[] = $node; |
|
1075
|
|
|
} |
|
1076
|
|
|
$this->elements = $stack; |
|
1077
|
|
|
break; |
|
1078
|
|
|
case 'not': |
|
1079
|
|
|
$selector = self::unQuote($args); |
|
1080
|
|
|
$this->elements = $this->not($selector)->stack(); |
|
1081
|
|
|
break; |
|
1082
|
|
|
case 'slice': |
|
1083
|
|
|
// TODO jQuery difference ? |
|
1084
|
|
|
$args = explode(',', str_replace(', ', ',', trim($args, "\"'"))); |
|
1085
|
|
|
$start = $args[0]; |
|
1086
|
|
|
$end = isset($args[1]) ? $args[1] : null; |
|
1087
|
|
|
if ($end > 0) |
|
1088
|
|
|
$end = $end - $start; |
|
1089
|
|
|
$this->elements = array_slice($this->elements, $start, $end); |
|
1090
|
|
|
break; |
|
1091
|
|
|
case 'has': |
|
1092
|
|
|
$selector = trim($args, "\"'"); |
|
1093
|
|
|
$stack = array(); |
|
1094
|
|
|
foreach ($this->stack(1) as $el) { |
|
1095
|
|
|
if ($this->find($selector, $el, true)->length) |
|
1096
|
|
|
$stack[] = $el; |
|
1097
|
|
|
} |
|
1098
|
|
|
$this->elements = $stack; |
|
1099
|
|
|
break; |
|
1100
|
|
|
case 'submit': |
|
1101
|
|
|
case 'reset': |
|
1102
|
|
|
$this->elements = PhpQuery::merge( |
|
1103
|
|
|
$this->map( |
|
|
|
|
|
|
1104
|
|
|
array( |
|
1105
|
|
|
$this, |
|
1106
|
|
|
'is' |
|
1107
|
|
|
), |
|
1108
|
|
|
"input[type=$class]", |
|
1109
|
|
|
new \CallbackParam() |
|
1110
|
|
|
), |
|
1111
|
|
|
$this->map( |
|
|
|
|
|
|
1112
|
|
|
array( |
|
1113
|
|
|
$this, |
|
1114
|
|
|
'is' |
|
1115
|
|
|
), |
|
1116
|
|
|
"button[type=$class]", |
|
1117
|
|
|
new \CallbackParam() |
|
1118
|
|
|
) |
|
1119
|
|
|
); |
|
1120
|
|
|
break; |
|
1121
|
|
|
// $stack = array(); |
|
|
|
|
|
|
1122
|
|
|
// foreach($this->elements as $node) |
|
|
|
|
|
|
1123
|
|
|
// if ($node->is('input[type=submit]') || $node->is('button[type=submit]')) |
|
|
|
|
|
|
1124
|
|
|
// $stack[] = $el; |
|
|
|
|
|
|
1125
|
|
|
// $this->elements = $stack; |
|
|
|
|
|
|
1126
|
|
|
case 'input': |
|
1127
|
|
|
$this->elements = $this->map( |
|
1128
|
|
|
array( |
|
1129
|
|
|
$this, |
|
1130
|
|
|
'is' |
|
1131
|
|
|
), |
|
1132
|
|
|
'input', |
|
1133
|
|
|
new \CallbackParam() |
|
1134
|
|
|
)->elements; |
|
1135
|
|
|
break; |
|
1136
|
|
|
case 'password': |
|
1137
|
|
|
case 'checkbox': |
|
1138
|
|
|
case 'radio': |
|
1139
|
|
|
case 'hidden': |
|
1140
|
|
|
case 'image': |
|
1141
|
|
View Code Duplication |
case 'file': |
|
|
|
|
|
|
1142
|
|
|
$this->elements = $this->map( |
|
1143
|
|
|
array( |
|
1144
|
|
|
$this, |
|
1145
|
|
|
'is' |
|
1146
|
|
|
), |
|
1147
|
|
|
"input[type=$class]", |
|
1148
|
|
|
new \CallbackParam() |
|
1149
|
|
|
)->elements; |
|
1150
|
|
|
break; |
|
1151
|
|
|
case 'parent': |
|
1152
|
|
|
$this->elements = $this->map( |
|
1153
|
|
|
create_function( |
|
|
|
|
|
|
1154
|
|
|
'$node', |
|
1155
|
|
|
' |
|
1156
|
|
|
return $node instanceof \DOMElement && $node->childNodes->length |
|
1157
|
|
|
? $node : null;' |
|
1158
|
|
|
) |
|
1159
|
|
|
)->elements; |
|
1160
|
|
|
break; |
|
1161
|
|
|
case 'empty': |
|
1162
|
|
|
$this->elements = $this->map( |
|
1163
|
|
|
create_function( |
|
|
|
|
|
|
1164
|
|
|
'$node', |
|
1165
|
|
|
' |
|
1166
|
|
|
return $node instanceof \DOMElement && $node->childNodes->length |
|
1167
|
|
|
? null : $node;' |
|
1168
|
|
|
) |
|
1169
|
|
|
)->elements; |
|
1170
|
|
|
break; |
|
1171
|
|
|
case 'disabled': |
|
1172
|
|
|
case 'selected': |
|
1173
|
|
View Code Duplication |
case 'checked': |
|
|
|
|
|
|
1174
|
|
|
$this->elements = $this->map( |
|
1175
|
|
|
array( |
|
1176
|
|
|
$this, |
|
1177
|
|
|
'is' |
|
1178
|
|
|
), |
|
1179
|
|
|
"[$class]", |
|
1180
|
|
|
new \CallbackParam() |
|
1181
|
|
|
)->elements; |
|
1182
|
|
|
break; |
|
1183
|
|
|
case 'enabled': |
|
1184
|
|
|
$this->elements = $this->map( |
|
1185
|
|
|
create_function( |
|
|
|
|
|
|
1186
|
|
|
'$node', |
|
1187
|
|
|
' |
|
1188
|
|
|
return pq($node)->not(":disabled") ? $node : null;' |
|
1189
|
|
|
) |
|
1190
|
|
|
)->elements; |
|
1191
|
|
|
break; |
|
1192
|
|
|
case 'header': |
|
1193
|
|
|
$this->elements = $this->map( |
|
1194
|
|
|
create_function( |
|
|
|
|
|
|
1195
|
|
|
'$node', |
|
1196
|
|
|
'$isHeader = isset($node->tagName) && in_array($node->tagName, array( |
|
1197
|
|
|
"h1", "h2", "h3", "h4", "h5", "h6", "h7" |
|
1198
|
|
|
)); |
|
1199
|
|
|
return $isHeader |
|
1200
|
|
|
? $node |
|
1201
|
|
|
: null;' |
|
1202
|
|
|
) |
|
1203
|
|
|
)->elements; |
|
1204
|
|
|
// $this->elements = $this->map( |
|
|
|
|
|
|
1205
|
|
|
// create_function('$node', '$node = pq($node); |
|
1206
|
|
|
// return $node->is("h1") |
|
|
|
|
|
|
1207
|
|
|
// || $node->is("h2") |
|
|
|
|
|
|
1208
|
|
|
// || $node->is("h3") |
|
|
|
|
|
|
1209
|
|
|
// || $node->is("h4") |
|
|
|
|
|
|
1210
|
|
|
// || $node->is("h5") |
|
|
|
|
|
|
1211
|
|
|
// || $node->is("h6") |
|
|
|
|
|
|
1212
|
|
|
// || $node->is("h7") |
|
|
|
|
|
|
1213
|
|
|
// ? $node |
|
1214
|
|
|
// : null;') |
|
1215
|
|
|
// )->elements; |
|
1216
|
|
|
break; |
|
1217
|
|
|
case 'only-child': |
|
1218
|
|
|
$this->elements = $this->map( |
|
1219
|
|
|
create_function('$node', 'return pq($node)->siblings()->size() == 0 ? $node : null;') |
|
|
|
|
|
|
1220
|
|
|
)->elements; |
|
1221
|
|
|
break; |
|
1222
|
|
|
case 'first-child': |
|
1223
|
|
|
$this->elements = $this->map( |
|
1224
|
|
|
create_function('$node', 'return pq($node)->prevAll()->size() == 0 ? $node : null;') |
|
|
|
|
|
|
1225
|
|
|
)->elements; |
|
1226
|
|
|
break; |
|
1227
|
|
|
case 'last-child': |
|
1228
|
|
|
$this->elements = $this->map( |
|
1229
|
|
|
create_function('$node', 'return pq($node)->nextAll()->size() == 0 ? $node : null;') |
|
|
|
|
|
|
1230
|
|
|
)->elements; |
|
1231
|
|
|
break; |
|
1232
|
|
|
case 'nth-child': |
|
1233
|
|
|
$param = trim($args, "\"'"); |
|
1234
|
|
|
if (!$param) |
|
1235
|
|
|
break; |
|
1236
|
|
|
// nth-child(n+b) to nth-child(1n+b) |
|
1237
|
|
|
if ($param{0} == 'n') |
|
1238
|
|
|
$param = '1' . $param; |
|
1239
|
|
|
// :nth-child(index/even/odd/equation) |
|
1240
|
|
|
if ($param == 'even' || $param == 'odd') |
|
1241
|
|
|
$mapped = $this->map( |
|
1242
|
|
|
create_function( |
|
|
|
|
|
|
1243
|
|
|
'$node, $param', |
|
1244
|
|
|
'$index = pq($node)->prevAll()->size()+1; |
|
1245
|
|
|
if ($param == "even" && ($index%2) == 0) |
|
1246
|
|
|
return $node; |
|
1247
|
|
|
else if ($param == "odd" && $index%2 == 1) |
|
1248
|
|
|
return $node; |
|
1249
|
|
|
else |
|
1250
|
|
|
return null;' |
|
1251
|
|
|
), |
|
1252
|
|
|
new \CallbackParam(), |
|
1253
|
|
|
$param |
|
1254
|
|
|
); |
|
1255
|
|
|
else if (mb_strlen($param) > 1 |
|
1256
|
|
|
&& preg_match('/^(\d*)n([-+]?)(\d*)/', $param) === 1 |
|
1257
|
|
|
) |
|
1258
|
|
|
// an+b |
|
1259
|
|
|
$mapped = $this->map( |
|
1260
|
|
|
create_function( |
|
|
|
|
|
|
1261
|
|
|
'$node, $param', |
|
1262
|
|
|
'$prevs = pq($node)->prevAll()->size(); |
|
1263
|
|
|
$index = 1+$prevs; |
|
1264
|
|
|
|
|
1265
|
|
|
preg_match("/^(\d*)n([-+]?)(\d*)/", $param, $matches); |
|
1266
|
|
|
$a = intval($matches[1]); |
|
1267
|
|
|
$b = intval($matches[3]); |
|
1268
|
|
|
if( $matches[2] === "-" ) { |
|
1269
|
|
|
$b = -$b; |
|
1270
|
|
|
} |
|
1271
|
|
|
|
|
1272
|
|
|
if ($a > 0) { |
|
1273
|
|
|
return ($index-$b)%$a == 0 |
|
1274
|
|
|
? $node |
|
1275
|
|
|
: null; |
|
1276
|
|
|
PhpQuery::debug($a."*".floor($index/$a)."+$b-1 == ".($a*floor($index/$a)+$b-1)." ?= $prevs"); |
|
1277
|
|
|
return $a*floor($index/$a)+$b-1 == $prevs |
|
1278
|
|
|
? $node |
|
1279
|
|
|
: null; |
|
1280
|
|
|
} else if ($a == 0) |
|
1281
|
|
|
return $index == $b |
|
1282
|
|
|
? $node |
|
1283
|
|
|
: null; |
|
1284
|
|
|
else |
|
1285
|
|
|
// negative value |
|
1286
|
|
|
return $index <= $b |
|
1287
|
|
|
? $node |
|
1288
|
|
|
: null; |
|
1289
|
|
|
// if (! $b) |
|
1290
|
|
|
// return $index%$a == 0 |
|
1291
|
|
|
// ? $node |
|
1292
|
|
|
// : null; |
|
1293
|
|
|
// else |
|
1294
|
|
|
// return ($index-$b)%$a == 0 |
|
1295
|
|
|
// ? $node |
|
1296
|
|
|
// : null; |
|
1297
|
|
|
' |
|
1298
|
|
|
), |
|
1299
|
|
|
new \CallbackParam(), |
|
1300
|
|
|
$param |
|
1301
|
|
|
); |
|
1302
|
|
|
else |
|
1303
|
|
|
// index |
|
1304
|
|
|
$mapped = $this->map( |
|
1305
|
|
|
create_function( |
|
|
|
|
|
|
1306
|
|
|
'$node, $index', |
|
1307
|
|
|
'$prevs = pq($node)->prevAll()->size(); |
|
1308
|
|
|
if ($prevs && $prevs == $index-1) |
|
1309
|
|
|
return $node; |
|
1310
|
|
|
else if (! $prevs && $index == 1) |
|
1311
|
|
|
return $node; |
|
1312
|
|
|
else |
|
1313
|
|
|
return null;' |
|
1314
|
|
|
), |
|
1315
|
|
|
new \CallbackParam(), |
|
1316
|
|
|
$param |
|
1317
|
|
|
); |
|
1318
|
|
|
$this->elements = $mapped->elements; |
|
1319
|
|
|
break; |
|
1320
|
|
|
default: |
|
1321
|
|
|
$this->debug("Unknown pseudoclass '{$class}', skipping..."); |
|
1322
|
|
|
} |
|
1323
|
|
|
} |
|
1324
|
|
|
|
|
1325
|
|
|
/** |
|
1326
|
|
|
* @access private |
|
1327
|
|
|
*/ |
|
1328
|
|
|
protected function __pseudoClassParam($paramsString) |
|
|
|
|
|
|
1329
|
|
|
{ |
|
1330
|
|
|
// TODO; |
|
1331
|
|
|
} |
|
1332
|
|
|
|
|
1333
|
|
|
/** |
|
1334
|
|
|
* Enter description here... |
|
1335
|
|
|
* |
|
1336
|
|
|
* @param $selector |
|
1337
|
|
|
* @param null $nodes |
|
1338
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1339
|
|
|
*/ |
|
1340
|
|
|
public function is($selector, $nodes = null) |
|
1341
|
|
|
{ |
|
1342
|
|
|
PhpQuery::debug( |
|
1343
|
|
|
array( |
|
1344
|
|
|
"Is:", |
|
1345
|
|
|
$selector |
|
1346
|
|
|
) |
|
1347
|
|
|
); |
|
1348
|
|
|
if (!$selector) |
|
1349
|
|
|
return false; |
|
1350
|
|
|
$oldStack = $this->elements; |
|
1351
|
|
|
$returnArray = false; |
|
|
|
|
|
|
1352
|
|
|
if ($nodes && is_array($nodes)) { |
|
1353
|
|
|
$this->elements = $nodes; |
|
1354
|
|
|
} else if ($nodes) |
|
1355
|
|
|
$this->elements = array( |
|
1356
|
|
|
$nodes |
|
1357
|
|
|
); |
|
1358
|
|
|
$this->filter($selector, true); |
|
1359
|
|
|
$stack = $this->elements; |
|
1360
|
|
|
$this->elements = $oldStack; |
|
1361
|
|
|
if ($nodes) |
|
1362
|
|
|
return $stack ? $stack : null; |
|
1363
|
|
|
return (bool) count($stack); |
|
1364
|
|
|
} |
|
1365
|
|
|
|
|
1366
|
|
|
/** |
|
1367
|
|
|
* Enter description here... |
|
1368
|
|
|
* jQuery difference. |
|
1369
|
|
|
* |
|
1370
|
|
|
* Callback: |
|
1371
|
|
|
* - $index int |
|
1372
|
|
|
* - $node DOMNode |
|
1373
|
|
|
* |
|
1374
|
|
|
* @param $callback |
|
1375
|
|
|
* @param bool $_skipHistory |
|
1376
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1377
|
|
|
* @link http://docs.jquery.com/Traversing/filter |
|
1378
|
|
|
*/ |
|
1379
|
|
|
public function filterCallback($callback, $_skipHistory = false) |
|
1380
|
|
|
{ |
|
1381
|
|
|
if (!$_skipHistory) { |
|
1382
|
|
|
$this->elementsBackup = $this->elements; |
|
1383
|
|
|
$this->debug("Filtering by callback"); |
|
1384
|
|
|
} |
|
1385
|
|
|
$newStack = array(); |
|
1386
|
|
|
foreach ($this->elements as $index => $node) { |
|
1387
|
|
|
$result = PhpQuery::callbackRun( |
|
1388
|
|
|
$callback, |
|
1389
|
|
|
array( |
|
1390
|
|
|
$index, |
|
1391
|
|
|
$node |
|
1392
|
|
|
) |
|
1393
|
|
|
); |
|
1394
|
|
|
if (is_null($result) || (!is_null($result) && $result)) |
|
1395
|
|
|
$newStack[] = $node; |
|
1396
|
|
|
} |
|
1397
|
|
|
$this->elements = $newStack; |
|
1398
|
|
|
return $_skipHistory ? $this : $this->newInstance(); |
|
1399
|
|
|
} |
|
1400
|
|
|
|
|
1401
|
|
|
/** |
|
1402
|
|
|
* Enter description here... |
|
1403
|
|
|
* |
|
1404
|
|
|
* @param $selectors |
|
1405
|
|
|
* @param bool $_skipHistory |
|
1406
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1407
|
|
|
* @link http://docs.jquery.com/Traversing/filter |
|
1408
|
|
|
*/ |
|
1409
|
|
|
public function filter($selectors, $_skipHistory = false) |
|
1410
|
|
|
{ |
|
1411
|
|
|
if ($selectors instanceof \Callback OR $selectors instanceof \Closure) |
|
|
|
|
|
|
1412
|
|
|
return $this->filterCallback($selectors, $_skipHistory); |
|
1413
|
|
|
if (!$_skipHistory) |
|
1414
|
|
|
$this->elementsBackup = $this->elements; |
|
1415
|
|
|
$notSimpleSelector = array( |
|
1416
|
|
|
' ', |
|
1417
|
|
|
'>', |
|
1418
|
|
|
'~', |
|
1419
|
|
|
'+', |
|
1420
|
|
|
'/' |
|
1421
|
|
|
); |
|
1422
|
|
|
if (!is_array($selectors)) |
|
1423
|
|
|
$selectors = $this->parseSelector($selectors); |
|
1424
|
|
|
if (!$_skipHistory) |
|
1425
|
|
|
$this->debug( |
|
1426
|
|
|
array( |
|
1427
|
|
|
"Filtering:", |
|
1428
|
|
|
$selectors |
|
1429
|
|
|
) |
|
1430
|
|
|
); |
|
1431
|
|
|
$finalStack = array(); |
|
1432
|
|
|
foreach ($selectors as $selector) { |
|
1433
|
|
|
$stack = array(); |
|
1434
|
|
|
if (!$selector) |
|
1435
|
|
|
break; |
|
1436
|
|
|
// avoid first space or / |
|
1437
|
|
|
if (in_array($selector[0], $notSimpleSelector)) |
|
1438
|
|
|
$selector = array_slice($selector, 1); |
|
1439
|
|
|
// PER NODE selector chunks |
|
1440
|
|
|
foreach ($this->stack() as $node) { |
|
1441
|
|
|
$break = false; |
|
1442
|
|
|
foreach ($selector as $s) { |
|
1443
|
|
|
if (!($node instanceof \DOMElement)) { |
|
1444
|
|
|
// all besides \DOMElement |
|
1445
|
|
|
if ($s[0] == '[') { |
|
1446
|
|
|
$attr = trim($s, '[]'); |
|
1447
|
|
|
if (mb_strpos($attr, '=')) { |
|
1448
|
|
|
list($attr, $val) = explode('=', $attr); |
|
1449
|
|
|
if ($attr == 'nodeType' && $node->nodeType != $val) |
|
1450
|
|
|
$break = true; |
|
1451
|
|
|
} |
|
1452
|
|
|
} else |
|
1453
|
|
|
$break = true; |
|
1454
|
|
|
} else { |
|
1455
|
|
|
// \DOMElement only |
|
1456
|
|
|
// ID |
|
1457
|
|
|
if ($s[0] == '#') { |
|
1458
|
|
|
if ($node->getAttribute('id') != substr($s, 1)) |
|
1459
|
|
|
$break = true; |
|
1460
|
|
|
// CLASSES |
|
1461
|
|
|
} else if ($s[0] == '.') { |
|
1462
|
|
|
if (!$this->matchClasses($s, $node)) |
|
|
|
|
|
|
1463
|
|
|
$break = true; |
|
1464
|
|
|
// ATTRS |
|
1465
|
|
|
} else if ($s[0] == '[') { |
|
1466
|
|
|
// strip side brackets |
|
1467
|
|
|
$attr = trim($s, '[]'); |
|
1468
|
|
|
if (mb_strpos($attr, '=')) { |
|
1469
|
|
|
list($attr, $val) = explode('=', $attr); |
|
1470
|
|
|
$val = self::unQuote($val); |
|
1471
|
|
|
if ($attr == 'nodeType') { |
|
1472
|
|
|
if ($val != $node->nodeType) |
|
1473
|
|
|
$break = true; |
|
1474
|
|
|
} else if ($this->isRegexp($attr)) { |
|
1475
|
|
|
$val = extension_loaded('mbstring') |
|
1476
|
|
|
&& PhpQuery::$mbstringSupport ? quotemeta(trim($val, '"\'')) |
|
1477
|
|
|
: preg_quote(trim($val, '"\''), '@'); |
|
1478
|
|
|
// switch last character |
|
1479
|
|
|
switch (substr($attr, -1)) { |
|
1480
|
|
|
// quotemeta used insted of preg_quote |
|
1481
|
|
|
// http://code.google.com/p/phpquery/issues/detail?id=76 |
|
1482
|
|
|
case '^': |
|
1483
|
|
|
$pattern = '^' . $val; |
|
1484
|
|
|
break; |
|
1485
|
|
|
case '*': |
|
1486
|
|
|
$pattern = '.*' . $val . '.*'; |
|
1487
|
|
|
break; |
|
1488
|
|
|
case '$': |
|
1489
|
|
|
$pattern = '.*' . $val . '$'; |
|
1490
|
|
|
break; |
|
1491
|
|
|
} |
|
1492
|
|
|
// cut last character |
|
1493
|
|
|
$attr = substr($attr, 0, -1); |
|
1494
|
|
|
$isMatch = extension_loaded('mbstring') |
|
1495
|
|
|
&& PhpQuery::$mbstringSupport ? mb_ereg_match($pattern, $node->getAttribute($attr)) |
|
|
|
|
|
|
1496
|
|
|
: preg_match("@{$pattern}@", $node->getAttribute($attr)); |
|
1497
|
|
|
if (!$isMatch) |
|
1498
|
|
|
$break = true; |
|
1499
|
|
|
} else if ($node->getAttribute($attr) != $val) |
|
1500
|
|
|
$break = true; |
|
1501
|
|
|
} else if (!$node->hasAttribute($attr)) |
|
1502
|
|
|
$break = true; |
|
1503
|
|
|
// PSEUDO CLASSES |
|
1504
|
|
|
} else if ($s[0] == ':') { |
|
|
|
|
|
|
1505
|
|
|
// skip |
|
1506
|
|
|
// TAG |
|
1507
|
|
|
} else if (trim($s)) { |
|
1508
|
|
|
if ($s != '*') { |
|
1509
|
|
|
// TODO namespaces |
|
1510
|
|
|
if (isset($node->tagName)) { |
|
1511
|
|
|
if ($node->tagName != $s) |
|
1512
|
|
|
$break = true; |
|
1513
|
|
|
} else if ($s == 'html' && !$this->isRoot($node)) |
|
1514
|
|
|
$break = true; |
|
1515
|
|
|
} |
|
1516
|
|
|
// AVOID NON-SIMPLE SELECTORS |
|
1517
|
|
|
} else if (in_array($s, $notSimpleSelector)) { |
|
1518
|
|
|
$break = true; |
|
1519
|
|
|
$this->debug( |
|
1520
|
|
|
array( |
|
1521
|
|
|
'Skipping non simple selector', |
|
1522
|
|
|
$selector |
|
1523
|
|
|
) |
|
1524
|
|
|
); |
|
1525
|
|
|
} |
|
1526
|
|
|
} |
|
1527
|
|
|
if ($break) |
|
1528
|
|
|
break; |
|
1529
|
|
|
} |
|
1530
|
|
|
// if element passed all chunks of selector - add it to new stack |
|
1531
|
|
|
if (!$break) |
|
1532
|
|
|
$stack[] = $node; |
|
1533
|
|
|
} |
|
1534
|
|
|
$tmpStack = $this->elements; |
|
1535
|
|
|
$this->elements = $stack; |
|
1536
|
|
|
// PER ALL NODES selector chunks |
|
1537
|
|
|
foreach ($selector as $s) |
|
1538
|
|
|
// PSEUDO CLASSES |
|
1539
|
|
|
if ($s[0] == ':') |
|
1540
|
|
|
$this->pseudoClasses($s); |
|
1541
|
|
|
foreach ($this->elements as $node) |
|
1542
|
|
|
// XXX it should be merged without duplicates |
|
1543
|
|
|
// but jQuery doesnt do that |
|
1544
|
|
|
$finalStack[] = $node; |
|
1545
|
|
|
$this->elements = $tmpStack; |
|
1546
|
|
|
} |
|
1547
|
|
|
$this->elements = $finalStack; |
|
1548
|
|
|
if ($_skipHistory) { |
|
1549
|
|
|
return $this; |
|
1550
|
|
|
} else { |
|
1551
|
|
|
$this->debug("Stack length after filter(): " . count($finalStack)); |
|
1552
|
|
|
return $this->newInstance(); |
|
1553
|
|
|
} |
|
1554
|
|
|
} |
|
1555
|
|
|
|
|
1556
|
|
|
/** |
|
1557
|
|
|
* |
|
1558
|
|
|
* @param $value |
|
1559
|
|
|
* @return string |
|
1560
|
|
|
* @TODO implement in all methods using passed parameters |
|
1561
|
|
|
*/ |
|
1562
|
|
|
protected static function unQuote($value) |
|
1563
|
|
|
{ |
|
1564
|
|
|
return $value[0] == '\'' || $value[0] == '"' ? substr($value, 1, -1) |
|
1565
|
|
|
: $value; |
|
1566
|
|
|
} |
|
1567
|
|
|
|
|
1568
|
|
|
/** |
|
1569
|
|
|
* Enter description here... |
|
1570
|
|
|
* |
|
1571
|
|
|
* @link http://docs.jquery.com/Ajax/load |
|
1572
|
|
|
* @param $url |
|
1573
|
|
|
* @param null $data |
|
1574
|
|
|
* @param null $callback |
|
1575
|
|
|
* @return PhpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1576
|
|
|
* @todo Support $selector |
|
1577
|
|
|
*/ |
|
1578
|
|
|
public function load($url, $data = null, $callback = null) |
|
1579
|
|
|
{ |
|
1580
|
|
|
if ($data && !is_array($data)) { |
|
1581
|
|
|
$callback = $data; |
|
1582
|
|
|
$data = null; |
|
1583
|
|
|
} |
|
1584
|
|
|
if (mb_strpos($url, ' ') !== false) { |
|
1585
|
|
|
$matches = null; |
|
1586
|
|
|
if (extension_loaded('mbstring') && PhpQuery::$mbstringSupport) |
|
1587
|
|
|
mb_ereg('^([^ ]+) (.*)$', $url, $matches); |
|
1588
|
|
|
else |
|
1589
|
|
|
preg_match('@^([^ ]+) (.*)$@', $url, $matches); |
|
1590
|
|
|
$url = $matches[1]; |
|
1591
|
|
|
$selector = $matches[2]; |
|
1592
|
|
|
// FIXME this sucks, pass as callback param |
|
1593
|
|
|
$this->_loadSelector = $selector; |
|
|
|
|
|
|
1594
|
|
|
} |
|
1595
|
|
|
$ajax = array( |
|
1596
|
|
|
'url' => $url, |
|
1597
|
|
|
'type' => $data ? 'POST' : 'GET', |
|
1598
|
|
|
'data' => $data, |
|
1599
|
|
|
'complete' => $callback, |
|
1600
|
|
|
'success' => array( |
|
1601
|
|
|
$this, |
|
1602
|
|
|
'__loadSuccess' |
|
1603
|
|
|
) |
|
1604
|
|
|
); |
|
1605
|
|
|
PhpQuery::ajax($ajax); |
|
1606
|
|
|
return $this; |
|
1607
|
|
|
} |
|
1608
|
|
|
|
|
1609
|
|
|
/** |
|
1610
|
|
|
* @access private |
|
1611
|
|
|
* @param $html |
|
1612
|
|
|
*/ |
|
1613
|
|
|
public function __loadSuccess($html) |
|
1614
|
|
|
{ |
|
1615
|
|
|
if ($this->_loadSelector) { |
|
1616
|
|
|
$html = PhpQuery::newDocument($html)->find($this->_loadSelector); |
|
1617
|
|
|
unset($this->_loadSelector); |
|
1618
|
|
|
} |
|
1619
|
|
|
foreach ($this->stack(1) as $node) { |
|
1620
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->markup($html); |
|
1621
|
|
|
} |
|
1622
|
|
|
} |
|
1623
|
|
|
|
|
1624
|
|
|
/** |
|
1625
|
|
|
* Allows users to enter strings of CSS selectors. Useful |
|
1626
|
|
|
* when the CSS is loaded via style or @imports that PhpQuery can't load |
|
1627
|
|
|
* because it doesn't know the URL context of the request. |
|
1628
|
|
|
*/ |
|
1629
|
|
|
public function addCSS($string) |
|
1630
|
|
|
{ |
|
1631
|
|
|
if (!isset($this->cssString[$this->getDocumentID()])) { |
|
1632
|
|
|
$this->cssString[$this->getDocumentID()] = ''; |
|
1633
|
|
|
} |
|
1634
|
|
|
$this->cssString[$this->getDocumentID()] .= $string; |
|
1635
|
|
|
$this->parseCSS(); |
|
1636
|
|
|
} |
|
1637
|
|
|
|
|
1638
|
|
|
/** |
|
1639
|
|
|
* Either sets the CSS property of an object or retrieves the |
|
1640
|
|
|
* CSS property of a proejct. |
|
1641
|
|
|
* |
|
1642
|
|
|
* @param $property_name |
|
1643
|
|
|
* @param bool $value |
|
1644
|
|
|
* @return string of css property value |
|
1645
|
|
|
* @todo |
|
1646
|
|
|
*/ |
|
1647
|
|
|
public function css($property_name, $value = false) |
|
1648
|
|
|
{ |
|
1649
|
|
|
if (!isset($this->cssIsParsed[$this->getDocumentID()]) |
|
1650
|
|
|
|| $this->cssIsParsed[$this->getDocumentID()] === false |
|
1651
|
|
|
) { |
|
1652
|
|
|
$this->parseCSS(); |
|
1653
|
|
|
} |
|
1654
|
|
|
$data = PhpQuery::data($this->get(0), 'phpquery_css', null, $this->getDocumentID()); |
|
1655
|
|
|
if (!$value) { |
|
1656
|
|
|
if (isset($data[$property_name])) { |
|
1657
|
|
|
return $data[$property_name]['value']; |
|
1658
|
|
|
} |
|
1659
|
|
|
return null; |
|
1660
|
|
|
} |
|
1661
|
|
|
$specificity = (isset($data[$property_name])) ? $data[$property_name]['specificity'] |
|
1662
|
|
|
+ 1 : 1000; |
|
1663
|
|
|
$data[$property_name] = array( |
|
1664
|
|
|
'specificity' => $specificity, |
|
1665
|
|
|
'value' => $value |
|
1666
|
|
|
); |
|
1667
|
|
|
PhpQuery::data($this->get(0), 'phpquery_css', $data, $this->getDocumentID()); |
|
1668
|
|
|
$this->bubbleCSS(PhpQuery::pq($this->get(0), $this->getDocumentID())); |
|
1669
|
|
|
|
|
1670
|
|
|
if ($specificity >= 1000) { |
|
1671
|
|
|
$styles = array(); |
|
1672
|
|
|
foreach ($this->data('phpquery_css') as $k => $v) { |
|
1673
|
|
|
if ($v['specificity'] >= 1000) { |
|
1674
|
|
|
$styles[$k] = trim($k) . ':' . trim($v['value']); |
|
1675
|
|
|
} |
|
1676
|
|
|
} |
|
1677
|
|
|
ksort($styles); |
|
1678
|
|
|
if (empty($styles)) { |
|
1679
|
|
|
$this->removeAttr('style'); |
|
1680
|
|
|
} elseif (PhpQuery::$enableCssShorthand) { |
|
1681
|
|
|
$parser = new \Sabberworm\CSS\Parser('{' . join(';', $styles) . '}'); |
|
1682
|
|
|
$doc = $parser->parse(); |
|
1683
|
|
|
$doc->createShorthands(); |
|
1684
|
|
|
$style = trim($doc->__toString(), "\n\r\t {}"); |
|
1685
|
|
|
} else { |
|
1686
|
|
|
$style = join(';', $styles); |
|
1687
|
|
|
} |
|
1688
|
|
|
$this->attr('style', $style); |
|
|
|
|
|
|
1689
|
|
|
} |
|
1690
|
|
|
} |
|
1691
|
|
|
|
|
1692
|
|
|
public function parseCSS() |
|
1693
|
|
|
{ |
|
1694
|
|
|
if (!isset($this->cssString[$this->getDocumentID()])) { |
|
1695
|
|
|
$this->cssString[$this->getDocumentID()] = file_get_contents( |
|
1696
|
|
|
dirname(__FILE__) |
|
1697
|
|
|
. '/Resources/default.css' |
|
1698
|
|
|
); |
|
1699
|
|
|
} |
|
1700
|
|
|
foreach (PhpQuery::pq('style', $this->getDocumentID()) as $style) { |
|
1701
|
|
|
$this->cssString[$this->getDocumentID()] .= PhpQuery::pq($style)->text(); |
|
1702
|
|
|
} |
|
1703
|
|
|
|
|
1704
|
|
|
$CssParser = new CssParser($this->cssString[$this->getDocumentID()]); |
|
1705
|
|
|
$CssDocument = $CssParser->parse(); |
|
1706
|
|
|
foreach ($CssDocument->getAllRuleSets() as $ruleset) { |
|
1707
|
|
|
foreach ($ruleset->getSelector() as $selector) { |
|
1708
|
|
|
$specificity = $selector->getSpecificity(); |
|
1709
|
|
|
foreach (PhpQuery::pq($selector->getSelector(), $this->getDocumentID()) as $el) { |
|
1710
|
|
|
$existing = pq($el)->data('phpquery_css'); |
|
1711
|
|
|
if (PhpQuery::$enableCssShorthand) { |
|
1712
|
|
|
$ruleset->expandShorthands(); |
|
1713
|
|
|
} |
|
1714
|
|
|
foreach ($ruleset->getRules() as $value) { |
|
1715
|
|
|
$rule = $value->getRule(); |
|
1716
|
|
|
if (!isset($existing[$rule]) |
|
1717
|
|
|
|| $existing[$rule]['specificity'] <= $specificity |
|
1718
|
|
|
) { |
|
1719
|
|
|
$value = $value->getValue(); |
|
1720
|
|
|
$value = (is_object($value)) ? $value->__toString() : $value; |
|
1721
|
|
|
$existing[$rule] = array( |
|
1722
|
|
|
'specificity' => $specificity, |
|
1723
|
|
|
'value' => $value |
|
1724
|
|
|
); |
|
1725
|
|
|
} |
|
1726
|
|
|
} |
|
1727
|
|
|
PhpQuery::pq($el)->data('phpquery_css', $existing); |
|
1728
|
|
|
$this->bubbleCSS(PhpQuery::pq($el)); |
|
1729
|
|
|
} |
|
1730
|
|
|
} |
|
1731
|
|
|
} |
|
1732
|
|
|
foreach (PhpQuery::pq('*', $this->getDocumentID()) as $el) { |
|
1733
|
|
|
$existing = pq($el)->data('phpquery_css'); |
|
1734
|
|
|
$style = pq($el)->attr('style'); |
|
1735
|
|
|
$style = strlen($style) ? explode(';', $style) : array(); |
|
1736
|
|
|
foreach ($this->attribute_css_mapping as $map => $css_equivalent) { |
|
1737
|
|
|
if ($el->hasAttribute($map)) { |
|
1738
|
|
|
$style[] = $css_equivalent . ':' . pq($el)->attr($map); |
|
1739
|
|
|
} |
|
1740
|
|
|
} |
|
1741
|
|
|
if (count($style)) { |
|
1742
|
|
|
$CssParser = new CssParser('#ruleset {' . implode(';', $style) . '}'); |
|
1743
|
|
|
$CssDocument = $CssParser->parse(); |
|
1744
|
|
|
$ruleset = $CssDocument->getAllRulesets(); |
|
1745
|
|
|
$ruleset = reset($ruleset); |
|
1746
|
|
|
if (PhpQuery::$enableCssShorthand) { |
|
1747
|
|
|
$ruleset->expandShorthands(); |
|
1748
|
|
|
} |
|
1749
|
|
|
foreach ($ruleset->getRules() as $value) { |
|
1750
|
|
|
$rule = $value->getRule(); |
|
1751
|
|
|
if (!isset($existing[$rule]) |
|
1752
|
|
|
|| 1000 >= $existing[$rule]['specificity'] |
|
1753
|
|
|
) { |
|
1754
|
|
|
$value = $value->getValue(); |
|
1755
|
|
|
$value = (is_object($value)) ? $value->__toString() : $value; |
|
1756
|
|
|
$existing[$rule] = array( |
|
1757
|
|
|
'specificity' => 1000, |
|
1758
|
|
|
'value' => $value |
|
1759
|
|
|
); |
|
1760
|
|
|
} |
|
1761
|
|
|
} |
|
1762
|
|
|
PhpQuery::pq($el)->data('phpquery_css', $existing); |
|
1763
|
|
|
$this->bubbleCSS(PhpQuery::pq($el)); |
|
1764
|
|
|
} |
|
1765
|
|
|
} |
|
1766
|
|
|
} |
|
1767
|
|
|
|
|
1768
|
|
|
protected function bubbleCSS($element) |
|
1769
|
|
|
{ |
|
1770
|
|
|
$style = $element->data('phpquery_css'); |
|
1771
|
|
|
foreach ($element->children() as $element_child) { |
|
1772
|
|
|
$existing = PhpQuery::pq($element_child)->data('phpquery_css'); |
|
1773
|
|
|
foreach ($style as $rule => $value) { |
|
1774
|
|
|
if (!isset($existing[$rule]) |
|
1775
|
|
|
|| $value['specificity'] > $existing[$rule]['specificity'] |
|
1776
|
|
|
) { |
|
1777
|
|
|
$existing[$rule] = $value; |
|
1778
|
|
|
} |
|
1779
|
|
|
} |
|
1780
|
|
|
PhpQuery::pq($element_child)->data('phpquery_css', $existing); |
|
1781
|
|
|
if (PhpQuery::pq($element_child)->children()->length) { |
|
1782
|
|
|
$this->bubbleCSS(PhpQuery::pq($element_child)); |
|
1783
|
|
|
} |
|
1784
|
|
|
} |
|
1785
|
|
|
} |
|
1786
|
|
|
|
|
1787
|
|
|
/** |
|
1788
|
|
|
* @todo |
|
1789
|
|
|
* |
|
1790
|
|
|
*/ |
|
1791
|
|
|
public function show() |
|
1792
|
|
|
{ |
|
1793
|
|
|
$display = ($this->data('phpquery_display_state')) ? $this->data('phpquery_display_state') |
|
1794
|
|
|
: 'block'; |
|
1795
|
|
|
$this->css('display', $display); |
|
|
|
|
|
|
1796
|
|
|
return $this; |
|
1797
|
|
|
} |
|
1798
|
|
|
|
|
1799
|
|
|
/** |
|
1800
|
|
|
* @todo |
|
1801
|
|
|
* |
|
1802
|
|
|
*/ |
|
1803
|
|
|
public function hide() |
|
1804
|
|
|
{ |
|
1805
|
|
|
$this->data('phpquery_display_state', $this->css('display')); |
|
1806
|
|
|
$this->css('display', 'none'); |
|
|
|
|
|
|
1807
|
|
|
return $this; |
|
1808
|
|
|
} |
|
1809
|
|
|
|
|
1810
|
|
|
/** |
|
1811
|
|
|
* Trigger a type of event on every matched element. |
|
1812
|
|
|
* |
|
1813
|
|
|
* @param $type |
|
1814
|
|
|
* @param array|\PhpQuery $data |
|
1815
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1816
|
|
|
* @TODO support more than event in $type (space-separated) |
|
1817
|
|
|
*/ |
|
1818
|
|
|
public function trigger($type, $data = array()) |
|
1819
|
|
|
{ |
|
1820
|
|
|
foreach ($this->elements as $node) |
|
1821
|
|
|
PhpQueryEvents::trigger($this->getDocumentID(), $type, $data, $node); |
|
|
|
|
|
|
1822
|
|
|
return $this; |
|
1823
|
|
|
} |
|
1824
|
|
|
|
|
1825
|
|
|
/** |
|
1826
|
|
|
* This particular method triggers all bound event handlers on an element (for a specific event type) WITHOUT executing the browsers default actions. |
|
1827
|
|
|
* |
|
1828
|
|
|
* @param $type |
|
1829
|
|
|
* @param $data |
|
1830
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1831
|
|
|
* @TODO |
|
1832
|
|
|
*/ |
|
1833
|
|
|
public function triggerHandler($type, $data = array()) |
|
|
|
|
|
|
1834
|
|
|
{ |
|
1835
|
|
|
// TODO; |
|
1836
|
|
|
} |
|
1837
|
|
|
|
|
1838
|
|
|
/** |
|
1839
|
|
|
* Binds a handler to one or more events (like click) for each matched element. |
|
1840
|
|
|
* Can also bind custom events. |
|
1841
|
|
|
* |
|
1842
|
|
|
* @param $type |
|
1843
|
|
|
* @param mixed $data Optional |
|
1844
|
|
|
* @param $callback |
|
1845
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1846
|
|
|
* @TODO support '!' (exclusive) events |
|
1847
|
|
|
* @TODO support more than event in $type (space-separated) |
|
1848
|
|
|
*/ |
|
1849
|
|
|
public function bind($type, $data, $callback = null) |
|
1850
|
|
|
{ |
|
1851
|
|
|
// TODO check if $data is callable, not using is_callable |
|
1852
|
|
|
if (!isset($callback)) { |
|
1853
|
|
|
$callback = $data; |
|
1854
|
|
|
$data = null; |
|
1855
|
|
|
} |
|
1856
|
|
|
foreach ($this->elements as $node) |
|
1857
|
|
|
PhpQueryEvents::add($this->getDocumentID(), $node, $type, $data, $callback); |
|
1858
|
|
|
return $this; |
|
1859
|
|
|
} |
|
1860
|
|
|
|
|
1861
|
|
|
/** |
|
1862
|
|
|
* Enter description here... |
|
1863
|
|
|
* |
|
1864
|
|
|
* @param string $type |
|
1865
|
|
|
* @param $callback |
|
1866
|
|
|
* @return unknown |
|
1867
|
|
|
* @TODO namespace events |
|
1868
|
|
|
* @TODO support more than event in $type (space-separated) |
|
1869
|
|
|
*/ |
|
1870
|
|
|
public function unbind($type = null, $callback = null) |
|
1871
|
|
|
{ |
|
1872
|
|
|
foreach ($this->elements as $node) |
|
1873
|
|
|
PhpQueryEvents::remove($this->getDocumentID(), $node, $type, $callback); |
|
1874
|
|
|
return $this; |
|
1875
|
|
|
} |
|
1876
|
|
|
|
|
1877
|
|
|
/** |
|
1878
|
|
|
* Enter description here... |
|
1879
|
|
|
* |
|
1880
|
|
|
* @param null $callback |
|
1881
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1882
|
|
|
*/ |
|
1883
|
|
|
public function change($callback = null) |
|
1884
|
|
|
{ |
|
1885
|
|
|
if ($callback) |
|
1886
|
|
|
return $this->bind('change', $callback); |
|
1887
|
|
|
return $this->trigger('change'); |
|
1888
|
|
|
} |
|
1889
|
|
|
|
|
1890
|
|
|
/** |
|
1891
|
|
|
* Enter description here... |
|
1892
|
|
|
* |
|
1893
|
|
|
* @param null $callback |
|
1894
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1895
|
|
|
*/ |
|
1896
|
|
|
public function submit($callback = null) |
|
1897
|
|
|
{ |
|
1898
|
|
|
if ($callback) |
|
1899
|
|
|
return $this->bind('submit', $callback); |
|
1900
|
|
|
return $this->trigger('submit'); |
|
1901
|
|
|
} |
|
1902
|
|
|
|
|
1903
|
|
|
/** |
|
1904
|
|
|
* Enter description here... |
|
1905
|
|
|
* |
|
1906
|
|
|
* @param null $callback |
|
1907
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1908
|
|
|
*/ |
|
1909
|
|
|
public function click($callback = null) |
|
1910
|
|
|
{ |
|
1911
|
|
|
if ($callback) |
|
1912
|
|
|
return $this->bind('click', $callback); |
|
1913
|
|
|
return $this->trigger('click'); |
|
1914
|
|
|
} |
|
1915
|
|
|
|
|
1916
|
|
|
/** |
|
1917
|
|
|
* Enter description here... |
|
1918
|
|
|
* |
|
1919
|
|
|
* @param String|PhpQuery |
|
1920
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1921
|
|
|
*/ |
|
1922
|
|
|
public function wrapAllOld($wrapper) |
|
1923
|
|
|
{ |
|
1924
|
|
|
$wrapper = pq($wrapper)->_clone(); |
|
1925
|
|
|
if (!$wrapper->length() || !$this->length()) |
|
|
|
|
|
|
1926
|
|
|
return $this; |
|
1927
|
|
|
$wrapper->insertBefore($this->elements[0]); |
|
1928
|
|
|
$deepest = $wrapper->elements[0]; |
|
1929
|
|
|
while ($deepest->firstChild && $deepest->firstChild instanceof \DOMElement) |
|
1930
|
|
|
$deepest = $deepest->firstChild; |
|
1931
|
|
|
pq($deepest)->append($this); |
|
1932
|
|
|
return $this; |
|
1933
|
|
|
} |
|
1934
|
|
|
|
|
1935
|
|
|
/** |
|
1936
|
|
|
* Enter description here... |
|
1937
|
|
|
* |
|
1938
|
|
|
* TODO testme... |
|
1939
|
|
|
* @param String|PhpQuery |
|
1940
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1941
|
|
|
*/ |
|
1942
|
|
|
public function wrapAll($wrapper) |
|
1943
|
|
|
{ |
|
1944
|
|
|
if (!$this->length()) |
|
|
|
|
|
|
1945
|
|
|
return $this; |
|
1946
|
|
|
return PhpQuery::pq($wrapper, $this->getDocumentID())->clone()->insertBefore($this->get(0))->map( |
|
1947
|
|
|
array( |
|
1948
|
|
|
$this, |
|
1949
|
|
|
'___wrapAllCallback' |
|
1950
|
|
|
) |
|
1951
|
|
|
)->append($this); |
|
1952
|
|
|
} |
|
1953
|
|
|
|
|
1954
|
|
|
/** |
|
1955
|
|
|
* |
|
1956
|
|
|
* @param $node |
|
1957
|
|
|
* @return unknown_type |
|
1958
|
|
|
* @access private |
|
1959
|
|
|
*/ |
|
1960
|
|
|
public function ___wrapAllCallback($node) |
|
1961
|
|
|
{ |
|
1962
|
|
|
$deepest = $node; |
|
1963
|
|
|
while ($deepest->firstChild && $deepest->firstChild instanceof \DOMElement) |
|
1964
|
|
|
$deepest = $deepest->firstChild; |
|
1965
|
|
|
return $deepest; |
|
1966
|
|
|
} |
|
1967
|
|
|
|
|
1968
|
|
|
/** |
|
1969
|
|
|
* Enter description here... |
|
1970
|
|
|
* NON JQUERY METHOD |
|
1971
|
|
|
* |
|
1972
|
|
|
* @param $codeBefore |
|
1973
|
|
|
* @param $codeAfter |
|
1974
|
|
|
* @internal param $ String|PhpQuery |
|
1975
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1976
|
|
|
*/ |
|
1977
|
|
|
public function wrapAllPHP($codeBefore, $codeAfter) |
|
1978
|
|
|
{ |
|
1979
|
|
|
return $this->slice(0, 1)->beforePHP($codeBefore)->end()->slice(-1)->afterPHP($codeAfter)->end(); |
|
|
|
|
|
|
1980
|
|
|
} |
|
1981
|
|
|
|
|
1982
|
|
|
/** |
|
1983
|
|
|
* Enter description here... |
|
1984
|
|
|
* |
|
1985
|
|
|
* @param String|PhpQuery |
|
1986
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
1987
|
|
|
*/ |
|
1988
|
|
|
public function wrap($wrapper) |
|
1989
|
|
|
{ |
|
1990
|
|
|
foreach ($this->stack() as $node) |
|
1991
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->wrapAll($wrapper); |
|
1992
|
|
|
return $this; |
|
1993
|
|
|
} |
|
1994
|
|
|
|
|
1995
|
|
|
/** |
|
1996
|
|
|
* Enter description here... |
|
1997
|
|
|
* |
|
1998
|
|
|
* @param $codeBefore |
|
1999
|
|
|
* @param $codeAfter |
|
2000
|
|
|
* @internal param $ String|PhpQuery |
|
2001
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2002
|
|
|
*/ |
|
2003
|
|
|
public function wrapPHP($codeBefore, $codeAfter) |
|
2004
|
|
|
{ |
|
2005
|
|
|
foreach ($this->stack() as $node) |
|
2006
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->wrapAllPHP($codeBefore, $codeAfter); |
|
2007
|
|
|
return $this; |
|
2008
|
|
|
} |
|
2009
|
|
|
|
|
2010
|
|
|
/** |
|
2011
|
|
|
* Enter description here... |
|
2012
|
|
|
* |
|
2013
|
|
|
* @param String|PhpQuery |
|
2014
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2015
|
|
|
*/ |
|
2016
|
|
|
public function wrapInner($wrapper) |
|
2017
|
|
|
{ |
|
2018
|
|
|
foreach ($this->stack() as $node) |
|
2019
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->contents()->wrapAll($wrapper); |
|
2020
|
|
|
return $this; |
|
2021
|
|
|
} |
|
2022
|
|
|
|
|
2023
|
|
|
/** |
|
2024
|
|
|
* Enter description here... |
|
2025
|
|
|
* |
|
2026
|
|
|
* @param $codeBefore |
|
2027
|
|
|
* @param $codeAfter |
|
2028
|
|
|
* @internal param $ String|PhpQuery |
|
2029
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2030
|
|
|
*/ |
|
2031
|
|
|
public function wrapInnerPHP($codeBefore, $codeAfter) |
|
2032
|
|
|
{ |
|
2033
|
|
|
foreach ($this->stack(1) as $node) |
|
2034
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->contents()->wrapAllPHP($codeBefore, $codeAfter); |
|
2035
|
|
|
return $this; |
|
2036
|
|
|
} |
|
2037
|
|
|
|
|
2038
|
|
|
/** |
|
2039
|
|
|
* Enter description here... |
|
2040
|
|
|
* |
|
2041
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2042
|
|
|
* @testme Support for text nodes |
|
2043
|
|
|
*/ |
|
2044
|
|
|
public function contents() |
|
2045
|
|
|
{ |
|
2046
|
|
|
$stack = array(); |
|
2047
|
|
|
foreach ($this->stack(1) as $el) { |
|
2048
|
|
|
// FIXME (fixed) http://code.google.com/p/phpquery/issues/detail?id=56 |
|
2049
|
|
|
// if (! isset($el->childNodes)) |
|
|
|
|
|
|
2050
|
|
|
// continue; |
|
2051
|
|
|
foreach ($el->childNodes as $node) { |
|
2052
|
|
|
$stack[] = $node; |
|
2053
|
|
|
} |
|
2054
|
|
|
} |
|
2055
|
|
|
return $this->newInstance($stack); |
|
2056
|
|
|
} |
|
2057
|
|
|
|
|
2058
|
|
|
/** |
|
2059
|
|
|
* Enter description here... |
|
2060
|
|
|
* |
|
2061
|
|
|
* jQuery difference. |
|
2062
|
|
|
* |
|
2063
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2064
|
|
|
*/ |
|
2065
|
|
|
public function contentsUnwrap() |
|
2066
|
|
|
{ |
|
2067
|
|
|
foreach ($this->stack(1) as $node) { |
|
2068
|
|
|
if (!$node->parentNode) |
|
2069
|
|
|
continue; |
|
2070
|
|
|
$childNodes = array(); |
|
2071
|
|
|
// any modification in DOM tree breaks childNodes iteration, so cache them first |
|
2072
|
|
|
foreach ($node->childNodes as $chNode) |
|
2073
|
|
|
$childNodes[] = $chNode; |
|
2074
|
|
|
foreach ($childNodes as $chNode) |
|
2075
|
|
|
// $node->parentNode->appendChild($chNode); |
|
|
|
|
|
|
2076
|
|
|
$node->parentNode->insertBefore($chNode, $node); |
|
2077
|
|
|
$node->parentNode->removeChild($node); |
|
2078
|
|
|
} |
|
2079
|
|
|
return $this; |
|
2080
|
|
|
} |
|
2081
|
|
|
|
|
2082
|
|
|
/** |
|
2083
|
|
|
* Enter description here... |
|
2084
|
|
|
* |
|
2085
|
|
|
* jQuery difference. |
|
2086
|
|
|
*/ |
|
2087
|
|
|
public function switchWith($markup) |
|
2088
|
|
|
{ |
|
2089
|
|
|
$markup = pq($markup, $this->getDocumentID()); |
|
2090
|
|
|
$content = null; |
|
2091
|
|
|
foreach ($this->stack(1) as $node) { |
|
2092
|
|
|
pq($node)->contents()->toReference($content)->end()->replaceWith($markup->clone()->append($content)); |
|
2093
|
|
|
} |
|
2094
|
|
|
return $this; |
|
2095
|
|
|
} |
|
2096
|
|
|
|
|
2097
|
|
|
/** |
|
2098
|
|
|
* Enter description here... |
|
2099
|
|
|
* |
|
2100
|
|
|
* @param $num |
|
2101
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2102
|
|
|
*/ |
|
2103
|
|
|
public function eq($num) |
|
2104
|
|
|
{ |
|
2105
|
|
|
$oldStack = $this->elements; |
|
2106
|
|
|
$this->elementsBackup = $this->elements; |
|
2107
|
|
|
$this->elements = array(); |
|
2108
|
|
|
if (isset($oldStack[$num])) |
|
2109
|
|
|
$this->elements[] = $oldStack[$num]; |
|
2110
|
|
|
return $this->newInstance(); |
|
2111
|
|
|
} |
|
2112
|
|
|
|
|
2113
|
|
|
/** |
|
2114
|
|
|
* Enter description here... |
|
2115
|
|
|
* |
|
2116
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2117
|
|
|
*/ |
|
2118
|
|
|
public function size() |
|
2119
|
|
|
{ |
|
2120
|
|
|
return count($this->elements); |
|
2121
|
|
|
} |
|
2122
|
|
|
|
|
2123
|
|
|
/** |
|
2124
|
|
|
* Enter description here... |
|
2125
|
|
|
* |
|
2126
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2127
|
|
|
* @deprecated Use length as attribute |
|
2128
|
|
|
*/ |
|
2129
|
|
|
public function length() |
|
2130
|
|
|
{ |
|
2131
|
|
|
return $this->size(); |
|
2132
|
|
|
} |
|
2133
|
|
|
|
|
2134
|
|
|
public function count() |
|
2135
|
|
|
{ |
|
2136
|
|
|
return $this->size(); |
|
2137
|
|
|
} |
|
2138
|
|
|
|
|
2139
|
|
|
/** |
|
2140
|
|
|
* Enter description here... |
|
2141
|
|
|
* |
|
2142
|
|
|
* @param int $level |
|
2143
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2144
|
|
|
* @todo $level |
|
2145
|
|
|
*/ |
|
2146
|
|
|
public function end($level = 1) |
|
|
|
|
|
|
2147
|
|
|
{ |
|
2148
|
|
|
// $this->elements = array_pop( $this->history ); |
|
|
|
|
|
|
2149
|
|
|
// return $this; |
|
2150
|
|
|
// $this->previous->DOM = $this->DOM; |
|
|
|
|
|
|
2151
|
|
|
// $this->previous->XPath = $this->XPath; |
|
|
|
|
|
|
2152
|
|
|
return $this->previous ? $this->previous : $this; |
|
2153
|
|
|
} |
|
2154
|
|
|
|
|
2155
|
|
|
/** |
|
2156
|
|
|
* Enter description here... |
|
2157
|
|
|
* Normal use ->clone() . |
|
2158
|
|
|
* |
|
2159
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2160
|
|
|
* @access private |
|
2161
|
|
|
*/ |
|
2162
|
|
|
public function _clone() |
|
2163
|
|
|
{ |
|
2164
|
|
|
$newStack = array(); |
|
2165
|
|
|
//pr(array('copy... ', $this->whois())); |
|
|
|
|
|
|
2166
|
|
|
//$this->dumpHistory('copy'); |
|
|
|
|
|
|
2167
|
|
|
$this->elementsBackup = $this->elements; |
|
2168
|
|
|
foreach ($this->elements as $node) { |
|
2169
|
|
|
$newStack[] = $node->cloneNode(true); |
|
2170
|
|
|
} |
|
2171
|
|
|
$this->elements = $newStack; |
|
2172
|
|
|
return $this->newInstance(); |
|
2173
|
|
|
} |
|
2174
|
|
|
|
|
2175
|
|
|
/** |
|
2176
|
|
|
* Enter description here... |
|
2177
|
|
|
* |
|
2178
|
|
|
* @param $code |
|
2179
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2180
|
|
|
*/ |
|
2181
|
|
|
public function replaceWithPHP($code) |
|
2182
|
|
|
{ |
|
2183
|
|
|
return $this->replaceWith(PhpQuery::php($code)); |
|
2184
|
|
|
} |
|
2185
|
|
|
|
|
2186
|
|
|
/** |
|
2187
|
|
|
* Enter description here... |
|
2188
|
|
|
* |
|
2189
|
|
|
* @param String|PhpQuery $content |
|
2190
|
|
|
* @link http://docs.jquery.com/Manipulation/replaceWith#content |
|
2191
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2192
|
|
|
*/ |
|
2193
|
|
|
public function replaceWith($content) |
|
2194
|
|
|
{ |
|
2195
|
|
|
return $this->after($content)->remove(); |
|
2196
|
|
|
} |
|
2197
|
|
|
|
|
2198
|
|
|
/** |
|
2199
|
|
|
* Enter description here... |
|
2200
|
|
|
* |
|
2201
|
|
|
* @param String $selector |
|
2202
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2203
|
|
|
* @todo this works ? |
|
2204
|
|
|
*/ |
|
2205
|
|
|
public function replaceAll($selector) |
|
2206
|
|
|
{ |
|
2207
|
|
|
foreach (PhpQuery::pq($selector, $this->getDocumentID()) as $node) |
|
2208
|
|
|
PhpQuery::pq($node, $this->getDocumentID())->after($this->_clone())->remove(); |
|
2209
|
|
|
return $this; |
|
2210
|
|
|
} |
|
2211
|
|
|
|
|
2212
|
|
|
/** |
|
2213
|
|
|
* Enter description here... |
|
2214
|
|
|
* |
|
2215
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2216
|
|
|
*/ |
|
2217
|
|
|
public function remove($selector = null) |
|
2218
|
|
|
{ |
|
2219
|
|
|
$loop = $selector ? $this->filter($selector)->elements : $this->elements; |
|
2220
|
|
|
foreach ($loop as $node) { |
|
2221
|
|
|
if (!$node->parentNode) |
|
2222
|
|
|
continue; |
|
2223
|
|
|
if (isset($node->tagName)) |
|
2224
|
|
|
$this->debug("Removing '{$node->tagName}'"); |
|
2225
|
|
|
$node->parentNode->removeChild($node); |
|
2226
|
|
|
// Mutation event |
|
2227
|
|
|
$event = new Dom\DOMEvent(array( |
|
2228
|
|
|
'target' => $node, |
|
2229
|
|
|
'type' => 'DOMNodeRemoved' |
|
2230
|
|
|
)); |
|
2231
|
|
|
PhpQueryEvents::trigger( |
|
2232
|
|
|
$this->getDocumentID(), |
|
2233
|
|
|
$event->type, |
|
2234
|
|
|
array( |
|
2235
|
|
|
$event |
|
2236
|
|
|
), |
|
2237
|
|
|
$node |
|
2238
|
|
|
); |
|
2239
|
|
|
} |
|
2240
|
|
|
return $this; |
|
2241
|
|
|
} |
|
2242
|
|
|
|
|
2243
|
|
|
protected function markupEvents($newMarkup, $oldMarkup, $node) |
|
2244
|
|
|
{ |
|
2245
|
|
|
if ($node->tagName == 'textarea' && $newMarkup != $oldMarkup) { |
|
2246
|
|
|
$event = new Dom\DOMEvent(array( |
|
2247
|
|
|
'target' => $node, |
|
2248
|
|
|
'type' => 'change' |
|
2249
|
|
|
)); |
|
2250
|
|
|
PhpQueryEvents::trigger( |
|
2251
|
|
|
$this->getDocumentID(), |
|
2252
|
|
|
$event->type, |
|
2253
|
|
|
array( |
|
2254
|
|
|
$event |
|
2255
|
|
|
), |
|
2256
|
|
|
$node |
|
2257
|
|
|
); |
|
2258
|
|
|
} |
|
2259
|
|
|
} |
|
2260
|
|
|
|
|
2261
|
|
|
/** |
|
2262
|
|
|
* jQuey difference |
|
2263
|
|
|
* |
|
2264
|
|
|
* @param $markup |
|
2265
|
|
|
* @param null $callback1 |
|
2266
|
|
|
* @param null $callback2 |
|
2267
|
|
|
* @param null $callback3 |
|
2268
|
|
|
* @return unknown_type |
|
2269
|
|
|
* @TODO trigger change event for textarea |
|
2270
|
|
|
*/ |
|
2271
|
|
View Code Duplication |
public function markup($markup = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2272
|
|
|
{ |
|
2273
|
|
|
$args = func_get_args(); |
|
2274
|
|
|
if ($this->documentWrapper->isXML) |
|
2275
|
|
|
return call_user_func_array( |
|
2276
|
|
|
array( |
|
2277
|
|
|
$this, |
|
2278
|
|
|
'xml' |
|
2279
|
|
|
), |
|
2280
|
|
|
$args |
|
2281
|
|
|
); |
|
2282
|
|
|
else |
|
2283
|
|
|
return call_user_func_array( |
|
2284
|
|
|
array( |
|
2285
|
|
|
$this, |
|
2286
|
|
|
'html' |
|
2287
|
|
|
), |
|
2288
|
|
|
$args |
|
2289
|
|
|
); |
|
2290
|
|
|
} |
|
2291
|
|
|
|
|
2292
|
|
|
/** |
|
2293
|
|
|
* jQuey difference |
|
2294
|
|
|
* |
|
2295
|
|
|
* @param null $callback1 |
|
2296
|
|
|
* @param null $callback2 |
|
2297
|
|
|
* @param null $callback3 |
|
2298
|
|
|
* @internal param $markup |
|
2299
|
|
|
* @return string |
|
2300
|
|
|
*/ |
|
2301
|
|
View Code Duplication |
public function markupOuter($callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2302
|
|
|
{ |
|
2303
|
|
|
$args = func_get_args(); |
|
2304
|
|
|
if ($this->documentWrapper->isXML) |
|
2305
|
|
|
return call_user_func_array( |
|
2306
|
|
|
array( |
|
2307
|
|
|
$this, |
|
2308
|
|
|
'xmlOuter' |
|
2309
|
|
|
), |
|
2310
|
|
|
$args |
|
2311
|
|
|
); |
|
2312
|
|
|
else |
|
2313
|
|
|
return call_user_func_array( |
|
2314
|
|
|
array( |
|
2315
|
|
|
$this, |
|
2316
|
|
|
'htmlOuter' |
|
2317
|
|
|
), |
|
2318
|
|
|
$args |
|
2319
|
|
|
); |
|
2320
|
|
|
} |
|
2321
|
|
|
|
|
2322
|
|
|
/** |
|
2323
|
|
|
* Enter description here... |
|
2324
|
|
|
* |
|
2325
|
|
|
* @param unknown_type $html |
|
2326
|
|
|
* @param null $callback1 |
|
2327
|
|
|
* @param null $callback2 |
|
2328
|
|
|
* @param null $callback3 |
|
2329
|
|
|
* @return string|PhpQuery|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2330
|
|
|
* @TODO force html result |
|
2331
|
|
|
*/ |
|
2332
|
|
|
public function html($html = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2333
|
|
|
{ |
|
2334
|
|
|
if (isset($html)) { |
|
2335
|
|
|
// INSERT |
|
2336
|
|
|
$nodes = $this->documentWrapper->import($html); |
|
2337
|
|
|
$this->empty(); |
|
2338
|
|
|
foreach ($this->stack(1) as $alreadyAdded => $node) { |
|
2339
|
|
|
// for now, limit events for textarea |
|
2340
|
|
|
if (($this->isXHTML() || $this->isHTML()) |
|
2341
|
|
|
&& $node->tagName == 'textarea' |
|
2342
|
|
|
) |
|
2343
|
|
|
$oldHtml = pq($node, $this->getDocumentID())->markup(); |
|
2344
|
|
|
foreach ($nodes as $newNode) { |
|
2345
|
|
|
$node->appendChild( |
|
2346
|
|
|
$alreadyAdded ? $newNode->cloneNode(true) |
|
2347
|
|
|
: $newNode |
|
2348
|
|
|
); |
|
2349
|
|
|
} |
|
2350
|
|
|
// for now, limit events for textarea |
|
2351
|
|
|
if (($this->isXHTML() || $this->isHTML()) |
|
2352
|
|
|
&& $node->tagName == 'textarea' |
|
2353
|
|
|
) |
|
2354
|
|
|
$this->markupEvents($html, $oldHtml, $node); |
|
|
|
|
|
|
2355
|
|
|
} |
|
2356
|
|
|
return $this; |
|
|
|
|
|
|
2357
|
|
|
} else { |
|
2358
|
|
|
// FETCH |
|
2359
|
|
|
$return = $this->documentWrapper->markup($this->elements, true); |
|
2360
|
|
|
$args = func_get_args(); |
|
2361
|
|
|
foreach (array_slice($args, 1) as $callback) { |
|
2362
|
|
|
$return = PhpQuery::callbackRun( |
|
2363
|
|
|
$callback, |
|
2364
|
|
|
array( |
|
2365
|
|
|
$return |
|
2366
|
|
|
) |
|
2367
|
|
|
); |
|
2368
|
|
|
} |
|
2369
|
|
|
return $return; |
|
2370
|
|
|
} |
|
2371
|
|
|
} |
|
2372
|
|
|
|
|
2373
|
|
|
/** |
|
2374
|
|
|
* @TODO force xml result |
|
2375
|
|
|
*/ |
|
2376
|
|
View Code Duplication |
public function xml($xml = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2377
|
|
|
{ |
|
2378
|
|
|
$args = func_get_args(); |
|
2379
|
|
|
return call_user_func_array( |
|
2380
|
|
|
array( |
|
2381
|
|
|
$this, |
|
2382
|
|
|
'html' |
|
2383
|
|
|
), |
|
2384
|
|
|
$args |
|
2385
|
|
|
); |
|
2386
|
|
|
} |
|
2387
|
|
|
|
|
2388
|
|
|
/** |
|
2389
|
|
|
* Enter description here... |
|
2390
|
|
|
* @TODO force html result |
|
2391
|
|
|
* |
|
2392
|
|
|
* @param null $callback1 |
|
2393
|
|
|
* @param null $callback2 |
|
2394
|
|
|
* @param null $callback3 |
|
2395
|
|
|
* @return String |
|
2396
|
|
|
*/ |
|
2397
|
|
|
public function htmlOuter($callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2398
|
|
|
{ |
|
2399
|
|
|
$markup = $this->documentWrapper->markup($this->elements); |
|
2400
|
|
|
// pass thou callbacks |
|
2401
|
|
|
$args = func_get_args(); |
|
2402
|
|
|
foreach ($args as $callback) { |
|
2403
|
|
|
$markup = PhpQuery::callbackRun( |
|
2404
|
|
|
$callback, |
|
2405
|
|
|
array( |
|
2406
|
|
|
$markup |
|
2407
|
|
|
) |
|
2408
|
|
|
); |
|
2409
|
|
|
} |
|
2410
|
|
|
return $markup; |
|
2411
|
|
|
} |
|
2412
|
|
|
|
|
2413
|
|
|
/** |
|
2414
|
|
|
* @TODO force xml result |
|
2415
|
|
|
*/ |
|
2416
|
|
View Code Duplication |
public function xmlOuter($callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2417
|
|
|
{ |
|
2418
|
|
|
$args = func_get_args(); |
|
2419
|
|
|
return call_user_func_array( |
|
2420
|
|
|
array( |
|
2421
|
|
|
$this, |
|
2422
|
|
|
'htmlOuter' |
|
2423
|
|
|
), |
|
2424
|
|
|
$args |
|
2425
|
|
|
); |
|
2426
|
|
|
} |
|
2427
|
|
|
|
|
2428
|
|
|
public function __toString() |
|
2429
|
|
|
{ |
|
2430
|
|
|
return $this->markupOuter(); |
|
2431
|
|
|
} |
|
2432
|
|
|
|
|
2433
|
|
|
/** |
|
2434
|
|
|
* Just like html(), but returns markup with VALID (dangerous) PHP tags. |
|
2435
|
|
|
* |
|
2436
|
|
|
* @param null $code |
|
2437
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2438
|
|
|
* @todo support returning markup with PHP tags when called without param |
|
2439
|
|
|
*/ |
|
2440
|
|
|
public function php($code = null) |
|
2441
|
|
|
{ |
|
2442
|
|
|
return $this->markupPHP($code); |
|
2443
|
|
|
} |
|
2444
|
|
|
|
|
2445
|
|
|
/** |
|
2446
|
|
|
* Enter description here... |
|
2447
|
|
|
* |
|
2448
|
|
|
* @param $code |
|
2449
|
|
|
* @return unknown_type |
|
2450
|
|
|
*/ |
|
2451
|
|
|
public function markupPHP($code = null) |
|
2452
|
|
|
{ |
|
2453
|
|
|
return isset($code) ? $this->markup(PhpQuery::php($code)) |
|
|
|
|
|
|
2454
|
|
|
: PhpQuery::markupToPHP($this->markup()); |
|
2455
|
|
|
} |
|
2456
|
|
|
|
|
2457
|
|
|
/** |
|
2458
|
|
|
* Enter description here... |
|
2459
|
|
|
* |
|
2460
|
|
|
* @internal param $code |
|
2461
|
|
|
* @return string PHP code |
|
2462
|
|
|
*/ |
|
2463
|
|
|
public function markupOuterPHP() |
|
2464
|
|
|
{ |
|
2465
|
|
|
return PhpQuery::markupToPHP($this->markupOuter()); |
|
2466
|
|
|
} |
|
2467
|
|
|
|
|
2468
|
|
|
/** |
|
2469
|
|
|
* Enter description here... |
|
2470
|
|
|
* |
|
2471
|
|
|
* @param null $selector |
|
2472
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2473
|
|
|
*/ |
|
2474
|
|
|
public function children($selector = null) |
|
2475
|
|
|
{ |
|
2476
|
|
|
$stack = array(); |
|
2477
|
|
|
foreach ($this->stack(1) as $node) { |
|
2478
|
|
|
// foreach($node->getElementsByTagName('*') as $newNode) { |
|
|
|
|
|
|
2479
|
|
|
foreach ($node->childNodes as $newNode) { |
|
2480
|
|
|
if ($newNode->nodeType != 1) |
|
2481
|
|
|
continue; |
|
2482
|
|
|
if ($selector && !$this->is($selector, $newNode)) |
|
2483
|
|
|
continue; |
|
2484
|
|
|
if ($this->elementsContainsNode($newNode, $stack)) |
|
2485
|
|
|
continue; |
|
2486
|
|
|
$stack[] = $newNode; |
|
2487
|
|
|
} |
|
2488
|
|
|
} |
|
2489
|
|
|
$this->elementsBackup = $this->elements; |
|
2490
|
|
|
$this->elements = $stack; |
|
2491
|
|
|
return $this->newInstance(); |
|
2492
|
|
|
} |
|
2493
|
|
|
|
|
2494
|
|
|
/** |
|
2495
|
|
|
* Enter description here... |
|
2496
|
|
|
* |
|
2497
|
|
|
* @param null $selector |
|
2498
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2499
|
|
|
*/ |
|
2500
|
|
|
public function ancestors($selector = null) |
|
2501
|
|
|
{ |
|
2502
|
|
|
return $this->children($selector); |
|
2503
|
|
|
} |
|
2504
|
|
|
|
|
2505
|
|
|
/** |
|
2506
|
|
|
* Enter description here... |
|
2507
|
|
|
* |
|
2508
|
|
|
* @param $content |
|
2509
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2510
|
|
|
*/ |
|
2511
|
|
|
public function append($content) |
|
2512
|
|
|
{ |
|
2513
|
|
|
return $this->insert($content, __FUNCTION__); |
|
|
|
|
|
|
2514
|
|
|
} |
|
2515
|
|
|
|
|
2516
|
|
|
/** |
|
2517
|
|
|
* Enter description here... |
|
2518
|
|
|
* |
|
2519
|
|
|
* @param $content |
|
2520
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2521
|
|
|
*/ |
|
2522
|
|
|
public function appendPHP($content) |
|
2523
|
|
|
{ |
|
2524
|
|
|
return $this->insert("<php><!-- {$content} --></php>", 'append'); |
|
|
|
|
|
|
2525
|
|
|
} |
|
2526
|
|
|
|
|
2527
|
|
|
/** |
|
2528
|
|
|
* Enter description here... |
|
2529
|
|
|
* |
|
2530
|
|
|
* @param $seletor |
|
2531
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2532
|
|
|
*/ |
|
2533
|
|
|
public function appendTo($seletor) |
|
2534
|
|
|
{ |
|
2535
|
|
|
return $this->insert($seletor, __FUNCTION__); |
|
|
|
|
|
|
2536
|
|
|
} |
|
2537
|
|
|
|
|
2538
|
|
|
/** |
|
2539
|
|
|
* Enter description here... |
|
2540
|
|
|
* |
|
2541
|
|
|
* @param $content |
|
2542
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2543
|
|
|
*/ |
|
2544
|
|
|
public function prepend($content) |
|
2545
|
|
|
{ |
|
2546
|
|
|
return $this->insert($content, __FUNCTION__); |
|
|
|
|
|
|
2547
|
|
|
} |
|
2548
|
|
|
|
|
2549
|
|
|
/** |
|
2550
|
|
|
* Enter description here... |
|
2551
|
|
|
* |
|
2552
|
|
|
* @todo accept many arguments, which are joined, arrays maybe also |
|
2553
|
|
|
* @param $content |
|
2554
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2555
|
|
|
*/ |
|
2556
|
|
|
public function prependPHP($content) |
|
2557
|
|
|
{ |
|
2558
|
|
|
return $this->insert("<php><!-- {$content} --></php>", 'prepend'); |
|
|
|
|
|
|
2559
|
|
|
} |
|
2560
|
|
|
|
|
2561
|
|
|
/** |
|
2562
|
|
|
* Enter description here... |
|
2563
|
|
|
* |
|
2564
|
|
|
* @param $seletor |
|
2565
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2566
|
|
|
*/ |
|
2567
|
|
|
public function prependTo($seletor) |
|
2568
|
|
|
{ |
|
2569
|
|
|
return $this->insert($seletor, __FUNCTION__); |
|
|
|
|
|
|
2570
|
|
|
} |
|
2571
|
|
|
|
|
2572
|
|
|
/** |
|
2573
|
|
|
* Enter description here... |
|
2574
|
|
|
* |
|
2575
|
|
|
* @param $content |
|
2576
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2577
|
|
|
*/ |
|
2578
|
|
|
public function before($content) |
|
2579
|
|
|
{ |
|
2580
|
|
|
return $this->insert($content, __FUNCTION__); |
|
|
|
|
|
|
2581
|
|
|
} |
|
2582
|
|
|
|
|
2583
|
|
|
/** |
|
2584
|
|
|
* Enter description here... |
|
2585
|
|
|
* |
|
2586
|
|
|
* @param $content |
|
2587
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2588
|
|
|
*/ |
|
2589
|
|
|
public function beforePHP($content) |
|
2590
|
|
|
{ |
|
2591
|
|
|
return $this->insert("<php><!-- {$content} --></php>", 'before'); |
|
|
|
|
|
|
2592
|
|
|
} |
|
2593
|
|
|
|
|
2594
|
|
|
/** |
|
2595
|
|
|
* Enter description here... |
|
2596
|
|
|
* |
|
2597
|
|
|
* @param String|PhpQuery |
|
2598
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2599
|
|
|
*/ |
|
2600
|
|
|
public function insertBefore($seletor) |
|
2601
|
|
|
{ |
|
2602
|
|
|
return $this->insert($seletor, __FUNCTION__); |
|
|
|
|
|
|
2603
|
|
|
} |
|
2604
|
|
|
|
|
2605
|
|
|
/** |
|
2606
|
|
|
* Enter description here... |
|
2607
|
|
|
* |
|
2608
|
|
|
* @param $content |
|
2609
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2610
|
|
|
*/ |
|
2611
|
|
|
public function after($content) |
|
2612
|
|
|
{ |
|
2613
|
|
|
return $this->insert($content, __FUNCTION__); |
|
|
|
|
|
|
2614
|
|
|
} |
|
2615
|
|
|
|
|
2616
|
|
|
/** |
|
2617
|
|
|
* Enter description here... |
|
2618
|
|
|
* |
|
2619
|
|
|
* @param $content |
|
2620
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2621
|
|
|
*/ |
|
2622
|
|
|
public function afterPHP($content) |
|
2623
|
|
|
{ |
|
2624
|
|
|
return $this->insert("<php><!-- {$content} --></php>", 'after'); |
|
|
|
|
|
|
2625
|
|
|
} |
|
2626
|
|
|
|
|
2627
|
|
|
/** |
|
2628
|
|
|
* Enter description here... |
|
2629
|
|
|
* |
|
2630
|
|
|
* @param $seletor |
|
2631
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2632
|
|
|
*/ |
|
2633
|
|
|
public function insertAfter($seletor) |
|
2634
|
|
|
{ |
|
2635
|
|
|
return $this->insert($seletor, __FUNCTION__); |
|
|
|
|
|
|
2636
|
|
|
} |
|
2637
|
|
|
|
|
2638
|
|
|
/** |
|
2639
|
|
|
* Internal insert method. Don't use it. |
|
2640
|
|
|
* |
|
2641
|
|
|
* @param unknown_type $target |
|
2642
|
|
|
* @param unknown_type $type |
|
2643
|
|
|
* @throws \Exception |
|
2644
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2645
|
|
|
* @access private |
|
2646
|
|
|
*/ |
|
2647
|
|
|
public function insert($target, $type) |
|
2648
|
|
|
{ |
|
2649
|
|
|
$this->debug("Inserting data with '{$type}'"); |
|
2650
|
|
|
$to = false; |
|
2651
|
|
|
switch ($type) { |
|
2652
|
|
|
case 'appendTo': |
|
2653
|
|
|
case 'prependTo': |
|
2654
|
|
|
case 'insertBefore': |
|
2655
|
|
|
case 'insertAfter': |
|
2656
|
|
|
$to = true; |
|
2657
|
|
|
} |
|
2658
|
|
|
switch (gettype($target)) { |
|
2659
|
|
|
case 'string': |
|
2660
|
|
|
$insertFrom = $insertTo = array(); |
|
|
|
|
|
|
2661
|
|
|
if ($to) { |
|
2662
|
|
|
// INSERT TO |
|
2663
|
|
|
$insertFrom = $this->elements; |
|
2664
|
|
|
if (PhpQuery::isMarkup($target)) { |
|
|
|
|
|
|
2665
|
|
|
// $target is new markup, import it |
|
2666
|
|
|
$insertTo = $this->documentWrapper->import($target); |
|
2667
|
|
|
// insert into selected element |
|
2668
|
|
|
} else { |
|
2669
|
|
|
// $tagret is a selector |
|
2670
|
|
|
$thisStack = $this->elements; |
|
2671
|
|
|
$this->toRoot(); |
|
2672
|
|
|
$insertTo = $this->find($target)->elements; |
|
2673
|
|
|
$this->elements = $thisStack; |
|
2674
|
|
|
} |
|
2675
|
|
|
} else { |
|
2676
|
|
|
// INSERT FROM |
|
2677
|
|
|
$insertTo = $this->elements; |
|
2678
|
|
|
$insertFrom = $this->documentWrapper->import($target); |
|
2679
|
|
|
} |
|
2680
|
|
|
break; |
|
2681
|
|
|
case 'object': |
|
2682
|
|
|
$insertFrom = $insertTo = array(); |
|
2683
|
|
|
// PhpQuery |
|
2684
|
|
|
if ($target instanceof self) { |
|
2685
|
|
|
if ($to) { |
|
2686
|
|
|
$insertTo = $target->elements; |
|
2687
|
|
View Code Duplication |
if ($this->documentFragment && $this->stackIsRoot()) |
|
|
|
|
|
|
2688
|
|
|
// get all body children |
|
2689
|
|
|
// $loop = $this->find('body > *')->elements; |
|
|
|
|
|
|
2690
|
|
|
// TODO test it, test it hard... |
|
2691
|
|
|
// $loop = $this->newInstance($this->root)->find('> *')->elements; |
|
|
|
|
|
|
2692
|
|
|
$loop = $this->root->childNodes; |
|
2693
|
|
|
else |
|
2694
|
|
|
$loop = $this->elements; |
|
2695
|
|
|
// import nodes if needed |
|
2696
|
|
|
$insertFrom = $this->getDocumentID() == $target->getDocumentID() ? $loop |
|
2697
|
|
|
: $target->documentWrapper->import($loop); |
|
2698
|
|
|
} else { |
|
2699
|
|
|
$insertTo = $this->elements; |
|
2700
|
|
|
if ($target->documentFragment && $target->stackIsRoot()) |
|
2701
|
|
|
// get all body children |
|
2702
|
|
|
// $loop = $target->find('body > *')->elements; |
|
|
|
|
|
|
2703
|
|
|
$loop = $target->root->childNodes; |
|
2704
|
|
|
else |
|
2705
|
|
|
$loop = $target->elements; |
|
2706
|
|
|
// import nodes if needed |
|
2707
|
|
|
$insertFrom = $this->getDocumentID() == $target->getDocumentID() ? $loop |
|
2708
|
|
|
: $this->documentWrapper->import($loop); |
|
2709
|
|
|
} |
|
2710
|
|
|
// DOMNODE |
|
2711
|
|
|
} elseif ($target instanceof \DOMNODE) { |
|
2712
|
|
|
// import node if needed |
|
2713
|
|
|
// if ( $target->ownerDocument != $this->DOM ) |
|
|
|
|
|
|
2714
|
|
|
// $target = $this->DOM->importNode($target, true); |
|
|
|
|
|
|
2715
|
|
|
if ($to) { |
|
2716
|
|
|
$insertTo = array( |
|
2717
|
|
|
$target |
|
2718
|
|
|
); |
|
2719
|
|
View Code Duplication |
if ($this->documentFragment && $this->stackIsRoot()) |
|
|
|
|
|
|
2720
|
|
|
// get all body children |
|
2721
|
|
|
$loop = $this->root->childNodes; |
|
2722
|
|
|
// $loop = $this->find('body > *')->elements; |
|
|
|
|
|
|
2723
|
|
|
else |
|
2724
|
|
|
$loop = $this->elements; |
|
2725
|
|
|
foreach ($loop as $fromNode) |
|
2726
|
|
|
// import nodes if needed |
|
2727
|
|
|
$insertFrom[] = !$fromNode->ownerDocument->isSameNode( |
|
2728
|
|
|
$target->ownerDocument |
|
2729
|
|
|
) ? $target->ownerDocument->importNode($fromNode, true) |
|
2730
|
|
|
: $fromNode; |
|
2731
|
|
|
} else { |
|
2732
|
|
|
// import node if needed |
|
2733
|
|
|
if (!$target->ownerDocument->isSameNode($this->document)) |
|
2734
|
|
|
$target = $this->document->importNode($target, true); |
|
2735
|
|
|
$insertTo = $this->elements; |
|
2736
|
|
|
$insertFrom[] = $target; |
|
2737
|
|
|
} |
|
2738
|
|
|
} |
|
2739
|
|
|
break; |
|
2740
|
|
|
} |
|
2741
|
|
|
PhpQuery::debug( |
|
2742
|
|
|
"From " . count($insertFrom) . "; To " . count($insertTo) |
|
|
|
|
|
|
2743
|
|
|
. " nodes" |
|
2744
|
|
|
); |
|
2745
|
|
|
foreach ($insertTo as $insertNumber => $toNode) { |
|
2746
|
|
|
// we need static relative elements in some cases |
|
2747
|
|
|
switch ($type) { |
|
2748
|
|
|
case 'prependTo': |
|
2749
|
|
|
case 'prepend': |
|
2750
|
|
|
$firstChild = $toNode->firstChild; |
|
2751
|
|
|
break; |
|
2752
|
|
|
case 'insertAfter': |
|
2753
|
|
|
case 'after': |
|
2754
|
|
|
$nextSibling = $toNode->nextSibling; |
|
2755
|
|
|
break; |
|
2756
|
|
|
} |
|
2757
|
|
|
foreach ($insertFrom as $fromNode) { |
|
2758
|
|
|
// clone if inserted already before |
|
2759
|
|
|
$insert = $insertNumber ? $fromNode->cloneNode(true) : $fromNode; |
|
2760
|
|
|
switch ($type) { |
|
2761
|
|
|
case 'appendTo': |
|
2762
|
|
|
case 'append': |
|
2763
|
|
|
// $toNode->insertBefore( |
|
2764
|
|
|
// $fromNode, |
|
2765
|
|
|
// $toNode->lastChild->nextSibling |
|
|
|
|
|
|
2766
|
|
|
// ); |
|
2767
|
|
|
$toNode->appendChild($insert); |
|
2768
|
|
|
$eventTarget = $insert; |
|
|
|
|
|
|
2769
|
|
|
break; |
|
2770
|
|
|
case 'prependTo': |
|
2771
|
|
|
case 'prepend': |
|
2772
|
|
|
$toNode->insertBefore($insert, $firstChild); |
|
|
|
|
|
|
2773
|
|
|
break; |
|
2774
|
|
|
case 'insertBefore': |
|
2775
|
|
|
case 'before': |
|
2776
|
|
|
if (!$toNode->parentNode) |
|
2777
|
|
|
throw new \Exception("No parentNode, can't do {$type}()"); |
|
2778
|
|
|
else |
|
2779
|
|
|
$toNode->parentNode->insertBefore($insert, $toNode); |
|
2780
|
|
|
break; |
|
2781
|
|
|
case 'insertAfter': |
|
2782
|
|
|
case 'after': |
|
2783
|
|
|
if (!$toNode->parentNode) |
|
2784
|
|
|
throw new \Exception("No parentNode, can't do {$type}()"); |
|
2785
|
|
|
else |
|
2786
|
|
|
$toNode->parentNode->insertBefore($insert, $nextSibling); |
|
|
|
|
|
|
2787
|
|
|
break; |
|
2788
|
|
|
} |
|
2789
|
|
|
// Mutation event |
|
2790
|
|
|
$event = new Dom\DOMEvent(array( |
|
2791
|
|
|
'target' => $insert, |
|
2792
|
|
|
'type' => 'DOMNodeInserted' |
|
2793
|
|
|
)); |
|
2794
|
|
|
PhpQueryEvents::trigger( |
|
2795
|
|
|
$this->getDocumentID(), |
|
2796
|
|
|
$event->type, |
|
2797
|
|
|
array( |
|
2798
|
|
|
$event |
|
2799
|
|
|
), |
|
2800
|
|
|
$insert |
|
2801
|
|
|
); |
|
2802
|
|
|
} |
|
2803
|
|
|
} |
|
2804
|
|
|
return $this; |
|
2805
|
|
|
} |
|
2806
|
|
|
|
|
2807
|
|
|
/** |
|
2808
|
|
|
* Enter description here... |
|
2809
|
|
|
* |
|
2810
|
|
|
* @param $subject |
|
2811
|
|
|
* @return Int |
|
2812
|
|
|
*/ |
|
2813
|
|
|
public function index($subject) |
|
2814
|
|
|
{ |
|
2815
|
|
|
$index = -1; |
|
2816
|
|
|
$subject = $subject instanceof PhpQueryObject ? $subject->elements[0] |
|
2817
|
|
|
: $subject; |
|
2818
|
|
|
foreach ($this->newInstance() as $k => $node) { |
|
2819
|
|
|
if ($node->isSameNode($subject)) |
|
2820
|
|
|
$index = $k; |
|
2821
|
|
|
} |
|
2822
|
|
|
return $index; |
|
2823
|
|
|
} |
|
2824
|
|
|
|
|
2825
|
|
|
/** |
|
2826
|
|
|
* Enter description here... |
|
2827
|
|
|
* |
|
2828
|
|
|
* @param unknown_type $start |
|
2829
|
|
|
* @param unknown_type $end |
|
2830
|
|
|
* |
|
2831
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2832
|
|
|
* @testme |
|
2833
|
|
|
*/ |
|
2834
|
|
|
public function slice($start, $end = null) |
|
2835
|
|
|
{ |
|
2836
|
|
|
// $last = count($this->elements)-1; |
|
|
|
|
|
|
2837
|
|
|
// $end = $end |
|
2838
|
|
|
// ? min($end, $last) |
|
|
|
|
|
|
2839
|
|
|
// : $last; |
|
2840
|
|
|
// if ($start < 0) |
|
|
|
|
|
|
2841
|
|
|
// $start = $last+$start; |
|
|
|
|
|
|
2842
|
|
|
// if ($start > $last) |
|
|
|
|
|
|
2843
|
|
|
// return array(); |
|
|
|
|
|
|
2844
|
|
|
if ($end > 0) |
|
2845
|
|
|
$end = $end - $start; |
|
2846
|
|
|
return $this->newInstance(array_slice($this->elements, $start, $end)); |
|
2847
|
|
|
} |
|
2848
|
|
|
|
|
2849
|
|
|
/** |
|
2850
|
|
|
* Enter description here... |
|
2851
|
|
|
* |
|
2852
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2853
|
|
|
*/ |
|
2854
|
|
|
public function reverse() |
|
2855
|
|
|
{ |
|
2856
|
|
|
$this->elementsBackup = $this->elements; |
|
2857
|
|
|
$this->elements = array_reverse($this->elements); |
|
2858
|
|
|
return $this->newInstance(); |
|
2859
|
|
|
} |
|
2860
|
|
|
|
|
2861
|
|
|
/** |
|
2862
|
|
|
* Return joined text content. |
|
2863
|
|
|
* @param null $text |
|
2864
|
|
|
* @param null $callback1 |
|
2865
|
|
|
* @param null $callback2 |
|
2866
|
|
|
* @param null $callback3 |
|
2867
|
|
|
* @return String |
|
2868
|
|
|
*/ |
|
2869
|
|
|
public function text($text = null, $callback1 = null, $callback2 = null, $callback3 = null) |
|
|
|
|
|
|
2870
|
|
|
{ |
|
2871
|
|
|
if (isset($text)) |
|
2872
|
|
|
return $this->html(htmlspecialchars($text)); |
|
2873
|
|
|
$args = func_get_args(); |
|
2874
|
|
|
$args = array_slice($args, 1); |
|
2875
|
|
|
$return = ''; |
|
2876
|
|
|
foreach ($this->elements as $node) { |
|
2877
|
|
|
$text = $node->textContent; |
|
2878
|
|
|
if (count($this->elements) > 1 && $text) |
|
2879
|
|
|
$text .= "\n"; |
|
2880
|
|
|
foreach ($args as $callback) { |
|
2881
|
|
|
$text = PhpQuery::callbackRun( |
|
2882
|
|
|
$callback, |
|
2883
|
|
|
array( |
|
2884
|
|
|
$text |
|
2885
|
|
|
) |
|
2886
|
|
|
); |
|
2887
|
|
|
} |
|
2888
|
|
|
$return .= $text; |
|
2889
|
|
|
} |
|
2890
|
|
|
return $return; |
|
2891
|
|
|
} |
|
2892
|
|
|
|
|
2893
|
|
|
/** |
|
2894
|
|
|
* @param null $attr |
|
2895
|
|
|
* @return The text content of each matching element, like |
|
2896
|
|
|
* text() but returns an array with one entry per matched element. |
|
2897
|
|
|
* Read only. |
|
2898
|
|
|
*/ |
|
2899
|
|
|
public function texts($attr = null) |
|
|
|
|
|
|
2900
|
|
|
{ |
|
2901
|
|
|
$results = array(); |
|
2902
|
|
|
foreach ($this->elements as $node) { |
|
2903
|
|
|
$results[] = $node->textContent; |
|
2904
|
|
|
} |
|
2905
|
|
|
return $results; |
|
2906
|
|
|
} |
|
2907
|
|
|
|
|
2908
|
|
|
/** |
|
2909
|
|
|
* Enter description here... |
|
2910
|
|
|
* |
|
2911
|
|
|
* @param $class |
|
2912
|
|
|
* @param null $file |
|
2913
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2914
|
|
|
*/ |
|
2915
|
|
|
public function plugin($class, $file = null) |
|
2916
|
|
|
{ |
|
2917
|
|
|
PhpQuery::plugin($class, $file); |
|
2918
|
|
|
return $this; |
|
2919
|
|
|
} |
|
2920
|
|
|
|
|
2921
|
|
|
/** |
|
2922
|
|
|
* Deprecated, use $pq->plugin() instead. |
|
2923
|
|
|
* |
|
2924
|
|
|
* @deprecated |
|
2925
|
|
|
* @param $class |
|
2926
|
|
|
* @param $file |
|
2927
|
|
|
* @return unknown_type |
|
2928
|
|
|
*/ |
|
2929
|
|
|
public static function extend($class, $file = null) |
|
2930
|
|
|
{ |
|
2931
|
|
|
return self::plugin($class, $file); |
|
2932
|
|
|
} |
|
2933
|
|
|
|
|
2934
|
|
|
/** |
|
2935
|
|
|
* |
|
2936
|
|
|
* @access private |
|
2937
|
|
|
* @param $method |
|
2938
|
|
|
* @param $args |
|
2939
|
|
|
* @throws \Exception |
|
2940
|
|
|
* @return unknown_type |
|
2941
|
|
|
*/ |
|
2942
|
|
|
public function __call($method, $args) |
|
2943
|
|
|
{ |
|
2944
|
|
|
$aliasMethods = array( |
|
2945
|
|
|
'clone', |
|
2946
|
|
|
'empty' |
|
2947
|
|
|
); |
|
2948
|
|
|
if (isset(PhpQuery::$extendMethods[$method])) { |
|
2949
|
|
|
array_unshift($args, $this); |
|
2950
|
|
|
return PhpQuery::callbackRun(PhpQuery::$extendMethods[$method], $args); |
|
2951
|
|
|
} else if (isset(PhpQuery::$pluginsMethods[$method])) { |
|
2952
|
|
|
array_unshift($args, $this); |
|
2953
|
|
|
$class = PhpQuery::$pluginsMethods[$method]; |
|
2954
|
|
|
$realClass = "\\PhpQuery\\Plugin\\$class"; |
|
2955
|
|
|
$return = call_user_func_array( |
|
2956
|
|
|
array( |
|
2957
|
|
|
$realClass, |
|
2958
|
|
|
$method |
|
2959
|
|
|
), |
|
2960
|
|
|
$args |
|
2961
|
|
|
); |
|
2962
|
|
|
// XXX deprecate ? |
|
2963
|
|
|
return is_null($return) ? $this : $return; |
|
|
|
|
|
|
2964
|
|
|
} else if (in_array($method, $aliasMethods)) { |
|
2965
|
|
|
return call_user_func_array( |
|
2966
|
|
|
array( |
|
2967
|
|
|
$this, |
|
2968
|
|
|
'_' . $method |
|
2969
|
|
|
), |
|
2970
|
|
|
$args |
|
2971
|
|
|
); |
|
2972
|
|
|
} else |
|
2973
|
|
|
throw new \Exception("Method '{$method}' doesnt exist"); |
|
2974
|
|
|
} |
|
2975
|
|
|
|
|
2976
|
|
|
/** |
|
2977
|
|
|
* Safe rename of next(). |
|
2978
|
|
|
* |
|
2979
|
|
|
* Use it ONLY when need to call next() on an iterated object (in same time). |
|
2980
|
|
|
* Normaly there is no need to do such thing ;) |
|
2981
|
|
|
* |
|
2982
|
|
|
* @param null $selector |
|
2983
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2984
|
|
|
* @access private |
|
2985
|
|
|
*/ |
|
2986
|
|
|
public function _next($selector = null) |
|
2987
|
|
|
{ |
|
2988
|
|
|
return $this->newInstance($this->getElementSiblings('nextSibling', $selector, true)); |
|
2989
|
|
|
} |
|
2990
|
|
|
|
|
2991
|
|
|
/** |
|
2992
|
|
|
* Use prev() and next(). |
|
2993
|
|
|
* |
|
2994
|
|
|
* @deprecated |
|
2995
|
|
|
* @param null $selector |
|
2996
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
2997
|
|
|
* @access private |
|
2998
|
|
|
*/ |
|
2999
|
|
|
public function _prev($selector = null) |
|
3000
|
|
|
{ |
|
3001
|
|
|
return $this->prev($selector); |
|
3002
|
|
|
} |
|
3003
|
|
|
|
|
3004
|
|
|
/** |
|
3005
|
|
|
* Enter description here... |
|
3006
|
|
|
* |
|
3007
|
|
|
* @param null $selector |
|
3008
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3009
|
|
|
*/ |
|
3010
|
|
|
public function prev($selector = null) |
|
3011
|
|
|
{ |
|
3012
|
|
|
return $this->newInstance($this->getElementSiblings('previousSibling', $selector, true)); |
|
3013
|
|
|
} |
|
3014
|
|
|
|
|
3015
|
|
|
/** |
|
3016
|
|
|
* @param null $selector |
|
3017
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3018
|
|
|
* @todo |
|
3019
|
|
|
*/ |
|
3020
|
|
|
public function prevAll($selector = null) |
|
3021
|
|
|
{ |
|
3022
|
|
|
return $this->newInstance($this->getElementSiblings('previousSibling', $selector)); |
|
3023
|
|
|
} |
|
3024
|
|
|
|
|
3025
|
|
|
/** |
|
3026
|
|
|
* @param null $selector |
|
3027
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3028
|
|
|
* @todo FIXME: returns source elements insted of next siblings |
|
3029
|
|
|
*/ |
|
3030
|
|
|
public function nextAll($selector = null) |
|
3031
|
|
|
{ |
|
3032
|
|
|
return $this->newInstance($this->getElementSiblings('nextSibling', $selector)); |
|
3033
|
|
|
} |
|
3034
|
|
|
|
|
3035
|
|
|
/** |
|
3036
|
|
|
* @access private |
|
3037
|
|
|
*/ |
|
3038
|
|
|
protected function getElementSiblings($direction, $selector = null, $limitToOne = false) |
|
3039
|
|
|
{ |
|
3040
|
|
|
$stack = array(); |
|
3041
|
|
|
$count = 0; |
|
|
|
|
|
|
3042
|
|
|
foreach ($this->stack() as $node) { |
|
3043
|
|
|
$test = $node; |
|
3044
|
|
|
while (isset($test->{$direction}) && $test->{$direction}) { |
|
3045
|
|
|
$test = $test->{$direction}; |
|
3046
|
|
|
if (!$test instanceof \DOMElement) |
|
3047
|
|
|
continue; |
|
3048
|
|
|
$stack[] = $test; |
|
3049
|
|
|
if ($limitToOne) |
|
3050
|
|
|
break; |
|
3051
|
|
|
} |
|
3052
|
|
|
} |
|
3053
|
|
|
if ($selector) { |
|
3054
|
|
|
$stackOld = $this->elements; |
|
3055
|
|
|
$this->elements = $stack; |
|
3056
|
|
|
$stack = $this->filter($selector, true)->stack(); |
|
3057
|
|
|
$this->elements = $stackOld; |
|
3058
|
|
|
} |
|
3059
|
|
|
return $stack; |
|
3060
|
|
|
} |
|
3061
|
|
|
|
|
3062
|
|
|
/** |
|
3063
|
|
|
* Enter description here... |
|
3064
|
|
|
* |
|
3065
|
|
|
* @param null $selector |
|
3066
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3067
|
|
|
*/ |
|
3068
|
|
|
public function siblings($selector = null) |
|
3069
|
|
|
{ |
|
3070
|
|
|
$stack = array(); |
|
3071
|
|
|
$siblings = array_merge( |
|
3072
|
|
|
$this->getElementSiblings('previousSibling', $selector), |
|
3073
|
|
|
$this->getElementSiblings('nextSibling', $selector) |
|
3074
|
|
|
); |
|
3075
|
|
|
foreach ($siblings as $node) { |
|
3076
|
|
|
if (!$this->elementsContainsNode($node, $stack)) |
|
3077
|
|
|
$stack[] = $node; |
|
3078
|
|
|
} |
|
3079
|
|
|
return $this->newInstance($stack); |
|
3080
|
|
|
} |
|
3081
|
|
|
|
|
3082
|
|
|
/** |
|
3083
|
|
|
* Enter description here... |
|
3084
|
|
|
* |
|
3085
|
|
|
* @param null $selector |
|
3086
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3087
|
|
|
*/ |
|
3088
|
|
|
public function not($selector = null) |
|
3089
|
|
|
{ |
|
3090
|
|
|
if (is_string($selector)) |
|
3091
|
|
|
PhpQuery::debug( |
|
3092
|
|
|
array( |
|
3093
|
|
|
'not', |
|
3094
|
|
|
$selector |
|
3095
|
|
|
) |
|
3096
|
|
|
); |
|
3097
|
|
|
else |
|
3098
|
|
|
PhpQuery::debug('not'); |
|
3099
|
|
|
$stack = array(); |
|
3100
|
|
|
if ($selector instanceof self || $selector instanceof \DOMNODE) { |
|
3101
|
|
|
foreach ($this->stack() as $node) { |
|
3102
|
|
|
if ($selector instanceof self) { |
|
3103
|
|
|
$matchFound = false; |
|
3104
|
|
|
foreach ($selector->stack() as $notNode) { |
|
3105
|
|
|
if ($notNode->isSameNode($node)) |
|
3106
|
|
|
$matchFound = true; |
|
3107
|
|
|
} |
|
3108
|
|
|
if (!$matchFound) |
|
3109
|
|
|
$stack[] = $node; |
|
3110
|
|
|
} else if ($selector instanceof \DOMNODE) { |
|
3111
|
|
|
if (!$selector->isSameNode($node)) |
|
3112
|
|
|
$stack[] = $node; |
|
3113
|
|
|
} else { |
|
3114
|
|
|
if (!$this->is($selector)) |
|
3115
|
|
|
$stack[] = $node; |
|
3116
|
|
|
} |
|
3117
|
|
|
} |
|
3118
|
|
|
} else { |
|
3119
|
|
|
$orgStack = $this->stack(); |
|
3120
|
|
|
$matched = $this->filter($selector, true)->stack(); |
|
3121
|
|
|
// $matched = array(); |
|
|
|
|
|
|
3122
|
|
|
// // simulate OR in filter() instead of AND 5y |
|
3123
|
|
|
// foreach($this->parseSelector($selector) as $s) { |
|
|
|
|
|
|
3124
|
|
|
// $matched = array_merge($matched, |
|
|
|
|
|
|
3125
|
|
|
// $this->filter(array($s))->stack() |
|
|
|
|
|
|
3126
|
|
|
// ); |
|
3127
|
|
|
// } |
|
3128
|
|
|
foreach ($orgStack as $node) |
|
3129
|
|
|
if (!$this->elementsContainsNode($node, $matched)) |
|
3130
|
|
|
$stack[] = $node; |
|
3131
|
|
|
} |
|
3132
|
|
|
return $this->newInstance($stack); |
|
3133
|
|
|
} |
|
3134
|
|
|
|
|
3135
|
|
|
/** |
|
3136
|
|
|
* Enter description here... |
|
3137
|
|
|
* |
|
3138
|
|
|
* @param string|PhpQueryObject |
|
3139
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3140
|
|
|
*/ |
|
3141
|
|
|
public function add($selector = null) |
|
3142
|
|
|
{ |
|
3143
|
|
|
if (!$selector) |
|
3144
|
|
|
return $this; |
|
3145
|
|
|
$stack = array(); |
|
|
|
|
|
|
3146
|
|
|
$this->elementsBackup = $this->elements; |
|
3147
|
|
|
$found = PhpQuery::pq($selector, $this->getDocumentID()); |
|
3148
|
|
|
$this->merge($found->elements); |
|
3149
|
|
|
return $this->newInstance(); |
|
3150
|
|
|
} |
|
3151
|
|
|
|
|
3152
|
|
|
/** |
|
3153
|
|
|
* @access private |
|
3154
|
|
|
*/ |
|
3155
|
|
|
protected function merge() |
|
3156
|
|
|
{ |
|
3157
|
|
|
foreach (func_get_args() as $nodes) |
|
3158
|
|
|
foreach ($nodes as $newNode) |
|
3159
|
|
|
if (!$this->elementsContainsNode($newNode)) |
|
3160
|
|
|
$this->elements[] = $newNode; |
|
3161
|
|
|
} |
|
3162
|
|
|
|
|
3163
|
|
|
/** |
|
3164
|
|
|
* @access private |
|
3165
|
|
|
* TODO refactor to stackContainsNode |
|
3166
|
|
|
*/ |
|
3167
|
|
|
protected function elementsContainsNode($nodeToCheck, $elementsStack = null) |
|
3168
|
|
|
{ |
|
3169
|
|
|
$loop = !is_null($elementsStack) ? $elementsStack : $this->elements; |
|
3170
|
|
|
foreach ($loop as $node) { |
|
3171
|
|
|
if ($node->isSameNode($nodeToCheck)) |
|
3172
|
|
|
return true; |
|
3173
|
|
|
} |
|
3174
|
|
|
return false; |
|
3175
|
|
|
} |
|
3176
|
|
|
|
|
3177
|
|
|
/** |
|
3178
|
|
|
* Enter description here... |
|
3179
|
|
|
* |
|
3180
|
|
|
* @param null $selector |
|
3181
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3182
|
|
|
*/ |
|
3183
|
|
|
public function parent($selector = null) |
|
3184
|
|
|
{ |
|
3185
|
|
|
$stack = array(); |
|
3186
|
|
|
foreach ($this->elements as $node) |
|
3187
|
|
|
if ($node->parentNode |
|
3188
|
|
|
&& !$this->elementsContainsNode($node->parentNode, $stack) |
|
3189
|
|
|
) |
|
3190
|
|
|
$stack[] = $node->parentNode; |
|
3191
|
|
|
$this->elementsBackup = $this->elements; |
|
3192
|
|
|
$this->elements = $stack; |
|
3193
|
|
|
if ($selector) |
|
3194
|
|
|
$this->filter($selector, true); |
|
3195
|
|
|
return $this->newInstance(); |
|
3196
|
|
|
} |
|
3197
|
|
|
|
|
3198
|
|
|
/** |
|
3199
|
|
|
* Enter description here... |
|
3200
|
|
|
* |
|
3201
|
|
|
* @param null $selector |
|
3202
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3203
|
|
|
*/ |
|
3204
|
|
|
public function parents($selector = null) |
|
3205
|
|
|
{ |
|
3206
|
|
|
$stack = array(); |
|
3207
|
|
|
if (!$this->elements) |
|
|
|
|
|
|
3208
|
|
|
$this->debug('parents() - stack empty'); |
|
3209
|
|
|
foreach ($this->elements as $node) { |
|
3210
|
|
|
$test = $node; |
|
3211
|
|
|
while ($test->parentNode) { |
|
3212
|
|
|
$test = $test->parentNode; |
|
3213
|
|
|
if ($this->isRoot($test)) |
|
3214
|
|
|
break; |
|
3215
|
|
|
if (!$this->elementsContainsNode($test, $stack)) { |
|
3216
|
|
|
$stack[] = $test; |
|
3217
|
|
|
continue; |
|
3218
|
|
|
} |
|
3219
|
|
|
} |
|
3220
|
|
|
} |
|
3221
|
|
|
$this->elementsBackup = $this->elements; |
|
3222
|
|
|
$this->elements = $stack; |
|
3223
|
|
|
if ($selector) |
|
3224
|
|
|
$this->filter($selector, true); |
|
3225
|
|
|
return $this->newInstance(); |
|
3226
|
|
|
} |
|
3227
|
|
|
|
|
3228
|
|
|
/** |
|
3229
|
|
|
* Internal stack iterator. |
|
3230
|
|
|
* |
|
3231
|
|
|
* @access private |
|
3232
|
|
|
* @param null $nodeTypes |
|
3233
|
|
|
* @return array {Array.<DOMNode>} |
|
3234
|
|
|
*/ |
|
3235
|
|
|
public function stack($nodeTypes = null) |
|
3236
|
|
|
{ |
|
3237
|
|
|
if (!isset($nodeTypes)) |
|
3238
|
|
|
return $this->elements; |
|
3239
|
|
|
if (!is_array($nodeTypes)) |
|
3240
|
|
|
$nodeTypes = array( |
|
3241
|
|
|
$nodeTypes |
|
3242
|
|
|
); |
|
3243
|
|
|
$return = array(); |
|
3244
|
|
|
foreach ($this->elements as $node) { |
|
3245
|
|
|
if (in_array($node->nodeType, $nodeTypes)) |
|
3246
|
|
|
$return[] = $node; |
|
3247
|
|
|
} |
|
3248
|
|
|
return $return; |
|
3249
|
|
|
} |
|
3250
|
|
|
|
|
3251
|
|
|
// TODO phpdoc; $oldAttr is result of hasAttribute, before any changes |
|
3252
|
|
|
protected function attrEvents($attr, $oldAttr, $oldValue, $node) |
|
3253
|
|
|
{ |
|
3254
|
|
|
// skip events for XML documents |
|
3255
|
|
|
if (!$this->isXHTML() && !$this->isHTML()) |
|
3256
|
|
|
return; |
|
3257
|
|
|
$event = null; |
|
3258
|
|
|
// identify |
|
3259
|
|
|
$isInputValue = $node->tagName == 'input' |
|
3260
|
|
|
&& (in_array( |
|
3261
|
|
|
$node->getAttribute('type'), |
|
3262
|
|
|
array( |
|
3263
|
|
|
'text', |
|
3264
|
|
|
'password', |
|
3265
|
|
|
'hidden' |
|
3266
|
|
|
) |
|
3267
|
|
|
) || !$node->getAttribute('type')); |
|
3268
|
|
|
$isRadio = $node->tagName == 'input' |
|
3269
|
|
|
&& $node->getAttribute('type') == 'radio'; |
|
3270
|
|
|
$isCheckbox = $node->tagName == 'input' |
|
3271
|
|
|
&& $node->getAttribute('type') == 'checkbox'; |
|
3272
|
|
|
$isOption = $node->tagName == 'option'; |
|
3273
|
|
|
if ($isInputValue && $attr == 'value' |
|
3274
|
|
|
&& $oldValue != $node->getAttribute($attr) |
|
3275
|
|
|
) { |
|
3276
|
|
|
$event = new Dom\DOMEvent(array( |
|
3277
|
|
|
'target' => $node, |
|
3278
|
|
|
'type' => 'change' |
|
3279
|
|
|
)); |
|
3280
|
|
|
} else if (($isRadio || $isCheckbox) && $attr == 'checked' |
|
3281
|
|
|
&& ( |
|
3282
|
|
|
// check |
|
3283
|
|
|
(!$oldAttr && $node->hasAttribute($attr)) |
|
3284
|
|
|
// un-check |
|
3285
|
|
|
|| (!$node->hasAttribute($attr) && $oldAttr)) |
|
3286
|
|
|
) { |
|
3287
|
|
|
$event = new Dom\DOMEvent(array( |
|
3288
|
|
|
'target' => $node, |
|
3289
|
|
|
'type' => 'change' |
|
3290
|
|
|
)); |
|
3291
|
|
|
} else if ($isOption && $node->parentNode && $attr == 'selected' |
|
3292
|
|
|
&& ( |
|
3293
|
|
|
// select |
|
3294
|
|
|
(!$oldAttr && $node->hasAttribute($attr)) |
|
3295
|
|
|
// un-select |
|
3296
|
|
|
|| (!$node->hasAttribute($attr) && $oldAttr)) |
|
3297
|
|
|
) { |
|
3298
|
|
|
$event = new Dom\DOMEvent(array( |
|
3299
|
|
|
'target' => $node->parentNode, |
|
3300
|
|
|
'type' => 'change' |
|
3301
|
|
|
)); |
|
3302
|
|
|
} |
|
3303
|
|
|
if ($event) { |
|
3304
|
|
|
PhpQueryEvents::trigger( |
|
3305
|
|
|
$this->getDocumentID(), |
|
3306
|
|
|
$event->type, |
|
3307
|
|
|
array( |
|
3308
|
|
|
$event |
|
3309
|
|
|
), |
|
3310
|
|
|
$node |
|
3311
|
|
|
); |
|
3312
|
|
|
} |
|
3313
|
|
|
} |
|
3314
|
|
|
|
|
3315
|
|
|
public function attr($attr = null, $value = null) |
|
3316
|
|
|
{ |
|
3317
|
|
|
foreach ($this->stack(1) as $node) { |
|
3318
|
|
|
if (!is_null($value)) { |
|
3319
|
|
|
$loop = $attr == '*' ? $this->getNodeAttrs($node) |
|
3320
|
|
|
: array( |
|
3321
|
|
|
$attr |
|
3322
|
|
|
); |
|
3323
|
|
|
foreach ($loop as $a) { |
|
3324
|
|
|
$oldValue = $node->getAttribute($a); |
|
3325
|
|
|
$oldAttr = $node->hasAttribute($a); |
|
3326
|
|
|
// TODO raises an error when charset other than UTF-8 |
|
3327
|
|
|
// while document's charset is also not UTF-8 |
|
3328
|
|
|
@$node->setAttribute($a, $value); |
|
|
|
|
|
|
3329
|
|
|
$this->attrEvents($a, $oldAttr, $oldValue, $node); |
|
3330
|
|
|
} |
|
3331
|
|
View Code Duplication |
} else if ($attr == '*') { |
|
|
|
|
|
|
3332
|
|
|
// jQuery difference |
|
3333
|
|
|
$return = array(); |
|
3334
|
|
|
foreach ($node->attributes as $n => $v) |
|
3335
|
|
|
$return[$n] = $v->value; |
|
3336
|
|
|
return $return; |
|
3337
|
|
|
} else |
|
3338
|
|
|
return $node->hasAttribute($attr) ? $node->getAttribute($attr) : null; |
|
3339
|
|
|
} |
|
3340
|
|
|
return is_null($value) ? '' : $this; |
|
3341
|
|
|
} |
|
3342
|
|
|
|
|
3343
|
|
|
/** |
|
3344
|
|
|
* @param null $attr |
|
3345
|
|
|
* @return The same attribute of each matching element, like |
|
3346
|
|
|
* attr() but returns an array with one entry per matched element. |
|
3347
|
|
|
* Read only. |
|
3348
|
|
|
*/ |
|
3349
|
|
|
public function attrs($attr = null) |
|
3350
|
|
|
{ |
|
3351
|
|
|
$results = array(); |
|
3352
|
|
|
foreach ($this->stack(1) as $node) { |
|
3353
|
|
|
$results[] = $node->hasAttribute($attr) ? $node->getAttribute($attr) |
|
3354
|
|
|
: null; |
|
3355
|
|
|
} |
|
3356
|
|
|
return $results; |
|
3357
|
|
|
} |
|
3358
|
|
|
|
|
3359
|
|
|
/** |
|
3360
|
|
|
* @access private |
|
3361
|
|
|
*/ |
|
3362
|
|
|
protected function getNodeAttrs($node) |
|
3363
|
|
|
{ |
|
3364
|
|
|
$return = array(); |
|
3365
|
|
|
foreach ($node->attributes as $n => $o) |
|
3366
|
|
|
$return[] = $n; |
|
3367
|
|
|
return $return; |
|
3368
|
|
|
} |
|
3369
|
|
|
|
|
3370
|
|
|
/** |
|
3371
|
|
|
* Enter description here... |
|
3372
|
|
|
* |
|
3373
|
|
|
* @param $attr |
|
3374
|
|
|
* @param $code |
|
3375
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3376
|
|
|
* @todo check CDATA ??? |
|
3377
|
|
|
*/ |
|
3378
|
|
|
public function attrPHP($attr, $code) |
|
3379
|
|
|
{ |
|
3380
|
|
|
if (!is_null($code)) { |
|
3381
|
|
|
$value = '<' . '?php ' . $code . ' ?' . '>'; |
|
3382
|
|
|
// TODO tempolary solution |
|
3383
|
|
|
// http://code.google.com/p/phpquery/issues/detail?id=17 |
|
3384
|
|
|
// if (function_exists('mb_detect_encoding') && mb_detect_encoding($value) == 'ASCII') |
|
|
|
|
|
|
3385
|
|
|
// $value = mb_convert_encoding($value, 'UTF-8', 'HTML-ENTITIES'); |
|
|
|
|
|
|
3386
|
|
|
} |
|
3387
|
|
|
foreach ($this->stack(1) as $node) { |
|
3388
|
|
View Code Duplication |
if (!is_null($code)) { |
|
|
|
|
|
|
3389
|
|
|
// $attrNode = $this->DOM->createAttribute($attr); |
|
|
|
|
|
|
3390
|
|
|
$node->setAttribute($attr, $value); |
|
|
|
|
|
|
3391
|
|
|
// $attrNode->value = $value; |
|
|
|
|
|
|
3392
|
|
|
// $node->appendChild($attrNode); |
|
|
|
|
|
|
3393
|
|
|
} else if ($attr == '*') { |
|
3394
|
|
|
// jQuery diff |
|
3395
|
|
|
$return = array(); |
|
3396
|
|
|
foreach ($node->attributes as $n => $v) |
|
3397
|
|
|
$return[$n] = $v->value; |
|
3398
|
|
|
return $return; |
|
|
|
|
|
|
3399
|
|
|
} else |
|
3400
|
|
|
return $node->getAttribute($attr); |
|
3401
|
|
|
} |
|
3402
|
|
|
return $this; |
|
3403
|
|
|
} |
|
3404
|
|
|
|
|
3405
|
|
|
/** |
|
3406
|
|
|
* Enter description here... |
|
3407
|
|
|
* |
|
3408
|
|
|
* @param $attr |
|
3409
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3410
|
|
|
*/ |
|
3411
|
|
|
public function removeAttr($attr) |
|
3412
|
|
|
{ |
|
3413
|
|
|
foreach ($this->stack(1) as $node) { |
|
3414
|
|
|
$loop = $attr == '*' ? $this->getNodeAttrs($node) |
|
3415
|
|
|
: array( |
|
3416
|
|
|
$attr |
|
3417
|
|
|
); |
|
3418
|
|
|
foreach ($loop as $a) { |
|
3419
|
|
|
$oldValue = $node->getAttribute($a); |
|
3420
|
|
|
$node->removeAttribute($a); |
|
3421
|
|
|
$this->attrEvents($a, $oldValue, null, $node); |
|
3422
|
|
|
} |
|
3423
|
|
|
} |
|
3424
|
|
|
return $this; |
|
3425
|
|
|
} |
|
3426
|
|
|
|
|
3427
|
|
|
/** |
|
3428
|
|
|
* Return form element value. |
|
3429
|
|
|
* |
|
3430
|
|
|
* @param null $val |
|
3431
|
|
|
* @return String Fields value. |
|
3432
|
|
|
*/ |
|
3433
|
|
|
public function val($val = null) |
|
3434
|
|
|
{ |
|
3435
|
|
|
if (!isset($val)) { |
|
3436
|
|
|
if ($this->eq(0)->is('select')) { |
|
3437
|
|
|
$selected = $this->eq(0)->find('option[selected=selected]'); |
|
3438
|
|
|
if ($selected->is('[value]')) |
|
3439
|
|
|
return $selected->attr('value'); |
|
3440
|
|
|
else |
|
3441
|
|
|
return $selected->text(); |
|
3442
|
|
|
} else if ($this->eq(0)->is('textarea')) |
|
3443
|
|
|
return $this->eq(0)->markup(); |
|
3444
|
|
|
else |
|
3445
|
|
|
return $this->eq(0)->attr('value'); |
|
3446
|
|
|
} else { |
|
3447
|
|
|
$_val = null; |
|
3448
|
|
|
foreach ($this->stack(1) as $node) { |
|
3449
|
|
|
$node = pq($node, $this->getDocumentID()); |
|
3450
|
|
|
if (is_array($val) |
|
3451
|
|
|
&& in_array( |
|
3452
|
|
|
$node->attr('type'), |
|
3453
|
|
|
array( |
|
3454
|
|
|
'checkbox', |
|
3455
|
|
|
'radio' |
|
3456
|
|
|
) |
|
3457
|
|
|
) |
|
3458
|
|
|
) { |
|
3459
|
|
|
$isChecked = in_array($node->attr('value'), $val) |
|
3460
|
|
|
|| in_array($node->attr('name'), $val); |
|
3461
|
|
|
if ($isChecked) |
|
3462
|
|
|
$node->attr('checked', 'checked'); |
|
3463
|
|
|
else |
|
3464
|
|
|
$node->removeAttr('checked'); |
|
3465
|
|
|
} else if ($node->get(0)->tagName == 'select') { |
|
3466
|
|
|
if (!isset($_val)) { |
|
3467
|
|
|
$_val = array(); |
|
3468
|
|
|
if (!is_array($val)) |
|
3469
|
|
|
$_val = array( |
|
3470
|
|
|
(string) $val |
|
3471
|
|
|
); |
|
3472
|
|
|
else |
|
3473
|
|
|
foreach ($val as $v) |
|
3474
|
|
|
$_val[] = $v; |
|
3475
|
|
|
} |
|
3476
|
|
|
foreach ($node['option']->stack(1) as $option) { |
|
3477
|
|
|
$option = pq($option, $this->getDocumentID()); |
|
3478
|
|
|
$selected = false; |
|
|
|
|
|
|
3479
|
|
|
// XXX: workaround for string comparsion, see issue #96 |
|
3480
|
|
|
// http://code.google.com/p/phpquery/issues/detail?id=96 |
|
3481
|
|
|
$selected = is_null($option->attr('value')) ? in_array($option->markup(), $_val) |
|
3482
|
|
|
: in_array($option->attr('value'), $_val); |
|
3483
|
|
|
// $optionValue = $option->attr('value'); |
|
|
|
|
|
|
3484
|
|
|
// $optionText = $option->text(); |
|
|
|
|
|
|
3485
|
|
|
// $optionTextLenght = mb_strlen($optionText); |
|
|
|
|
|
|
3486
|
|
|
// foreach($_val as $v) |
|
|
|
|
|
|
3487
|
|
|
// if ($optionValue == $v) |
|
|
|
|
|
|
3488
|
|
|
// $selected = true; |
|
|
|
|
|
|
3489
|
|
|
// else if ($optionText == $v && $optionTextLenght == mb_strlen($v)) |
|
|
|
|
|
|
3490
|
|
|
// $selected = true; |
|
|
|
|
|
|
3491
|
|
|
if ($selected) |
|
3492
|
|
|
$option->attr('selected', 'selected'); |
|
3493
|
|
|
else |
|
3494
|
|
|
$option->removeAttr('selected'); |
|
3495
|
|
|
} |
|
3496
|
|
|
} else if ($node->get(0)->tagName == 'textarea') |
|
3497
|
|
|
$node->markup($val); |
|
3498
|
|
|
else |
|
3499
|
|
|
$node->attr('value', $val); |
|
3500
|
|
|
} |
|
3501
|
|
|
} |
|
3502
|
|
|
return $this; |
|
3503
|
|
|
} |
|
3504
|
|
|
|
|
3505
|
|
|
/** |
|
3506
|
|
|
* Enter description here... |
|
3507
|
|
|
* |
|
3508
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3509
|
|
|
*/ |
|
3510
|
|
|
public function andSelf() |
|
3511
|
|
|
{ |
|
3512
|
|
|
if ($this->previous) |
|
3513
|
|
|
$this->elements = array_merge($this->elements, $this->previous->elements); |
|
3514
|
|
|
return $this; |
|
3515
|
|
|
} |
|
3516
|
|
|
|
|
3517
|
|
|
/** |
|
3518
|
|
|
* Enter description here... |
|
3519
|
|
|
* |
|
3520
|
|
|
* @param $className |
|
3521
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3522
|
|
|
*/ |
|
3523
|
|
|
public function addClass($className) |
|
3524
|
|
|
{ |
|
3525
|
|
|
if (!$className) |
|
3526
|
|
|
return $this; |
|
3527
|
|
|
foreach ($this->stack(1) as $node) { |
|
3528
|
|
|
if (!$this->is(".$className", $node)) |
|
3529
|
|
|
$node->setAttribute( |
|
3530
|
|
|
'class', |
|
3531
|
|
|
trim( |
|
3532
|
|
|
$node->getAttribute('class') . ' ' |
|
3533
|
|
|
. $className |
|
3534
|
|
|
) |
|
3535
|
|
|
); |
|
3536
|
|
|
} |
|
3537
|
|
|
return $this; |
|
3538
|
|
|
} |
|
3539
|
|
|
|
|
3540
|
|
|
/** |
|
3541
|
|
|
* Enter description here... |
|
3542
|
|
|
* |
|
3543
|
|
|
* @param $className |
|
3544
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3545
|
|
|
*/ |
|
3546
|
|
|
public function addClassPHP($className) |
|
3547
|
|
|
{ |
|
3548
|
|
|
foreach ($this->stack(1) as $node) { |
|
3549
|
|
|
$classes = $node->getAttribute('class'); |
|
3550
|
|
|
$newValue = $classes ? $classes . ' <' . '?php ' . $className . ' ?' |
|
3551
|
|
|
. '>' : '<' . '?php ' . $className . ' ?' . '>'; |
|
3552
|
|
|
$node->setAttribute('class', $newValue); |
|
3553
|
|
|
} |
|
3554
|
|
|
return $this; |
|
3555
|
|
|
} |
|
3556
|
|
|
|
|
3557
|
|
|
/** |
|
3558
|
|
|
* Enter description here... |
|
3559
|
|
|
* |
|
3560
|
|
|
* @param string $className |
|
3561
|
|
|
* @return bool |
|
3562
|
|
|
*/ |
|
3563
|
|
|
public function hasClass($className) |
|
3564
|
|
|
{ |
|
3565
|
|
|
foreach ($this->stack(1) as $node) { |
|
3566
|
|
|
if ($this->is(".$className", $node)) |
|
3567
|
|
|
return true; |
|
3568
|
|
|
} |
|
3569
|
|
|
return false; |
|
3570
|
|
|
} |
|
3571
|
|
|
|
|
3572
|
|
|
/** |
|
3573
|
|
|
* Enter description here... |
|
3574
|
|
|
* |
|
3575
|
|
|
* @param $className |
|
3576
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3577
|
|
|
*/ |
|
3578
|
|
|
public function removeClass($className) |
|
3579
|
|
|
{ |
|
3580
|
|
|
foreach ($this->stack(1) as $node) { |
|
3581
|
|
|
$classes = explode(' ', $node->getAttribute('class')); |
|
3582
|
|
|
if (in_array($className, $classes)) { |
|
3583
|
|
|
$classes = array_diff( |
|
3584
|
|
|
$classes, |
|
3585
|
|
|
array( |
|
3586
|
|
|
$className |
|
3587
|
|
|
) |
|
3588
|
|
|
); |
|
3589
|
|
|
if ($classes) |
|
|
|
|
|
|
3590
|
|
|
$node->setAttribute('class', implode(' ', $classes)); |
|
3591
|
|
|
else |
|
3592
|
|
|
$node->removeAttribute('class'); |
|
3593
|
|
|
} |
|
3594
|
|
|
} |
|
3595
|
|
|
return $this; |
|
3596
|
|
|
} |
|
3597
|
|
|
|
|
3598
|
|
|
/** |
|
3599
|
|
|
* Enter description here... |
|
3600
|
|
|
* |
|
3601
|
|
|
* @param $className |
|
3602
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3603
|
|
|
*/ |
|
3604
|
|
|
public function toggleClass($className) |
|
3605
|
|
|
{ |
|
3606
|
|
|
foreach ($this->stack(1) as $node) { |
|
3607
|
|
|
if ($this->is($node, '.' . $className)) |
|
3608
|
|
|
$this->removeClass($className); |
|
3609
|
|
|
else |
|
3610
|
|
|
$this->addClass($className); |
|
3611
|
|
|
} |
|
3612
|
|
|
return $this; |
|
3613
|
|
|
} |
|
3614
|
|
|
|
|
3615
|
|
|
/** |
|
3616
|
|
|
* Proper name without underscore (just ->empty()) also works. |
|
3617
|
|
|
* |
|
3618
|
|
|
* Removes all child nodes from the set of matched elements. |
|
3619
|
|
|
* |
|
3620
|
|
|
* Example: |
|
3621
|
|
|
* pq("p")._empty() |
|
3622
|
|
|
* |
|
3623
|
|
|
* HTML: |
|
3624
|
|
|
* <p>Hello, <span>Person</span> <a href="#">and person</a></p> |
|
3625
|
|
|
* |
|
3626
|
|
|
* Result: |
|
3627
|
|
|
* [ <p></p> ] |
|
3628
|
|
|
* |
|
3629
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3630
|
|
|
* @access private |
|
3631
|
|
|
*/ |
|
3632
|
|
|
public function _empty() |
|
3633
|
|
|
{ |
|
3634
|
|
|
foreach ($this->stack(1) as $node) { |
|
3635
|
|
|
// thx to 'dave at dgx dot cz' |
|
3636
|
|
|
$node->nodeValue = ''; |
|
3637
|
|
|
} |
|
3638
|
|
|
return $this; |
|
3639
|
|
|
} |
|
3640
|
|
|
|
|
3641
|
|
|
/** |
|
3642
|
|
|
* Enter description here... |
|
3643
|
|
|
* |
|
3644
|
|
|
* @param array|string $callback Expects $node as first param, $index as second |
|
3645
|
|
|
* @param null $param1 |
|
3646
|
|
|
* @param null $param2 |
|
3647
|
|
|
* @param null $param3 |
|
3648
|
|
|
* @internal param array $scope External variables passed to callback. Use compact('varName1', 'varName2'...) and extract($scope) |
|
3649
|
|
|
* @internal param array $arg1 Will ba passed as third and futher args to callback. |
|
3650
|
|
|
* @internal param array $arg2 Will ba passed as fourth and futher args to callback, and so on... |
|
3651
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3652
|
|
|
*/ |
|
3653
|
|
|
public function each($callback, $param1 = null, $param2 = null, $param3 = null) |
|
|
|
|
|
|
3654
|
|
|
{ |
|
3655
|
|
|
$paramStructure = null; |
|
3656
|
|
|
if (func_num_args() > 1) { |
|
3657
|
|
|
$paramStructure = func_get_args(); |
|
3658
|
|
|
$paramStructure = array_slice($paramStructure, 1); |
|
3659
|
|
|
} |
|
3660
|
|
|
foreach ($this->elements as $v) |
|
3661
|
|
|
PhpQuery::callbackRun( |
|
3662
|
|
|
$callback, |
|
3663
|
|
|
array( |
|
3664
|
|
|
$v |
|
3665
|
|
|
), |
|
3666
|
|
|
$paramStructure |
|
3667
|
|
|
); |
|
3668
|
|
|
return $this; |
|
3669
|
|
|
} |
|
3670
|
|
|
|
|
3671
|
|
|
/** |
|
3672
|
|
|
* Run callback on actual object. |
|
3673
|
|
|
* |
|
3674
|
|
|
* @param $callback |
|
3675
|
|
|
* @param null $param1 |
|
3676
|
|
|
* @param null $param2 |
|
3677
|
|
|
* @param null $param3 |
|
3678
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3679
|
|
|
*/ |
|
3680
|
|
|
public function callback($callback, $param1 = null, $param2 = null, $param3 = null) |
|
|
|
|
|
|
3681
|
|
|
{ |
|
3682
|
|
|
$params = func_get_args(); |
|
3683
|
|
|
$params[0] = $this; |
|
3684
|
|
|
PhpQuery::callbackRun($callback, $params); |
|
3685
|
|
|
return $this; |
|
3686
|
|
|
} |
|
3687
|
|
|
|
|
3688
|
|
|
/** |
|
3689
|
|
|
* Enter description here... |
|
3690
|
|
|
* |
|
3691
|
|
|
* @param $callback |
|
3692
|
|
|
* @param null $param1 |
|
3693
|
|
|
* @param null $param2 |
|
3694
|
|
|
* @param null $param3 |
|
3695
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3696
|
|
|
* @todo add $scope and $args as in each() ??? |
|
3697
|
|
|
*/ |
|
3698
|
|
|
public function map($callback, $param1 = null, $param2 = null, $param3 = null) |
|
|
|
|
|
|
3699
|
|
|
{ |
|
3700
|
|
|
// $stack = array(); |
|
|
|
|
|
|
3701
|
|
|
//// foreach($this->newInstance() as $node) { |
|
3702
|
|
|
// foreach($this->newInstance() as $node) { |
|
|
|
|
|
|
3703
|
|
|
// $result = call_user_func($callback, $node); |
|
|
|
|
|
|
3704
|
|
|
// if ($result) |
|
|
|
|
|
|
3705
|
|
|
// $stack[] = $result; |
|
|
|
|
|
|
3706
|
|
|
// } |
|
3707
|
|
|
$params = func_get_args(); |
|
3708
|
|
|
array_unshift($params, $this->elements); |
|
3709
|
|
|
return $this->newInstance( |
|
3710
|
|
|
call_user_func_array( |
|
3711
|
|
|
array( |
|
3712
|
|
|
'\\PhpQuery\\PhpQuery', |
|
3713
|
|
|
'map' |
|
3714
|
|
|
), |
|
3715
|
|
|
$params |
|
3716
|
|
|
) |
|
3717
|
|
|
// PhpQuery::map($this->elements, $callback) |
|
|
|
|
|
|
3718
|
|
|
); |
|
3719
|
|
|
} |
|
3720
|
|
|
|
|
3721
|
|
|
/** |
|
3722
|
|
|
* Enter description here... |
|
3723
|
|
|
* |
|
3724
|
|
|
* @param <type> $key |
|
|
|
|
|
|
3725
|
|
|
* @param <type> $value |
|
|
|
|
|
|
3726
|
|
|
* @return $this |
|
3727
|
|
|
*/ |
|
3728
|
|
|
public function data($key, $value = null) |
|
3729
|
|
|
{ |
|
3730
|
|
|
if (!isset($value)) { |
|
3731
|
|
|
// TODO? implement specific jQuery behavior od returning parent values |
|
3732
|
|
|
// is child which we look up doesn't exist |
|
3733
|
|
|
return PhpQuery::data($this->get(0), $key, $value, $this->getDocumentID()); |
|
3734
|
|
|
} else { |
|
3735
|
|
|
foreach ($this as $node) |
|
3736
|
|
|
PhpQuery::data($node, $key, $value, $this->getDocumentID()); |
|
3737
|
|
|
return $this; |
|
3738
|
|
|
} |
|
3739
|
|
|
} |
|
3740
|
|
|
|
|
3741
|
|
|
/** |
|
3742
|
|
|
* Enter description here... |
|
3743
|
|
|
* |
|
3744
|
|
|
* @param <type> $key |
|
|
|
|
|
|
3745
|
|
|
* @return $this |
|
3746
|
|
|
*/ |
|
3747
|
|
|
public function removeData($key) |
|
3748
|
|
|
{ |
|
3749
|
|
|
foreach ($this as $node) |
|
3750
|
|
|
PhpQuery::removeData($node, $key, $this->getDocumentID()); |
|
3751
|
|
|
return $this; |
|
3752
|
|
|
} |
|
3753
|
|
|
// INTERFACE IMPLEMENTATIONS |
|
3754
|
|
|
|
|
3755
|
|
|
// ITERATOR INTERFACE |
|
3756
|
|
|
/** |
|
3757
|
|
|
* @access private |
|
3758
|
|
|
*/ |
|
3759
|
|
|
public function rewind() |
|
3760
|
|
|
{ |
|
3761
|
|
|
$this->debug('iterating foreach'); |
|
3762
|
|
|
// PhpQuery::selectDocument($this->getDocumentID()); |
|
|
|
|
|
|
3763
|
|
|
$this->elementsBackup = $this->elements; |
|
3764
|
|
|
$this->elementsInterator = $this->elements; |
|
3765
|
|
|
$this->valid = isset($this->elements[0]) ? 1 : 0; |
|
|
|
|
|
|
3766
|
|
|
// $this->elements = $this->valid |
|
|
|
|
|
|
3767
|
|
|
// ? array($this->elements[0]) |
|
|
|
|
|
|
3768
|
|
|
// : array(); |
|
|
|
|
|
|
3769
|
|
|
$this->current = 0; |
|
3770
|
|
|
} |
|
3771
|
|
|
|
|
3772
|
|
|
/** |
|
3773
|
|
|
* @access private |
|
3774
|
|
|
*/ |
|
3775
|
|
|
public function current() |
|
3776
|
|
|
{ |
|
3777
|
|
|
return $this->elementsInterator[$this->current]; |
|
3778
|
|
|
} |
|
3779
|
|
|
|
|
3780
|
|
|
/** |
|
3781
|
|
|
* @access private |
|
3782
|
|
|
*/ |
|
3783
|
|
|
public function key() |
|
3784
|
|
|
{ |
|
3785
|
|
|
return $this->current; |
|
3786
|
|
|
} |
|
3787
|
|
|
|
|
3788
|
|
|
/** |
|
3789
|
|
|
* Double-function method. |
|
3790
|
|
|
* |
|
3791
|
|
|
* First: main iterator interface method. |
|
3792
|
|
|
* Second: Returning next sibling, alias for _next(). |
|
3793
|
|
|
* |
|
3794
|
|
|
* Proper functionality is choosed automagicaly. |
|
3795
|
|
|
* |
|
3796
|
|
|
* @see PhpQueryObject::_next() |
|
3797
|
|
|
* @param null $cssSelector |
|
3798
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3799
|
|
|
*/ |
|
3800
|
|
|
public function next($cssSelector = null) |
|
3801
|
|
|
{ |
|
3802
|
|
|
// if ($cssSelector || $this->valid) |
|
|
|
|
|
|
3803
|
|
|
// return $this->_next($cssSelector); |
|
|
|
|
|
|
3804
|
|
|
$this->valid = isset($this->elementsInterator[$this->current + 1]) ? true |
|
3805
|
|
|
: false; |
|
3806
|
|
|
if (!$this->valid && $this->elementsInterator) { |
|
|
|
|
|
|
3807
|
|
|
$this->elementsInterator = null; |
|
|
|
|
|
|
3808
|
|
|
} else if ($this->valid) { |
|
3809
|
|
|
$this->current++; |
|
3810
|
|
|
} else { |
|
3811
|
|
|
return $this->_next($cssSelector); |
|
3812
|
|
|
} |
|
3813
|
|
|
} |
|
3814
|
|
|
|
|
3815
|
|
|
/** |
|
3816
|
|
|
* @access private |
|
3817
|
|
|
*/ |
|
3818
|
|
|
public function valid() |
|
3819
|
|
|
{ |
|
3820
|
|
|
return $this->valid; |
|
3821
|
|
|
} |
|
3822
|
|
|
// ITERATOR INTERFACE END |
|
3823
|
|
|
// ARRAYACCESS INTERFACE |
|
3824
|
|
|
/** |
|
3825
|
|
|
* @access private |
|
3826
|
|
|
*/ |
|
3827
|
|
|
public function offsetExists($offset) |
|
3828
|
|
|
{ |
|
3829
|
|
|
return $this->find($offset)->size() > 0; |
|
3830
|
|
|
} |
|
3831
|
|
|
|
|
3832
|
|
|
/** |
|
3833
|
|
|
* @access private |
|
3834
|
|
|
*/ |
|
3835
|
|
|
public function offsetGet($offset) |
|
3836
|
|
|
{ |
|
3837
|
|
|
return $this->find($offset); |
|
3838
|
|
|
} |
|
3839
|
|
|
|
|
3840
|
|
|
/** |
|
3841
|
|
|
* @access private |
|
3842
|
|
|
*/ |
|
3843
|
|
|
public function offsetSet($offset, $value) |
|
3844
|
|
|
{ |
|
3845
|
|
|
// $this->find($offset)->replaceWith($value); |
|
|
|
|
|
|
3846
|
|
|
$this->find($offset)->html($value); |
|
3847
|
|
|
} |
|
3848
|
|
|
|
|
3849
|
|
|
/** |
|
3850
|
|
|
* @access private |
|
3851
|
|
|
*/ |
|
3852
|
|
|
public function offsetUnset($offset) |
|
3853
|
|
|
{ |
|
3854
|
|
|
// empty |
|
3855
|
|
|
throw new \Exception("Can't do unset, use array interface only for calling queries and replacing HTML."); |
|
3856
|
|
|
} |
|
3857
|
|
|
// ARRAYACCESS INTERFACE END |
|
3858
|
|
|
/** |
|
3859
|
|
|
* Returns node's XPath. |
|
3860
|
|
|
* |
|
3861
|
|
|
* @param unknown_type $oneNode |
|
3862
|
|
|
* @param null $namespace |
|
3863
|
|
|
* @return string |
|
3864
|
|
|
* @TODO use native getNodePath is avaible |
|
3865
|
|
|
* @access private |
|
3866
|
|
|
*/ |
|
3867
|
|
|
protected function getNodeXpath($oneNode = null, $namespace = null) |
|
|
|
|
|
|
3868
|
|
|
{ |
|
3869
|
|
|
$return = array(); |
|
3870
|
|
|
$loop = $oneNode ? array( |
|
3871
|
|
|
$oneNode |
|
3872
|
|
|
) : $this->elements; |
|
3873
|
|
|
// if ($namespace) |
|
|
|
|
|
|
3874
|
|
|
// $namespace .= ':'; |
|
|
|
|
|
|
3875
|
|
|
foreach ($loop as $node) { |
|
3876
|
|
|
if ($node instanceof \DOMDocument) { |
|
3877
|
|
|
$return[] = ''; |
|
3878
|
|
|
continue; |
|
3879
|
|
|
} |
|
3880
|
|
|
$xpath = array(); |
|
3881
|
|
|
while (!($node instanceof \DOMDocument)) { |
|
3882
|
|
|
$i = 1; |
|
3883
|
|
|
$sibling = $node; |
|
3884
|
|
|
while ($sibling->previousSibling) { |
|
3885
|
|
|
$sibling = $sibling->previousSibling; |
|
3886
|
|
|
$isElement = $sibling instanceof \DOMElement; |
|
3887
|
|
|
if ($isElement && $sibling->tagName == $node->tagName) |
|
3888
|
|
|
$i++; |
|
3889
|
|
|
} |
|
3890
|
|
|
$xpath[] = $this->isXML() ? "*[local-name()='{$node->tagName}'][{$i}]" |
|
3891
|
|
|
: "{$node->tagName}[{$i}]"; |
|
3892
|
|
|
$node = $node->parentNode; |
|
3893
|
|
|
} |
|
3894
|
|
|
$xpath = join('/', array_reverse($xpath)); |
|
3895
|
|
|
$return[] = '/' . $xpath; |
|
3896
|
|
|
} |
|
3897
|
|
|
return $oneNode ? $return[0] : $return; |
|
3898
|
|
|
} |
|
3899
|
|
|
|
|
3900
|
|
|
// HELPERS |
|
3901
|
|
|
public function whois($oneNode = null) |
|
3902
|
|
|
{ |
|
3903
|
|
|
$return = array(); |
|
3904
|
|
|
$loop = $oneNode ? array( |
|
3905
|
|
|
$oneNode |
|
3906
|
|
|
) : $this->elements; |
|
3907
|
|
|
foreach ($loop as $node) { |
|
3908
|
|
|
if (isset($node->tagName)) { |
|
3909
|
|
|
$tag = in_array( |
|
3910
|
|
|
$node->tagName, |
|
3911
|
|
|
array( |
|
3912
|
|
|
'php', |
|
3913
|
|
|
'js' |
|
3914
|
|
|
) |
|
3915
|
|
|
) ? strtoupper($node->tagName) : $node->tagName; |
|
3916
|
|
|
$return[] = $tag |
|
3917
|
|
|
. ($node->getAttribute('id') ? '#' . $node->getAttribute('id') : '') |
|
3918
|
|
|
. ($node->getAttribute('class') ? '.' |
|
3919
|
|
|
. join('.', explode(' ', $node->getAttribute('class'))) : '') |
|
3920
|
|
|
. ($node->getAttribute('name') ? '[name="' |
|
3921
|
|
|
. $node->getAttribute('name') . '"]' : '') |
|
3922
|
|
|
. ($node->getAttribute('value') |
|
3923
|
|
|
&& strpos($node->getAttribute('value'), '<' . '?php') === false ? '[value="' |
|
3924
|
|
|
. substr(str_replace("\n", '', $node->getAttribute('value')), 0, 15) |
|
3925
|
|
|
. '"]' : '') |
|
3926
|
|
|
. ($node->getAttribute('value') |
|
3927
|
|
|
&& strpos($node->getAttribute('value'), '<' . '?php') !== false ? '[value=PHP]' |
|
3928
|
|
|
: '') . ($node->getAttribute('selected') ? '[selected]' : '') |
|
3929
|
|
|
. ($node->getAttribute('checked') ? '[checked]' : ''); |
|
3930
|
|
|
} else if ($node instanceof \DOMTEXT) { |
|
3931
|
|
|
if (trim($node->textContent)) |
|
3932
|
|
|
$return[] = 'Text:' |
|
3933
|
|
|
. substr(str_replace("\n", ' ', $node->textContent), 0, 15); |
|
3934
|
|
|
} else { |
|
|
|
|
|
|
3935
|
|
|
|
|
3936
|
|
|
} |
|
3937
|
|
|
} |
|
3938
|
|
|
return $oneNode && isset($return[0]) ? $return[0] : $return; |
|
3939
|
|
|
} |
|
3940
|
|
|
|
|
3941
|
|
|
/** |
|
3942
|
|
|
* Dump htmlOuter and preserve chain. Usefull for debugging. |
|
3943
|
|
|
* |
|
3944
|
|
|
* @return PhpQueryObject|QueryTemplatesSource|QueryTemplatesParse|QueryTemplatesSourceQuery |
|
3945
|
|
|
* |
|
3946
|
|
|
*/ |
|
3947
|
|
|
public function dump() |
|
3948
|
|
|
{ |
|
3949
|
|
|
print 'DUMP #' . (PhpQuery::$dumpCount++) . ' '; |
|
3950
|
|
|
PhpQuery::$debug = false; |
|
3951
|
|
|
// print __FILE__.':'.__LINE__."\n"; |
|
|
|
|
|
|
3952
|
|
|
var_dump($this->htmlOuter()); |
|
|
|
|
|
|
3953
|
|
|
return $this; |
|
3954
|
|
|
} |
|
3955
|
|
|
|
|
3956
|
|
View Code Duplication |
public function dumpWhois() |
|
|
|
|
|
|
3957
|
|
|
{ |
|
3958
|
|
|
print 'DUMP #' . (PhpQuery::$dumpCount++) . ' '; |
|
3959
|
|
|
$debug = PhpQuery::$debug; |
|
3960
|
|
|
PhpQuery::$debug = false; |
|
3961
|
|
|
// print __FILE__.':'.__LINE__."\n"; |
|
|
|
|
|
|
3962
|
|
|
var_dump('whois', $this->whois()); |
|
|
|
|
|
|
3963
|
|
|
PhpQuery::$debug = $debug; |
|
3964
|
|
|
return $this; |
|
3965
|
|
|
} |
|
3966
|
|
|
|
|
3967
|
|
View Code Duplication |
public function dumpLength() |
|
|
|
|
|
|
3968
|
|
|
{ |
|
3969
|
|
|
print 'DUMP #' . (PhpQuery::$dumpCount++) . ' '; |
|
3970
|
|
|
$debug = PhpQuery::$debug; |
|
3971
|
|
|
PhpQuery::$debug = false; |
|
3972
|
|
|
// print __FILE__.':'.__LINE__."\n"; |
|
|
|
|
|
|
3973
|
|
|
var_dump('length', $this->length()); |
|
|
|
|
|
|
3974
|
|
|
PhpQuery::$debug = $debug; |
|
3975
|
|
|
return $this; |
|
3976
|
|
|
} |
|
3977
|
|
|
|
|
3978
|
|
|
public function dumpTree($html = true, $title = true) |
|
3979
|
|
|
{ |
|
3980
|
|
|
$output = $title ? 'DUMP #' . (PhpQuery::$dumpCount++) . " \n" : ''; |
|
3981
|
|
|
$debug = PhpQuery::$debug; |
|
3982
|
|
|
PhpQuery::$debug = false; |
|
3983
|
|
|
foreach ($this->stack() as $node) |
|
3984
|
|
|
$output .= $this->__dumpTree($node); |
|
3985
|
|
|
PhpQuery::$debug = $debug; |
|
3986
|
|
|
print $html ? nl2br(str_replace(' ', ' ', $output)) : $output; |
|
3987
|
|
|
return $this; |
|
3988
|
|
|
} |
|
3989
|
|
|
|
|
3990
|
|
|
private function __dumpTree($node, $intend = 0) |
|
3991
|
|
|
{ |
|
3992
|
|
|
$whois = $this->whois($node); |
|
3993
|
|
|
$return = ''; |
|
3994
|
|
|
if ($whois) |
|
3995
|
|
|
$return .= str_repeat(' - ', $intend) . $whois . "\n"; |
|
3996
|
|
|
if (isset($node->childNodes)) |
|
3997
|
|
|
foreach ($node->childNodes as $chNode) |
|
3998
|
|
|
$return .= $this->__dumpTree($chNode, $intend + 1); |
|
3999
|
|
|
return $return; |
|
4000
|
|
|
} |
|
4001
|
|
|
|
|
4002
|
|
|
/** |
|
4003
|
|
|
* Dump htmlOuter and stop script execution. Usefull for debugging. |
|
4004
|
|
|
* |
|
4005
|
|
|
*/ |
|
4006
|
|
|
public function dumpDie() |
|
4007
|
|
|
{ |
|
4008
|
|
|
print __FILE__ . ':' . __LINE__; |
|
4009
|
|
|
var_dump($this->htmlOuter()); |
|
|
|
|
|
|
4010
|
|
|
die(); |
|
|
|
|
|
|
4011
|
|
|
} |
|
4012
|
|
|
} |
|
4013
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.