1
|
|
|
<?php
|
2
|
|
|
/**
|
3
|
|
|
* @author Niels A.D.
|
4
|
|
|
* @author Todd Burry <[email protected]>
|
5
|
|
|
* @copyright 2010 Niels A.D., 2014 Todd Burry
|
6
|
|
|
* @license http://opensource.org/licenses/LGPL-2.1 LGPL-2.1
|
7
|
|
|
* @package pQuery
|
8
|
|
|
*/
|
9
|
|
|
|
10
|
|
|
namespace pQuery;
|
11
|
|
|
|
12
|
|
|
/**
|
13
|
|
|
* Holds (x)html/xml tag information like tag name, attributes,
|
14
|
|
|
* parent, children, self close, etc.
|
15
|
|
|
*
|
16
|
|
|
*/
|
17
|
|
|
class DomNode implements IQuery {
|
18
|
|
|
|
19
|
|
|
/**
|
20
|
|
|
* Element Node, used for regular elements
|
21
|
|
|
*/
|
22
|
|
|
const NODE_ELEMENT = 0;
|
23
|
|
|
/**
|
24
|
|
|
* Text Node
|
25
|
|
|
*/
|
26
|
|
|
const NODE_TEXT = 1;
|
27
|
|
|
/**
|
28
|
|
|
* Comment Node
|
29
|
|
|
*/
|
30
|
|
|
const NODE_COMMENT = 2;
|
31
|
|
|
/**
|
32
|
|
|
* Conditional Node (<![if]> <![endif])
|
33
|
|
|
*/
|
34
|
|
|
const NODE_CONDITIONAL = 3;
|
35
|
|
|
/**
|
36
|
|
|
* CDATA Node (<![CDATA[]]>
|
37
|
|
|
*/
|
38
|
|
|
const NODE_CDATA = 4;
|
39
|
|
|
/**
|
40
|
|
|
* Doctype Node
|
41
|
|
|
*/
|
42
|
|
|
const NODE_DOCTYPE = 5;
|
43
|
|
|
/**
|
44
|
|
|
* XML Node, used for tags that start with ?, like <?xml and <?php
|
45
|
|
|
*/
|
46
|
|
|
const NODE_XML = 6;
|
47
|
|
|
/**
|
48
|
|
|
* ASP Node
|
49
|
|
|
*/
|
50
|
|
|
const NODE_ASP = 7;
|
51
|
|
|
|
52
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
53
|
|
|
#static $NODE_TYPE = self::NODE_ELEMENT;
|
|
|
|
|
54
|
|
|
#php4e
|
55
|
|
|
#php5
|
56
|
|
|
/**
|
57
|
|
|
* Node type of class
|
58
|
|
|
*/
|
59
|
|
|
const NODE_TYPE = self::NODE_ELEMENT;
|
60
|
|
|
#php5e
|
61
|
|
|
|
62
|
|
|
|
63
|
|
|
/**
|
64
|
|
|
* Name of the selector class
|
65
|
|
|
* @var string
|
66
|
|
|
* @see select()
|
67
|
|
|
*/
|
68
|
|
|
var $selectClass = 'pQuery\\HtmlSelector';
|
|
|
|
|
69
|
|
|
/**
|
70
|
|
|
* Name of the parser class
|
71
|
|
|
* @var string
|
72
|
|
|
* @see setOuterText()
|
73
|
|
|
* @see setInnerText()
|
74
|
|
|
*/
|
75
|
|
|
var $parserClass = 'pQuery\\Html5Parser';
|
|
|
|
|
76
|
|
|
|
77
|
|
|
/**
|
78
|
|
|
* Name of the class used for {@link addChild()}
|
79
|
|
|
* @var string
|
80
|
|
|
*/
|
81
|
|
|
var $childClass = __CLASS__;
|
|
|
|
|
82
|
|
|
/**
|
83
|
|
|
* Name of the class used for {@link addText()}
|
84
|
|
|
* @var string
|
85
|
|
|
*/
|
86
|
|
|
var $childClass_Text = 'pQuery\\TextNode';
|
|
|
|
|
87
|
|
|
/**
|
88
|
|
|
* Name of the class used for {@link addComment()}
|
89
|
|
|
* @var string
|
90
|
|
|
*/
|
91
|
|
|
var $childClass_Comment = 'pQuery\\CommentNode';
|
|
|
|
|
92
|
|
|
/**
|
93
|
|
|
* Name of the class used for {@link addContional()}
|
94
|
|
|
* @var string
|
95
|
|
|
*/
|
96
|
|
|
var $childClass_Conditional = 'pQuery\\ConditionalTagNode';
|
|
|
|
|
97
|
|
|
/**
|
98
|
|
|
* Name of the class used for {@link addCDATA()}
|
99
|
|
|
* @var string
|
100
|
|
|
*/
|
101
|
|
|
var $childClass_CDATA = 'pQuery\\CdataNode';
|
|
|
|
|
102
|
|
|
/**
|
103
|
|
|
* Name of the class used for {@link addDoctype()}
|
104
|
|
|
* @var string
|
105
|
|
|
*/
|
106
|
|
|
var $childClass_Doctype = 'pQuery\\DoctypeNode';
|
|
|
|
|
107
|
|
|
/**
|
108
|
|
|
* Name of the class used for {@link addXML()}
|
109
|
|
|
* @var string
|
110
|
|
|
*/
|
111
|
|
|
var $childClass_XML = 'pQuery\\XmlNode';
|
|
|
|
|
112
|
|
|
/**
|
113
|
|
|
* Name of the class used for {@link addASP()}
|
114
|
|
|
* @var string
|
115
|
|
|
*/
|
116
|
|
|
var $childClass_ASP = 'pQuery\\AspEmbeddedNode';
|
|
|
|
|
117
|
|
|
|
118
|
|
|
/**
|
119
|
|
|
* Parent node, null if none
|
120
|
|
|
* @var DomNode
|
121
|
|
|
* @see changeParent()
|
122
|
|
|
*/
|
123
|
|
|
var $parent = null;
|
|
|
|
|
124
|
|
|
|
125
|
|
|
/**
|
126
|
|
|
* Attributes of node
|
127
|
|
|
* @var array
|
128
|
|
|
* @internal array('attribute' => 'value')
|
129
|
|
|
* @internal Public for faster access!
|
130
|
|
|
* @see getAttribute()
|
131
|
|
|
* @see setAttribute()
|
132
|
|
|
* @access private
|
133
|
|
|
*/
|
134
|
|
|
var $attributes = array();
|
|
|
|
|
135
|
|
|
|
136
|
|
|
/**
|
137
|
|
|
* Namespace info for attributes
|
138
|
|
|
* @var array
|
139
|
|
|
* @internal array('tag' => array(array('ns', 'tag', 'ns:tag', index)))
|
140
|
|
|
* @internal Public for easy outside modifications!
|
141
|
|
|
* @see findAttribute()
|
142
|
|
|
* @access private
|
143
|
|
|
*/
|
144
|
|
|
var $attributes_ns = null;
|
|
|
|
|
145
|
|
|
|
146
|
|
|
/**
|
147
|
|
|
* Array of child nodes
|
148
|
|
|
* @var array
|
149
|
|
|
* @internal Public for faster access!
|
150
|
|
|
* @see childCount()
|
151
|
|
|
* @see getChild()
|
152
|
|
|
* @see addChild()
|
153
|
|
|
* @see deleteChild()
|
154
|
|
|
* @access private
|
155
|
|
|
*/
|
156
|
|
|
var $children = array();
|
|
|
|
|
157
|
|
|
|
158
|
|
|
/**
|
159
|
|
|
* Full tag name (including namespace)
|
160
|
|
|
* @var string
|
161
|
|
|
* @see getTagName()
|
162
|
|
|
* @see getNamespace()
|
163
|
|
|
*/
|
164
|
|
|
var $tag = '';
|
|
|
|
|
165
|
|
|
|
166
|
|
|
/**
|
167
|
|
|
* Namespace info for tag
|
168
|
|
|
* @var array
|
169
|
|
|
* @internal array('namespace', 'tag')
|
170
|
|
|
* @internal Public for easy outside modifications!
|
171
|
|
|
* @access private
|
172
|
|
|
*/
|
173
|
|
|
var $tag_ns = null;
|
|
|
|
|
174
|
|
|
|
175
|
|
|
/**
|
176
|
|
|
* Is node a self closing node? No closing tag if true.
|
177
|
|
|
* @var bool
|
178
|
|
|
*/
|
179
|
|
|
var $self_close = false;
|
|
|
|
|
180
|
|
|
|
181
|
|
|
/**
|
182
|
|
|
* If self close, then this will be used to close the tag
|
183
|
|
|
* @var string
|
184
|
|
|
* @see $self_close
|
185
|
|
|
*/
|
186
|
|
|
var $self_close_str = ' /';
|
|
|
|
|
187
|
|
|
|
188
|
|
|
/**
|
189
|
|
|
* Use short tags for attributes? If true, then attributes
|
190
|
|
|
* with values equal to the attribute name will not output
|
191
|
|
|
* the value, e.g. selected="selected" will be selected.
|
192
|
|
|
* @var bool
|
193
|
|
|
*/
|
194
|
|
|
var $attribute_shorttag = true;
|
|
|
|
|
195
|
|
|
|
196
|
|
|
/**
|
197
|
|
|
* Function map used for the selector filter
|
198
|
|
|
* @var array
|
199
|
|
|
* @internal array('root' => 'filter_root') will cause the
|
200
|
|
|
* selector to call $this->filter_root at :root
|
201
|
|
|
* @access private
|
202
|
|
|
*/
|
203
|
|
|
var $filter_map = array(
|
|
|
|
|
204
|
|
|
'root' => 'filter_root',
|
205
|
|
|
'nth-child' => 'filter_nchild',
|
206
|
|
|
'eq' => 'filter_nchild', //jquery (naming) compatibility
|
207
|
|
|
'gt' => 'filter_gt',
|
208
|
|
|
'lt' => 'filter_lt',
|
209
|
|
|
'nth-last-child' => 'filter_nlastchild',
|
210
|
|
|
'nth-of-type' => 'filter_ntype',
|
211
|
|
|
'nth-last-of-type' => 'filter_nlastype',
|
212
|
|
|
'odd' => 'filter_odd',
|
213
|
|
|
'even' => 'filter_even',
|
214
|
|
|
'every' => 'filter_every',
|
215
|
|
|
'first-child' => 'filter_first',
|
216
|
|
|
'last-child' => 'filter_last',
|
217
|
|
|
'first-of-type' => 'filter_firsttype',
|
218
|
|
|
'last-of-type' => 'filter_lasttype',
|
219
|
|
|
'only-child' => 'filter_onlychild',
|
220
|
|
|
'only-of-type' => 'filter_onlytype',
|
221
|
|
|
'empty' => 'filter_empty',
|
222
|
|
|
'not-empty' => 'filter_notempty',
|
223
|
|
|
'has-text' => 'filter_hastext',
|
224
|
|
|
'no-text' => 'filter_notext',
|
225
|
|
|
'lang' => 'filter_lang',
|
226
|
|
|
'contains' => 'filter_contains',
|
227
|
|
|
'has' => 'filter_has',
|
228
|
|
|
'not' => 'filter_not',
|
229
|
|
|
'element' => 'filter_element',
|
230
|
|
|
'text' => 'filter_text',
|
231
|
|
|
'comment' => 'filter_comment',
|
232
|
|
|
'checked' => 'filter_checked',
|
233
|
|
|
'selected' => 'filter_selected',
|
234
|
|
|
);
|
235
|
|
|
|
236
|
|
|
/**
|
237
|
|
|
* Class constructor
|
238
|
|
|
* @param string|array $tag Name of the tag, or array with taginfo (array(
|
239
|
|
|
* 'tag_name' => 'tag',
|
240
|
|
|
* 'self_close' => false,
|
241
|
|
|
* 'attributes' => array('attribute' => 'value')))
|
242
|
|
|
* @param DomNode $parent Parent of node, null if none
|
243
|
|
|
*/
|
244
|
37 |
|
function __construct($tag, $parent) {
|
|
|
|
|
245
|
37 |
|
$this->parent = $parent;
|
246
|
|
|
|
247
|
37 |
|
if (is_string($tag)) {
|
248
|
37 |
|
$this->tag = $tag;
|
249
|
37 |
|
} else {
|
250
|
37 |
|
$this->tag = $tag['tag_name'];
|
251
|
37 |
|
$this->self_close = $tag['self_close'];
|
252
|
37 |
|
$this->attributes = $tag['attributes'];
|
253
|
|
|
}
|
254
|
37 |
|
}
|
255
|
|
|
|
256
|
|
|
#php4 PHP4 class constructor compatibility
|
257
|
|
|
#function DomNode($tag, $parent) {return $this->__construct($tag, $parent);}
|
|
|
|
|
258
|
|
|
#php4e
|
259
|
|
|
|
260
|
|
|
/**
|
261
|
|
|
* Class destructor
|
262
|
|
|
* @access private
|
263
|
|
|
*/
|
264
|
15 |
|
function __destruct() {
|
|
|
|
|
265
|
15 |
|
$this->delete();
|
266
|
15 |
|
}
|
267
|
|
|
|
268
|
|
|
/**
|
269
|
|
|
* Class toString, outputs {@link $tag}
|
270
|
|
|
* @return string
|
271
|
|
|
* @access private
|
272
|
|
|
*/
|
273
|
|
|
function __toString() {
|
|
|
|
|
274
|
|
|
return (($this->tag === '~root~') ? $this->toString(true, true, 1) : $this->tag);
|
275
|
|
|
}
|
276
|
|
|
|
277
|
|
|
/**
|
278
|
|
|
* Class magic get method, outputs {@link getAttribute()}
|
279
|
|
|
* @return string
|
280
|
|
|
* @access private
|
281
|
|
|
*/
|
282
|
4 |
|
function __get($attribute) {
|
|
|
|
|
283
|
4 |
|
return $this->getAttribute($attribute);
|
284
|
|
|
}
|
285
|
|
|
|
286
|
|
|
/**
|
287
|
|
|
* Class magic set method, performs {@link setAttribute()}
|
288
|
|
|
* @access private
|
289
|
|
|
*/
|
290
|
4 |
|
function __set($attribute, $value) {
|
|
|
|
|
291
|
4 |
|
$this->setAttribute($attribute, $value);
|
292
|
4 |
|
}
|
293
|
|
|
|
294
|
|
|
/**
|
295
|
|
|
* Class magic isset method, returns {@link hasAttribute()}
|
296
|
|
|
* @return bool
|
297
|
|
|
* @access private
|
298
|
|
|
*/
|
299
|
|
|
function __isset($attribute) {
|
|
|
|
|
300
|
|
|
return $this->hasAttribute($attribute);
|
301
|
|
|
}
|
302
|
|
|
|
303
|
|
|
/**
|
304
|
|
|
* Class magic unset method, performs {@link deleteAttribute()}
|
305
|
|
|
* @access private
|
306
|
|
|
*/
|
307
|
1 |
|
function __unset($attribute) {
|
|
|
|
|
308
|
1 |
|
return $this->deleteAttribute($attribute);
|
309
|
|
|
}
|
310
|
|
|
|
311
|
|
|
/**
|
312
|
|
|
* Class magic invoke method, performs {@link query()}.
|
313
|
|
|
* @param string $query The css query to run on the nodes.
|
314
|
|
|
* @return \pQuery
|
315
|
|
|
*/
|
316
|
3 |
|
function __invoke($query = '*') {
|
|
|
|
|
317
|
3 |
|
return $this->query($query);
|
318
|
|
|
}
|
319
|
|
|
|
320
|
|
|
/**
|
321
|
|
|
* Returns place in document
|
322
|
|
|
* @return string
|
323
|
|
|
*/
|
324
|
|
|
function dumpLocation() {
|
|
|
|
|
325
|
|
|
return (($this->parent) ? (($p = $this->parent->dumpLocation()) ? $p.' > ' : '').$this->tag.'('.$this->typeIndex().')' : '');
|
326
|
|
|
}
|
327
|
|
|
|
328
|
|
|
/**
|
329
|
|
|
* Returns all the attributes and their values
|
330
|
|
|
* @return string
|
331
|
|
|
* @access private
|
332
|
|
|
*/
|
333
|
20 |
|
protected function toString_attributes() {
|
334
|
20 |
|
$s = '';
|
335
|
20 |
|
foreach($this->attributes as $a => $v) {
|
336
|
17 |
|
$s .= ' '.$a;
|
337
|
17 |
|
if ((!$this->attribute_shorttag) || ($v !== $a)) {
|
338
|
17 |
|
$quote = (strpos($v, '"') === false) ? '"' : "'";
|
339
|
17 |
|
$s .= '='.$quote.$v.$quote;
|
340
|
17 |
|
}
|
341
|
20 |
|
}
|
342
|
20 |
|
return $s;
|
343
|
|
|
}
|
344
|
|
|
|
345
|
|
|
/**
|
346
|
|
|
* Returns the content of the node (child tags and text)
|
347
|
|
|
* @param bool $attributes Print attributes of child tags
|
348
|
|
|
* @param bool|int $recursive How many sublevels of childtags to print. True for all.
|
349
|
|
|
* @param bool $content_only Only print text, false will print tags too.
|
350
|
|
|
* @return string
|
351
|
|
|
* @access private
|
352
|
|
|
*/
|
353
|
23 |
|
protected function toString_content($attributes = true, $recursive = true, $content_only = false) {
|
354
|
23 |
|
$s = '';
|
355
|
23 |
|
foreach($this->children as $c) {
|
356
|
23 |
|
$s .= $c->toString($attributes, $recursive, $content_only);
|
357
|
23 |
|
}
|
358
|
23 |
|
return $s;
|
359
|
|
|
}
|
360
|
|
|
|
361
|
|
|
/**
|
362
|
|
|
* Returns the node as string
|
363
|
|
|
* @param bool $attributes Print attributes (of child tags)
|
364
|
|
|
* @param bool|int $recursive How many sub-levels of child tags to print. True for all.
|
365
|
|
|
* @param bool|int $content_only Only print text, false will print tags too.
|
366
|
|
|
* @return string
|
367
|
|
|
*/
|
368
|
23 |
|
function toString($attributes = true, $recursive = true, $content_only = false) {
|
|
|
|
|
369
|
23 |
|
if ($content_only) {
|
370
|
23 |
|
if (is_int($content_only)) {
|
371
|
21 |
|
--$content_only;
|
372
|
21 |
|
}
|
373
|
23 |
|
return $this->toString_content($attributes, $recursive, $content_only);
|
|
|
|
|
374
|
|
|
}
|
375
|
|
|
|
376
|
20 |
|
$s = '<'.$this->tag;
|
377
|
20 |
|
if ($attributes) {
|
378
|
20 |
|
$s .= $this->toString_attributes();
|
379
|
20 |
|
}
|
380
|
20 |
|
if ($this->self_close) {
|
381
|
|
|
$s .= $this->self_close_str.'>';
|
382
|
|
|
} else {
|
383
|
20 |
|
$s .= '>';
|
384
|
20 |
|
if($recursive) {
|
385
|
20 |
|
$s .= $this->toString_content($attributes);
|
386
|
20 |
|
}
|
387
|
20 |
|
$s .= '</'.$this->tag.'>';
|
388
|
|
|
}
|
389
|
20 |
|
return $s;
|
390
|
|
|
}
|
391
|
|
|
|
392
|
|
|
/**
|
393
|
|
|
* Similar to JavaScript outerText, will return full (html formatted) node
|
394
|
|
|
* @return string
|
395
|
|
|
*/
|
396
|
|
|
function getOuterText() {
|
|
|
|
|
397
|
|
|
return html_entity_decode($this->toString(), ENT_QUOTES);
|
398
|
|
|
}
|
399
|
|
|
|
400
|
|
|
/**
|
401
|
|
|
* Similar to JavaScript outerText, will replace node (and child nodes) with new text
|
402
|
|
|
* @param string $text
|
403
|
|
|
* @param HtmlParserBase $parser Null to auto create instance
|
404
|
|
|
* @return bool|array True on succeed, array with errors on failure
|
405
|
|
|
*/
|
406
|
|
|
function setOuterText($text, $parser = null) {
|
|
|
|
|
407
|
|
|
if (trim($text)) {
|
408
|
|
|
$index = $this->index();
|
409
|
|
|
if ($parser === null) {
|
410
|
|
|
$parser = new $this->parserClass();
|
411
|
|
|
}
|
412
|
|
|
$parser->setDoc($text);
|
413
|
|
|
$parser->parse_all();
|
414
|
|
|
$parser->root->moveChildren($this->parent, $index);
|
415
|
|
|
}
|
416
|
|
|
$this->delete();
|
417
|
|
|
return (($parser && $parser->errors) ? $parser->errors : true);
|
418
|
|
|
}
|
419
|
|
|
|
420
|
|
|
/**
|
421
|
|
|
* Return html code of node
|
422
|
|
|
* @internal jquery (naming) compatibility
|
423
|
|
|
* @param string|null $value The value to set or null to get the value.
|
424
|
|
|
* @see toString()
|
425
|
|
|
* @return string
|
426
|
|
|
*/
|
427
|
20 |
|
function html($value = null) {
|
|
|
|
|
428
|
20 |
|
if ($value !== null) {
|
429
|
1 |
|
$this->setInnerText($value);
|
430
|
1 |
|
}
|
431
|
20 |
|
return $this->getInnerText();
|
432
|
|
|
}
|
433
|
|
|
|
434
|
|
|
/**
|
435
|
|
|
* Similar to JavaScript innerText, will return (html formatted) content
|
436
|
|
|
* @return string
|
437
|
|
|
*/
|
438
|
21 |
|
function getInnerText() {
|
|
|
|
|
439
|
21 |
|
return html_entity_decode($this->toString(true, true, 1), ENT_QUOTES);
|
440
|
|
|
}
|
441
|
|
|
|
442
|
|
|
/**
|
443
|
|
|
* Similar to JavaScript innerText, will replace child nodes with new text
|
444
|
|
|
* @param string $text
|
445
|
|
|
* @param HtmlParserBase $parser Null to auto create instance
|
446
|
|
|
* @return bool|array True on succeed, array with errors on failure
|
447
|
|
|
*/
|
448
|
2 |
|
function setInnerText($text, $parser = null) {
|
|
|
|
|
449
|
2 |
|
$this->clear();
|
450
|
2 |
|
if (trim($text)) {
|
451
|
2 |
|
if ($parser === null) {
|
452
|
2 |
|
$parser = new $this->parserClass();
|
453
|
2 |
|
}
|
454
|
2 |
|
$parser->root =& $this;
|
455
|
2 |
|
$parser->setDoc($text);
|
456
|
2 |
|
$parser->parse_all();
|
457
|
2 |
|
}
|
458
|
2 |
|
return (($parser && $parser->errors) ? $parser->errors : true);
|
459
|
|
|
}
|
460
|
|
|
|
461
|
|
|
/**
|
462
|
|
|
* Similar to JavaScript plainText, will return text in node (and subnodes)
|
463
|
|
|
* @return string
|
464
|
|
|
*/
|
465
|
2 |
|
function getPlainText() {
|
|
|
|
|
466
|
2 |
|
return preg_replace('`\s+`', ' ', html_entity_decode($this->toString(true, true, true), ENT_QUOTES));
|
467
|
|
|
}
|
468
|
|
|
|
469
|
|
|
/**
|
470
|
|
|
* Return plaintext taking document encoding into account
|
471
|
|
|
* @return string
|
472
|
|
|
*/
|
473
|
|
|
function getPlainTextUTF8() {
|
|
|
|
|
474
|
|
|
$txt = $this->toString(true, true, true);
|
475
|
|
|
$enc = $this->getEncoding();
|
476
|
|
|
if ($enc !== false) {
|
477
|
|
|
$txt = mb_convert_encoding($txt, 'UTF-8', $enc);
|
478
|
|
|
}
|
479
|
|
|
return preg_replace('`\s+`', ' ', html_entity_decode($txt, ENT_QUOTES, 'UTF-8'));
|
480
|
|
|
}
|
481
|
|
|
|
482
|
|
|
/**
|
483
|
|
|
* Similar to JavaScript plainText, will replace child nodes with new text (literal)
|
484
|
|
|
* @param string $text
|
485
|
|
|
*/
|
486
|
1 |
|
function setPlainText($text) {
|
|
|
|
|
487
|
1 |
|
$this->clear();
|
488
|
1 |
|
if (trim($text)) {
|
489
|
1 |
|
$this->addText(htmlentities($text, ENT_QUOTES));
|
490
|
1 |
|
}
|
491
|
1 |
|
}
|
492
|
|
|
|
493
|
|
|
/**
|
494
|
|
|
* Delete node from parent and clear node
|
495
|
|
|
*/
|
496
|
15 |
|
function delete() {
|
|
|
|
|
497
|
15 |
|
if (($p = $this->parent) !== null) {
|
498
|
4 |
|
$this->parent = null;
|
499
|
4 |
|
$p->deleteChild($this);
|
500
|
4 |
|
} else {
|
501
|
15 |
|
$this->clear();
|
502
|
|
|
}
|
503
|
15 |
|
}
|
504
|
|
|
|
505
|
|
|
/**
|
506
|
|
|
* Detach node from parent
|
507
|
|
|
* @param bool $move_children_up Only detach current node and replace it with child nodes
|
508
|
|
|
* @internal jquery (naming) compatibility
|
509
|
|
|
* @see delete()
|
510
|
|
|
*/
|
511
|
1 |
|
function detach($move_children_up = false) {
|
|
|
|
|
512
|
1 |
|
if (($p = $this->parent) !== null) {
|
513
|
1 |
|
$index = $this->index();
|
514
|
1 |
|
$this->parent = null;
|
515
|
|
|
|
516
|
1 |
|
if ($move_children_up) {
|
517
|
1 |
|
$this->moveChildren($p, $index);
|
518
|
1 |
|
}
|
519
|
1 |
|
$p->deleteChild($this, true);
|
520
|
1 |
|
}
|
521
|
1 |
|
}
|
522
|
|
|
|
523
|
|
|
/**
|
524
|
|
|
* Deletes all child nodes from node
|
525
|
|
|
*/
|
526
|
15 |
|
function clear() {
|
|
|
|
|
527
|
15 |
|
foreach($this->children as $c) {
|
528
|
8 |
|
$c->parent = null;
|
529
|
8 |
|
$c->delete();
|
530
|
15 |
|
}
|
531
|
15 |
|
$this->children = array();
|
532
|
15 |
|
}
|
533
|
|
|
|
534
|
|
|
/**
|
535
|
|
|
* Get top parent
|
536
|
|
|
* @return DomNode Root, null if node has no parent
|
537
|
|
|
*/
|
538
|
|
|
function getRoot() {
|
|
|
|
|
539
|
|
|
$r = $this->parent;
|
540
|
|
|
$n = ($r === null) ? null : $r->parent;
|
541
|
|
|
while ($n !== null) {
|
542
|
|
|
$r = $n;
|
543
|
|
|
$n = $r->parent;
|
544
|
|
|
}
|
545
|
|
|
|
546
|
|
|
return $r;
|
547
|
|
|
}
|
548
|
|
|
|
549
|
|
|
/**
|
550
|
|
|
* Change parent
|
551
|
|
|
* @param null|DomNode $to New parent, null if none
|
552
|
|
|
* @param false|int $index Add child to parent if not present at index, false to not add, negative to count from end, null to append
|
553
|
|
|
*/
|
554
|
|
|
#php4
|
555
|
|
|
#function changeParent($to, &$index) {
|
|
|
|
|
556
|
|
|
#php4e
|
557
|
|
|
#php5
|
558
|
10 |
|
function changeParent($to, &$index = null) {
|
|
|
|
|
559
|
|
|
#php5e
|
560
|
10 |
|
if ($this->parent !== null) {
|
561
|
10 |
|
$this->parent->deleteChild($this, true);
|
562
|
10 |
|
}
|
563
|
10 |
|
$this->parent = $to;
|
564
|
10 |
|
if ($index !== false) {
|
565
|
10 |
|
$new_index = $this->index();
|
566
|
10 |
|
if (!(is_int($new_index) && ($new_index >= 0))) {
|
567
|
10 |
|
$this->parent->addChild($this, $index);
|
568
|
10 |
|
}
|
569
|
10 |
|
}
|
570
|
10 |
|
}
|
571
|
|
|
|
572
|
|
|
/**
|
573
|
|
|
* Find out if node has (a certain) parent
|
574
|
|
|
* @param DomNode|string $tag Match against parent, string to match tag, object to fully match node, null to return if node has parent
|
575
|
|
|
* @param bool $recursive
|
576
|
|
|
* @return bool
|
577
|
|
|
*/
|
578
|
|
|
function hasParent($tag = null, $recursive = false) {
|
|
|
|
|
579
|
|
|
if ($this->parent !== null) {
|
580
|
|
|
if ($tag === null) {
|
581
|
|
|
return true;
|
582
|
|
|
} elseif (is_string($tag)) {
|
583
|
|
|
return (($this->parent->tag === $tag) || ($recursive && $this->parent->hasParent($tag)));
|
584
|
|
|
} elseif (is_object($tag)) {
|
585
|
|
|
return (($this->parent === $tag) || ($recursive && $this->parent->hasParent($tag)));
|
586
|
|
|
}
|
587
|
|
|
}
|
588
|
|
|
|
589
|
|
|
return false;
|
590
|
|
|
}
|
591
|
|
|
|
592
|
|
|
/**
|
593
|
|
|
* Find out if node is parent of a certain tag
|
594
|
|
|
* @param DomNode|string $tag Match against parent, string to match tag, object to fully match node
|
595
|
|
|
* @param bool $recursive
|
596
|
|
|
* @return bool
|
597
|
|
|
* @see hasParent()
|
598
|
|
|
*/
|
599
|
|
|
function isParent($tag, $recursive = false) {
|
|
|
|
|
600
|
|
|
return ($this->hasParent($tag, $recursive) === ($tag !== null));
|
601
|
|
|
}
|
602
|
|
|
|
603
|
|
|
/**
|
604
|
|
|
* Find out if node is text
|
605
|
|
|
* @return bool
|
606
|
|
|
*/
|
607
|
|
|
function isText() {
|
|
|
|
|
608
|
|
|
return false;
|
609
|
|
|
}
|
610
|
|
|
|
611
|
|
|
/**
|
612
|
|
|
* Find out if node is comment
|
613
|
|
|
* @return bool
|
614
|
|
|
*/
|
615
|
|
|
function isComment() {
|
|
|
|
|
616
|
|
|
return false;
|
617
|
|
|
}
|
618
|
|
|
|
619
|
|
|
/**
|
620
|
|
|
* Find out if node is text or comment node
|
621
|
|
|
* @return bool
|
622
|
|
|
*/
|
623
|
|
|
function isTextOrComment() {
|
|
|
|
|
624
|
|
|
return false;
|
625
|
|
|
}
|
626
|
|
|
|
627
|
|
|
/**
|
628
|
|
|
* Move node to other node
|
629
|
|
|
* @param DomNode $to New parent, null if none
|
630
|
|
|
* @param int $new_index Add child to parent at index if not present, null to not add, negative to count from end
|
631
|
|
|
* @internal Performs {@link changeParent()}
|
632
|
|
|
*/
|
633
|
|
|
#php4
|
634
|
|
|
#function move($to, &$new_index) {
|
|
|
|
|
635
|
|
|
#php4e
|
636
|
|
|
#php5
|
637
|
|
|
function move($to, &$new_index = -1) {
|
|
|
|
|
638
|
|
|
#php5e
|
639
|
|
|
$this->changeParent($to, $new_index);
|
640
|
|
|
}
|
641
|
|
|
|
642
|
|
|
/**
|
643
|
|
|
* Move child nodes to other node
|
644
|
|
|
* @param DomNode $to New parent, null if none
|
645
|
|
|
* @param int $new_index Add child to new node at index if not present, null to not add, negative to count from end
|
646
|
|
|
* @param int $start Index from child node where to start wrapping, 0 for first element
|
647
|
|
|
* @param int $end Index from child node where to end wrapping, -1 for last element
|
648
|
|
|
*/
|
649
|
|
|
#php4
|
650
|
|
|
#function moveChildren($to, &$new_index, $start = 0, $end = -1) {
|
|
|
|
|
651
|
|
|
#php4e
|
652
|
|
|
#php5
|
653
|
2 |
|
function moveChildren($to, &$new_index = -1, $start = 0, $end = -1) {
|
|
|
|
|
654
|
|
|
#php5e
|
655
|
2 |
|
if ($end < 0) {
|
656
|
1 |
|
$end += count($this->children);
|
657
|
1 |
|
}
|
658
|
2 |
|
for ($i = $start; $i <= $end; $i++) {
|
659
|
2 |
|
$this->children[$start]->changeParent($to, $new_index);
|
660
|
2 |
|
}
|
661
|
2 |
|
}
|
662
|
|
|
|
663
|
|
|
/**
|
664
|
|
|
* Index of node in parent
|
665
|
|
|
* @param bool $count_all True to count all tags, false to ignore text and comments
|
666
|
|
|
* @return int -1 if not found
|
667
|
|
|
*/
|
668
|
10 |
|
function index($count_all = true) {
|
|
|
|
|
669
|
10 |
|
if (!$this->parent) {
|
670
|
|
|
return -1;
|
671
|
10 |
|
} elseif ($count_all) {
|
672
|
10 |
|
return $this->parent->findChild($this);
|
673
|
|
|
} else{
|
674
|
|
|
$index = -1;
|
675
|
|
|
//foreach($this->parent->children as &$c) {
|
|
|
|
|
676
|
|
|
// if (!$c->isTextOrComment()) {
|
|
|
|
|
677
|
|
|
// ++$index;
|
678
|
|
|
// }
|
679
|
|
|
// if ($c === $this) {
|
|
|
|
|
680
|
|
|
// return $index;
|
681
|
|
|
// }
|
682
|
|
|
//}
|
683
|
|
|
|
684
|
|
|
foreach(array_keys($this->parent->children) as $k) {
|
685
|
|
|
if (!$this->parent->children[$k]->isTextOrComment()) {
|
686
|
|
|
++$index;
|
687
|
|
|
}
|
688
|
|
|
if ($this->parent->children[$k] === $this) {
|
689
|
|
|
return $index;
|
690
|
|
|
}
|
691
|
|
|
}
|
692
|
|
|
return -1;
|
693
|
|
|
}
|
694
|
|
|
}
|
695
|
|
|
|
696
|
|
|
/**
|
697
|
|
|
* Change index of node in parent
|
698
|
|
|
* @param int $index New index
|
699
|
|
|
*/
|
700
|
|
|
function setIndex($index) {
|
|
|
|
|
701
|
|
|
if ($this->parent) {
|
702
|
|
|
if ($index > $this->index()) {
|
703
|
|
|
--$index;
|
704
|
|
|
}
|
705
|
|
|
$this->delete();
|
706
|
|
|
$this->parent->addChild($this, $index);
|
707
|
|
|
}
|
708
|
|
|
}
|
709
|
|
|
|
710
|
|
|
/**
|
711
|
|
|
* Index of all similar nodes in parent
|
712
|
|
|
* @return int -1 if not found
|
713
|
|
|
*/
|
714
|
|
|
function typeIndex() {
|
|
|
|
|
715
|
|
|
if (!$this->parent) {
|
716
|
|
|
return -1;
|
717
|
|
|
} else {
|
718
|
|
|
$index = -1;
|
719
|
|
|
//foreach($this->parent->children as &$c) {
|
|
|
|
|
720
|
|
|
// if (strcasecmp($this->tag, $c->tag) === 0) {
|
|
|
|
|
721
|
|
|
// ++$index;
|
722
|
|
|
// }
|
723
|
|
|
// if ($c === $this) {
|
|
|
|
|
724
|
|
|
// return $index;
|
725
|
|
|
// }
|
726
|
|
|
//}
|
727
|
|
|
|
728
|
|
|
foreach(array_keys($this->parent->children) as $k) {
|
729
|
|
|
if (strcasecmp($this->tag, $this->parent->children[$k]->tag) === 0) {
|
730
|
|
|
++$index;
|
731
|
|
|
}
|
732
|
|
|
if ($this->parent->children[$k] === $this) {
|
733
|
|
|
return $index;
|
734
|
|
|
}
|
735
|
|
|
}
|
736
|
|
|
return -1;
|
737
|
|
|
}
|
738
|
|
|
}
|
739
|
|
|
|
740
|
|
|
/**
|
741
|
|
|
* Calculate indent of node (number of parent tags - 1)
|
742
|
|
|
* @return int
|
743
|
|
|
*/
|
744
|
|
|
function indent() {
|
|
|
|
|
745
|
|
|
return (($this->parent) ? $this->parent->indent() + 1 : -1);
|
746
|
|
|
}
|
747
|
|
|
|
748
|
|
|
/**
|
749
|
|
|
* Get sibling node
|
750
|
|
|
* @param int $offset Offset from current node
|
751
|
|
|
* @return DomNode Null if not found
|
752
|
|
|
*/
|
753
|
|
|
function getSibling($offset = 1) {
|
|
|
|
|
754
|
|
|
$index = $this->index() + $offset;
|
755
|
|
|
if (($index >= 0) && ($index < $this->parent->childCount())) {
|
756
|
|
|
return $this->parent->getChild($index);
|
757
|
|
|
} else {
|
758
|
|
|
return null;
|
759
|
|
|
}
|
760
|
|
|
}
|
761
|
|
|
|
762
|
|
|
/**
|
763
|
|
|
* Get node next to current
|
764
|
|
|
* @param bool $skip_text_comments
|
765
|
|
|
* @return DomNode Null if not found
|
766
|
|
|
* @see getSibling()
|
767
|
|
|
* @see getPreviousSibling()
|
768
|
|
|
*/
|
769
|
|
|
function getNextSibling($skip_text_comments = true) {
|
|
|
|
|
770
|
|
|
$offset = 1;
|
771
|
|
|
while (($n = $this->getSibling($offset)) !== null) {
|
772
|
|
|
if ($skip_text_comments && ($n->tag[0] === '~')) {
|
773
|
|
|
++$offset;
|
774
|
|
|
} else {
|
775
|
|
|
break;
|
776
|
|
|
}
|
777
|
|
|
}
|
778
|
|
|
|
779
|
|
|
return $n;
|
780
|
|
|
}
|
781
|
|
|
|
782
|
|
|
/**
|
783
|
|
|
* Get node previous to current
|
784
|
|
|
* @param bool $skip_text_comments
|
785
|
|
|
* @return DomNode Null if not found
|
786
|
|
|
* @see getSibling()
|
787
|
|
|
* @see getNextSibling()
|
788
|
|
|
*/
|
789
|
|
|
function getPreviousSibling($skip_text_comments = true) {
|
|
|
|
|
790
|
|
|
$offset = -1;
|
791
|
|
|
while (($n = $this->getSibling($offset)) !== null) {
|
792
|
|
|
if ($skip_text_comments && ($n->tag[0] === '~')) {
|
793
|
|
|
--$offset;
|
794
|
|
|
} else {
|
795
|
|
|
break;
|
796
|
|
|
}
|
797
|
|
|
}
|
798
|
|
|
|
799
|
|
|
return $n;
|
800
|
|
|
}
|
801
|
|
|
|
802
|
|
|
/**
|
803
|
|
|
* Get namespace of node
|
804
|
|
|
* @return string
|
805
|
|
|
* @see setNamespace()
|
806
|
|
|
*/
|
807
|
2 |
|
function getNamespace() {
|
|
|
|
|
808
|
2 |
|
if ($this->tag_ns === null) {
|
809
|
2 |
|
$a = explode(':', $this->tag, 2);
|
810
|
2 |
|
if (empty($a[1])) {
|
811
|
2 |
|
$this->tag_ns = array('', $a[0]);
|
812
|
2 |
|
} else {
|
813
|
|
|
$this->tag_ns = array($a[0], $a[1]);
|
814
|
|
|
}
|
815
|
2 |
|
}
|
816
|
|
|
|
817
|
2 |
|
return $this->tag_ns[0];
|
818
|
|
|
}
|
819
|
|
|
|
820
|
|
|
/**
|
821
|
|
|
* Set namespace of node
|
822
|
|
|
* @param string $ns
|
823
|
|
|
* @see getNamespace()
|
824
|
|
|
*/
|
825
|
|
|
function setNamespace($ns) {
|
|
|
|
|
826
|
|
|
if ($this->getNamespace() !== $ns) {
|
827
|
|
|
$this->tag_ns[0] = $ns;
|
828
|
|
|
$this->tag = $ns.':'.$this->tag_ns[1];
|
829
|
|
|
}
|
830
|
|
|
}
|
831
|
|
|
|
832
|
|
|
/**
|
833
|
|
|
* Get tagname of node (without namespace)
|
834
|
|
|
* @return string
|
835
|
|
|
* @see setTag()
|
836
|
|
|
*/
|
837
|
2 |
|
function getTag() {
|
|
|
|
|
838
|
2 |
|
if ($this->tag_ns === null) {
|
839
|
2 |
|
$this->getNamespace();
|
840
|
2 |
|
}
|
841
|
|
|
|
842
|
2 |
|
return $this->tag_ns[1];
|
843
|
|
|
}
|
844
|
|
|
|
845
|
|
|
/**
|
846
|
|
|
* Set tag (with or without namespace)
|
847
|
|
|
* @param string $tag
|
848
|
|
|
* @param bool $with_ns Does $tag include namespace?
|
849
|
|
|
* @see getTag()
|
850
|
|
|
*/
|
851
|
2 |
|
function setTag($tag, $with_ns = false) {
|
|
|
|
|
852
|
2 |
|
$with_ns = $with_ns || (strpos($tag, ':') !== false);
|
853
|
2 |
|
if ($with_ns) {
|
854
|
|
|
$this->tag = $tag;
|
855
|
|
|
$this->tag_ns = null;
|
|
|
|
|
856
|
2 |
|
} elseif ($this->getTag() !== $tag) {
|
857
|
2 |
|
$this->tag_ns[1] = $tag;
|
858
|
2 |
|
$this->tag = (($this->tag_ns[0]) ? $this->tag_ns[0].':' : '').$tag;
|
859
|
2 |
|
}
|
860
|
2 |
|
}
|
861
|
|
|
|
862
|
|
|
/**
|
863
|
|
|
* Try to determine the encoding of the current tag
|
864
|
|
|
* @return string|bool False if encoding could not be found
|
865
|
|
|
*/
|
866
|
|
|
function getEncoding() {
|
|
|
|
|
867
|
|
|
$root = $this->getRoot();
|
868
|
|
|
if ($root !== null) {
|
869
|
|
|
if ($enc = $root->select('meta[charset]', 0, true, true)) {
|
870
|
|
|
return $enc->getAttribute("charset");
|
871
|
|
|
} elseif ($enc = $root->select('"?xml"[encoding]', 0, true, true)) {
|
872
|
|
|
return $enc->getAttribute("encoding");
|
|
|
|
|
873
|
|
|
} elseif ($enc = $root->select('meta[content*="charset="]', 0, true, true)) {
|
874
|
|
|
$enc = $enc->getAttribute("content");
|
|
|
|
|
875
|
|
|
return substr($enc, strpos($enc, "charset=")+8);
|
876
|
|
|
}
|
877
|
|
|
}
|
878
|
|
|
|
879
|
|
|
return false;
|
880
|
|
|
}
|
881
|
|
|
|
882
|
|
|
/**
|
883
|
|
|
* Number of children in node
|
884
|
|
|
* @param bool $ignore_text_comments Ignore text/comments with calculation
|
885
|
|
|
* @return int
|
886
|
|
|
*/
|
887
|
37 |
|
function childCount($ignore_text_comments = false) {
|
|
|
|
|
888
|
37 |
|
if (!$ignore_text_comments) {
|
889
|
37 |
|
return count($this->children);
|
890
|
|
|
} else{
|
891
|
|
|
$count = 0;
|
892
|
|
|
//foreach($this->children as &$c) {
|
|
|
|
|
893
|
|
|
// if (!$c->isTextOrComment()) {
|
|
|
|
|
894
|
|
|
// ++$count;
|
895
|
|
|
// }
|
896
|
|
|
//}
|
897
|
|
|
|
898
|
|
|
foreach(array_keys($this->children) as $k) {
|
899
|
|
|
if (!$this->children[$k]->isTextOrComment()) {
|
900
|
|
|
++$count;
|
901
|
|
|
}
|
902
|
|
|
}
|
903
|
|
|
return $count;
|
904
|
|
|
}
|
905
|
|
|
}
|
906
|
|
|
|
907
|
|
|
/**
|
908
|
|
|
* Find node in children
|
909
|
|
|
* @param DomNode $child
|
910
|
|
|
* @return int False if not found
|
911
|
|
|
*/
|
912
|
12 |
|
function findChild($child) {
|
|
|
|
|
913
|
12 |
|
return array_search($child, $this->children, true);
|
914
|
|
|
}
|
915
|
|
|
|
916
|
|
|
/**
|
917
|
|
|
* Checks if node has another node as child
|
918
|
|
|
* @param DomNode $child
|
919
|
|
|
* @return bool
|
920
|
|
|
*/
|
921
|
|
|
function hasChild($child) {
|
|
|
|
|
922
|
|
|
return ((bool) findChild($child));
|
923
|
|
|
}
|
924
|
|
|
|
925
|
|
|
/**
|
926
|
|
|
* Get childnode
|
927
|
|
|
* @param int|DomNode $child Index, negative to count from end
|
928
|
|
|
* @param bool $ignore_text_comments Ignore text/comments with index calculation
|
929
|
|
|
* @return DomNode
|
930
|
|
|
*/
|
931
|
|
|
function &getChild($child, $ignore_text_comments = false) {
|
932
|
|
|
if (!is_int($child)) {
|
933
|
|
|
$child = $this->findChild($child);
|
934
|
|
|
} elseif ($child < 0) {
|
935
|
|
|
$child += $this->childCount($ignore_text_comments);
|
936
|
|
|
}
|
937
|
|
|
|
938
|
|
|
if ($ignore_text_comments) {
|
939
|
|
|
$count = 0;
|
940
|
|
|
$last = null;
|
941
|
|
|
//foreach($this->children as &$c) {
|
|
|
|
|
942
|
|
|
// if (!$c->isTextOrComment()) {
|
|
|
|
|
943
|
|
|
// if ($count++ === $child) {
|
|
|
|
|
944
|
|
|
// return $c;
|
945
|
|
|
// }
|
946
|
|
|
// $last = $c;
|
|
|
|
|
947
|
|
|
// }
|
948
|
|
|
//}
|
949
|
|
|
|
950
|
|
|
foreach(array_keys($this->children) as $k) {
|
951
|
|
|
if (!$this->children[$k]->isTextOrComment()) {
|
952
|
|
|
if ($count++ === $child) {
|
953
|
|
|
return $this->children[$k];
|
954
|
|
|
}
|
955
|
|
|
$last = $this->children[$k];
|
956
|
|
|
}
|
957
|
|
|
}
|
958
|
|
|
return (($child > $count) ? $last : null);
|
959
|
|
|
} else {
|
960
|
|
|
return $this->children[$child];
|
961
|
|
|
}
|
962
|
|
|
}
|
963
|
|
|
|
964
|
|
|
/**
|
965
|
|
|
* Add child node
|
966
|
|
|
* @param string|DomNode $tag Tag name or object
|
967
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
968
|
|
|
* @return DomNode Added node
|
969
|
|
|
*/
|
970
|
|
|
#php4
|
971
|
|
|
#function &addChild($tag, &$offset) {
|
|
|
|
|
972
|
|
|
#php4e
|
973
|
|
|
#php5
|
974
|
37 |
|
function &addChild($tag, &$offset = null) {
|
975
|
|
|
#php5e
|
976
|
37 |
|
if (is_array($tag)) {
|
977
|
37 |
|
$tag = new $this->childClass($tag, $this);
|
978
|
37 |
|
} elseif (is_string($tag)) {
|
979
|
3 |
|
$nodes = $this->createNodes($tag);
|
980
|
3 |
|
$tag = array_shift($nodes);
|
981
|
|
|
|
982
|
3 |
|
if ($tag && $tag->parent !== $this) {
|
983
|
2 |
|
$index = false;
|
984
|
2 |
|
$tag->changeParent($this, $index);
|
985
|
2 |
|
}
|
986
|
37 |
|
} elseif (is_object($tag) && $tag->parent !== $this) {
|
987
|
|
|
$index = false; //Needs to be passed by ref
|
988
|
|
|
$tag->changeParent($this, $index);
|
989
|
|
|
}
|
990
|
|
|
|
991
|
37 |
|
if (is_int($offset) && ($offset < count($this->children)) && ($offset !== -1)) {
|
992
|
8 |
|
if ($offset < 0) {
|
993
|
|
|
$offset += count($this->children);
|
994
|
|
|
}
|
995
|
8 |
|
array_splice($this->children, $offset++, 0, array(&$tag));
|
996
|
8 |
|
} else {
|
997
|
37 |
|
$this->children[] =& $tag;
|
998
|
|
|
}
|
999
|
|
|
|
1000
|
37 |
|
return $tag;
|
1001
|
|
|
}
|
1002
|
|
|
|
1003
|
|
|
/**
|
1004
|
|
|
* First child node
|
1005
|
|
|
* @param bool $ignore_text_comments Ignore text/comments with index calculation
|
1006
|
|
|
* @return DomNode
|
1007
|
|
|
*/
|
1008
|
|
|
function &firstChild($ignore_text_comments = false) {
|
1009
|
|
|
return $this->getChild(0, $ignore_text_comments);
|
1010
|
|
|
}
|
1011
|
|
|
|
1012
|
|
|
/**
|
1013
|
|
|
* Last child node
|
1014
|
|
|
* @param bool $ignore_text_comments Ignore text/comments with index calculation
|
1015
|
|
|
* @return DomNode
|
1016
|
|
|
*/
|
1017
|
|
|
function &lastChild($ignore_text_comments = false) {
|
1018
|
|
|
return $this->getChild(-1, $ignore_text_comments);
|
1019
|
|
|
}
|
1020
|
|
|
|
1021
|
|
|
/**
|
1022
|
|
|
* Insert childnode
|
1023
|
|
|
* @param string|DomNode $tag Tagname or object
|
1024
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
|
|
|
|
1025
|
|
|
* @return DomNode Added node
|
1026
|
|
|
* @see addChild();
|
1027
|
|
|
*/
|
1028
|
|
|
function &insertChild($tag, $index) {
|
1029
|
|
|
return $this->addChild($tag, $index);
|
1030
|
|
|
}
|
1031
|
|
|
|
1032
|
|
|
/**
|
1033
|
|
|
* Add text node
|
1034
|
|
|
* @param string $text
|
1035
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1036
|
|
|
* @return DomNode Added node
|
1037
|
|
|
* @see addChild();
|
1038
|
|
|
*/
|
1039
|
|
|
#php4
|
1040
|
|
|
#function &addText($text, &$offset) {
|
|
|
|
|
1041
|
|
|
#php4e
|
1042
|
|
|
#php5
|
1043
|
37 |
|
function &addText($text, &$offset = null) {
|
1044
|
|
|
#php5e
|
1045
|
37 |
|
return $this->addChild(new $this->childClass_Text($this, $text), $offset);
|
1046
|
|
|
}
|
1047
|
|
|
|
1048
|
|
|
/**
|
1049
|
|
|
* Add comment node
|
1050
|
|
|
* @param string $text
|
1051
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1052
|
|
|
* @return DomNode Added node
|
1053
|
|
|
* @see addChild();
|
1054
|
|
|
*/
|
1055
|
|
|
#php4
|
1056
|
|
|
#function &addComment($text, &$offset) {
|
|
|
|
|
1057
|
|
|
#php4e
|
1058
|
|
|
#php5
|
1059
|
9 |
|
function &addComment($text, &$offset = null) {
|
1060
|
|
|
#php5e
|
1061
|
9 |
|
return $this->addChild(new $this->childClass_Comment($this, $text), $offset);
|
1062
|
|
|
}
|
1063
|
|
|
|
1064
|
|
|
/**
|
1065
|
|
|
* Add conditional node
|
1066
|
|
|
* @param string $condition
|
1067
|
|
|
* @param bool True for <!--[if, false for <![if
|
1068
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1069
|
|
|
* @return DomNode Added node
|
1070
|
|
|
* @see addChild();
|
1071
|
|
|
*/
|
1072
|
|
|
#php4
|
1073
|
|
|
#function &addConditional($condition, $hidden = true, &$offset) {
|
|
|
|
|
1074
|
|
|
#php4e
|
1075
|
|
|
#php5
|
1076
|
|
|
function &addConditional($condition, $hidden = true, &$offset = null) {
|
1077
|
|
|
#php5e
|
1078
|
|
|
return $this->addChild(new $this->childClass_Conditional($this, $condition, $hidden), $offset);
|
1079
|
|
|
}
|
1080
|
|
|
|
1081
|
|
|
/**
|
1082
|
|
|
* Add CDATA node
|
1083
|
|
|
* @param string $text
|
1084
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1085
|
|
|
* @return DomNode Added node
|
1086
|
|
|
* @see addChild();
|
1087
|
|
|
*/
|
1088
|
|
|
#php4
|
1089
|
|
|
#function &addCDATA($text, &$offset) {
|
|
|
|
|
1090
|
|
|
#php4e
|
1091
|
|
|
#php5
|
1092
|
|
|
function &addCDATA($text, &$offset = null) {
|
1093
|
|
|
#php5e
|
1094
|
|
|
return $this->addChild(new $this->childClass_CDATA($this, $text), $offset);
|
1095
|
|
|
}
|
1096
|
|
|
|
1097
|
|
|
/**
|
1098
|
|
|
* Add doctype node
|
1099
|
|
|
* @param string $dtd
|
1100
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1101
|
|
|
* @return DomNode Added node
|
1102
|
|
|
* @see addChild();
|
1103
|
|
|
*/
|
1104
|
|
|
#php4
|
1105
|
|
|
#function &addDoctype($dtd, &$offset) {
|
|
|
|
|
1106
|
|
|
#php4e
|
1107
|
|
|
#php5
|
1108
|
9 |
|
function &addDoctype($dtd, &$offset = null) {
|
1109
|
|
|
#php5e
|
1110
|
9 |
|
return $this->addChild(new $this->childClass_Doctype($this, $dtd), $offset);
|
1111
|
|
|
}
|
1112
|
|
|
|
1113
|
|
|
/**
|
1114
|
|
|
* Add xml node
|
1115
|
|
|
* @param string $tag Tag name after "?", e.g. "php" or "xml"
|
1116
|
|
|
* @param string $text
|
1117
|
|
|
* @param array $attributes Array of attributes (array('attribute' => 'value'))
|
1118
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1119
|
|
|
* @return DomNode Added node
|
1120
|
|
|
* @see addChild();
|
1121
|
|
|
*/
|
1122
|
|
|
#php4
|
1123
|
|
|
#function &addXML($tag = 'xml', $text = '', $attributes = array(), &$offset) {
|
|
|
|
|
1124
|
|
|
#php4e
|
1125
|
|
|
#php5
|
1126
|
|
|
function &addXML($tag = 'xml', $text = '', $attributes = array(), &$offset = null) {
|
1127
|
|
|
#php5e
|
1128
|
|
|
return $this->addChild(new $this->childClass_XML($this, $tag, $text, $attributes), $offset);
|
1129
|
|
|
}
|
1130
|
|
|
|
1131
|
|
|
/**
|
1132
|
|
|
* Add ASP node
|
1133
|
|
|
* @param string $tag Tag name after "%"
|
1134
|
|
|
* @param string $text
|
1135
|
|
|
* @param array $attributes Array of attributes (array('attribute' => 'value'))
|
1136
|
|
|
* @param int $offset Position to insert node, negative to count from end, null to append
|
1137
|
|
|
* @return DomNode Added node
|
1138
|
|
|
* @see addChild();
|
1139
|
|
|
*/
|
1140
|
|
|
#php4
|
1141
|
|
|
#function &addASP($tag = '', $text = '', $attributes = array(), &$offset) {
|
|
|
|
|
1142
|
|
|
#php4e
|
1143
|
|
|
#php5
|
1144
|
|
|
function &addASP($tag = '', $text = '', $attributes = array(), &$offset = null) {
|
1145
|
|
|
#php5e
|
1146
|
|
|
return $this->addChild(new $this->childClass_ASP($this, $tag, $text, $attributes), $offset);
|
1147
|
|
|
}
|
1148
|
|
|
|
1149
|
|
|
/**
|
1150
|
|
|
* Delete a child node
|
1151
|
|
|
* @param int|DomNode $child Child(index) to delete, negative to count from end
|
1152
|
|
|
* @param bool $soft_delete False to call {@link delete()} from child
|
1153
|
|
|
*/
|
1154
|
12 |
|
function deleteChild($child, $soft_delete = false) {
|
|
|
|
|
1155
|
12 |
|
if (is_object($child)) {
|
1156
|
12 |
|
$child = $this->findChild($child);
|
1157
|
12 |
|
} elseif ($child < 0) {
|
1158
|
|
|
$child += count($this->children);
|
1159
|
|
|
}
|
1160
|
|
|
|
1161
|
12 |
|
if (!$soft_delete) {
|
1162
|
4 |
|
$this->children[$child]->delete();
|
1163
|
4 |
|
}
|
1164
|
12 |
|
unset($this->children[$child]);
|
1165
|
|
|
|
1166
|
|
|
//Rebuild indices
|
1167
|
12 |
|
$tmp = array();
|
1168
|
|
|
|
1169
|
|
|
//foreach($this->children as &$c) {
|
|
|
|
|
1170
|
|
|
// $tmp[] =& $c;
|
|
|
|
|
1171
|
|
|
//}
|
1172
|
12 |
|
foreach(array_keys($this->children) as $k) {
|
1173
|
12 |
|
$tmp[] =& $this->children[$k];
|
1174
|
12 |
|
}
|
1175
|
12 |
|
$this->children = $tmp;
|
1176
|
12 |
|
}
|
1177
|
|
|
|
1178
|
|
|
/**
|
1179
|
|
|
* Wrap node
|
1180
|
|
|
* @param string|DomNode $node Wrapping node, string to create new element node
|
1181
|
|
|
* @param int $wrap_index Index to insert current node in wrapping node, -1 to append
|
1182
|
|
|
* @param int $node_index Index to insert wrapping node, null to keep at same position
|
1183
|
|
|
* @return DomNode Wrapping node
|
1184
|
|
|
*/
|
1185
|
2 |
|
function wrap($node, $wrap_index = -1, $node_index = null) {
|
|
|
|
|
1186
|
2 |
|
if ($node_index === null) {
|
1187
|
2 |
|
$node_index = $this->index();
|
1188
|
2 |
|
}
|
1189
|
|
|
|
1190
|
2 |
|
if (!is_object($node)) {
|
1191
|
2 |
|
$node = $this->parent->addChild($node, $node_index);
|
1192
|
2 |
|
} elseif ($node->parent !== $this->parent) {
|
1193
|
|
|
$node->changeParent($this->parent, $node_index);
|
1194
|
|
|
}
|
1195
|
|
|
|
1196
|
2 |
|
$this->changeParent($node, $wrap_index);
|
1197
|
2 |
|
return $node;
|
1198
|
|
|
}
|
1199
|
|
|
|
1200
|
|
|
/**
|
1201
|
|
|
* Wrap child nodes
|
1202
|
|
|
* @param string|DomNode $node Wrapping node, string to create new element node
|
1203
|
|
|
* @param int $start Index from child node where to start wrapping, 0 for first element
|
1204
|
|
|
* @param int $end Index from child node where to end wrapping, -1 for last element
|
1205
|
|
|
* @param int $wrap_index Index to insert in wrapping node, -1 to append
|
1206
|
|
|
* @param int $node_index Index to insert current node, null to keep at same position
|
1207
|
|
|
* @return DomNode Wrapping node
|
1208
|
|
|
*/
|
1209
|
1 |
|
function wrapInner($node, $start = 0, $end = -1, $wrap_index = -1, $node_index = null) {
|
|
|
|
|
1210
|
1 |
|
if ($end < 0) {
|
1211
|
1 |
|
$end += count($this->children);
|
1212
|
1 |
|
}
|
1213
|
1 |
|
if ($node_index === null) {
|
1214
|
1 |
|
$node_index = $end + 1;
|
1215
|
1 |
|
}
|
1216
|
|
|
|
1217
|
1 |
|
if (!is_object($node)) {
|
1218
|
1 |
|
$node = $this->addChild($node, $node_index);
|
1219
|
1 |
|
} elseif ($node->parent !== $this) {
|
1220
|
|
|
$node->changeParent($this->parent, $node_index);
|
1221
|
|
|
}
|
1222
|
|
|
|
1223
|
1 |
|
$this->moveChildren($node, $wrap_index, $start, $end);
|
1224
|
1 |
|
return $node;
|
1225
|
|
|
}
|
1226
|
|
|
|
1227
|
|
|
/**
|
1228
|
|
|
* Number of attributes
|
1229
|
|
|
* @return int
|
1230
|
|
|
*/
|
1231
|
|
|
function attributeCount() {
|
|
|
|
|
1232
|
|
|
return count($this->attributes);
|
1233
|
|
|
}
|
1234
|
|
|
|
1235
|
|
|
/**
|
1236
|
|
|
* Find attribute using namespace, name or both
|
1237
|
|
|
* @param string|int $attr Negative int to count from end
|
1238
|
|
|
* @param string $compare "namespace", "name" or "total"
|
1239
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1240
|
|
|
* @return array array('ns', 'attr', 'ns:attr', index)
|
1241
|
|
|
* @access private
|
1242
|
|
|
*/
|
1243
|
27 |
|
protected function findAttribute($attr, $compare = 'total', $case_sensitive = false) {
|
1244
|
27 |
|
if (is_int($attr)) {
|
1245
|
|
|
if ($attr < 0) {
|
1246
|
|
|
$attr += count($this->attributes);
|
1247
|
|
|
}
|
1248
|
|
|
$keys = array_keys($this->attributes);
|
1249
|
|
|
return $this->findAttribute($keys[$attr], 'total', true);
|
1250
|
27 |
|
} else if ($compare === 'total') {
|
1251
|
27 |
|
$b = explode(':', $attr, 2);
|
1252
|
27 |
|
if ($case_sensitive) {
|
1253
|
|
|
$t =& $this->attributes;
|
1254
|
|
|
} else {
|
1255
|
27 |
|
$t = array_change_key_case($this->attributes);
|
1256
|
27 |
|
$attr = strtolower($attr);
|
1257
|
|
|
}
|
1258
|
|
|
|
1259
|
27 |
|
if (isset($t[$attr])) {
|
1260
|
26 |
|
$index = 0;
|
1261
|
26 |
|
foreach($this->attributes as $a => $v) {
|
1262
|
26 |
|
if (($v === $t[$attr]) && (strcasecmp($a, $attr) === 0)) {
|
1263
|
26 |
|
$attr = $a;
|
1264
|
26 |
|
$b = explode(':', $attr, 2);
|
1265
|
26 |
|
break;
|
1266
|
|
|
}
|
1267
|
6 |
|
++$index;
|
1268
|
26 |
|
}
|
1269
|
|
|
|
1270
|
26 |
|
if (empty($b[1])) {
|
1271
|
26 |
|
return array(array('', $b[0], $attr, $index));
|
1272
|
|
|
} else {
|
1273
|
|
|
return array(array($b[0], $b[1], $attr, $index));
|
1274
|
|
|
}
|
1275
|
|
|
} else {
|
1276
|
22 |
|
return false;
|
|
|
|
|
1277
|
|
|
}
|
1278
|
|
|
} else {
|
1279
|
|
|
if ($this->attributes_ns === null) {
|
1280
|
|
|
$index = 0;
|
1281
|
|
|
foreach($this->attributes as $a => $v) {
|
1282
|
|
|
$b = explode(':', $a, 2);
|
1283
|
|
|
if (empty($b[1])) {
|
1284
|
|
|
$this->attributes_ns[$b[0]][] = array('', $b[0], $a, $index);
|
1285
|
|
|
} else {
|
1286
|
|
|
$this->attributes_ns[$b[1]][] = array($b[0], $b[1], $a, $index);
|
1287
|
|
|
}
|
1288
|
|
|
++$index;
|
1289
|
|
|
}
|
1290
|
|
|
}
|
1291
|
|
|
|
1292
|
|
|
if ($case_sensitive) {
|
1293
|
|
|
$t =& $this->attributes_ns;
|
1294
|
|
|
} else {
|
1295
|
|
|
$t = array_change_key_case($this->attributes_ns);
|
1296
|
|
|
$attr = strtolower($attr);
|
1297
|
|
|
}
|
1298
|
|
|
|
1299
|
|
|
if ($compare === 'namespace') {
|
1300
|
|
|
$res = array();
|
1301
|
|
|
foreach($t as $ar) {
|
1302
|
|
|
foreach($ar as $a) {
|
1303
|
|
|
if ($a[0] === $attr) {
|
1304
|
|
|
$res[] = $a;
|
1305
|
|
|
}
|
1306
|
|
|
}
|
1307
|
|
|
}
|
1308
|
|
|
return $res;
|
1309
|
|
|
} elseif ($compare === 'name') {
|
1310
|
|
|
return ((isset($t[$attr])) ? $t[$attr] : false);
|
1311
|
|
|
} else {
|
1312
|
|
|
trigger_error('Unknown comparison mode');
|
1313
|
|
|
}
|
1314
|
|
|
}
|
1315
|
|
|
}
|
1316
|
|
|
|
1317
|
|
|
/**
|
1318
|
|
|
* Checks if node has attribute
|
1319
|
|
|
* @param string|int$attr Negative int to count from end
|
1320
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1321
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1322
|
|
|
* @return bool
|
1323
|
|
|
*/
|
1324
|
|
|
function hasAttribute($attr, $compare = 'total', $case_sensitive = false) {
|
|
|
|
|
1325
|
|
|
return ((bool) $this->findAttribute($attr, $compare, $case_sensitive));
|
1326
|
|
|
}
|
1327
|
|
|
|
1328
|
|
|
/**
|
1329
|
|
|
* Gets namespace of attribute(s)
|
1330
|
|
|
* @param string|int $attr Negative int to count from end
|
1331
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1332
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1333
|
|
|
* @return string|array False if not found
|
1334
|
|
|
*/
|
1335
|
|
|
function getAttributeNS($attr, $compare = 'name', $case_sensitive = false) {
|
|
|
|
|
1336
|
|
|
$f = $this->findAttribute($attr, $compare, $case_sensitive);
|
1337
|
|
|
if (is_array($f) && $f) {
|
|
|
|
|
1338
|
|
|
if (count($f) === 1) {
|
1339
|
|
|
return $this->attributes[$f[0][0]];
|
1340
|
|
|
} else {
|
1341
|
|
|
$res = array();
|
1342
|
|
|
foreach($f as $a) {
|
1343
|
|
|
$res[] = $a[0];
|
1344
|
|
|
}
|
1345
|
|
|
return $res;
|
1346
|
|
|
}
|
1347
|
|
|
} else {
|
1348
|
|
|
return false;
|
|
|
|
|
1349
|
|
|
}
|
1350
|
|
|
}
|
1351
|
|
|
|
1352
|
|
|
/**
|
1353
|
|
|
* Sets namespace of attribute(s)
|
1354
|
|
|
* @param string|int $attr Negative int to count from end
|
1355
|
|
|
* @param string $namespace
|
1356
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1357
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1358
|
|
|
* @return bool
|
1359
|
|
|
*/
|
1360
|
|
|
function setAttributeNS($attr, $namespace, $compare = 'name', $case_sensitive = false) {
|
|
|
|
|
1361
|
|
|
$f = $this->findAttribute($attr, $compare, $case_sensitive);
|
1362
|
|
|
if (is_array($f) && $f) {
|
|
|
|
|
1363
|
|
|
if ($namespace) {
|
1364
|
|
|
$namespace .= ':';
|
1365
|
|
|
}
|
1366
|
|
|
foreach($f as $a) {
|
1367
|
|
|
$val = $this->attributes[$a[2]];
|
1368
|
|
|
unset($this->attributes[$a[2]]);
|
1369
|
|
|
$this->attributes[$namespace.$a[1]] = $val;
|
1370
|
|
|
}
|
1371
|
|
|
$this->attributes_ns = null;
|
|
|
|
|
1372
|
|
|
return true;
|
1373
|
|
|
} else {
|
1374
|
|
|
return false;
|
1375
|
|
|
}
|
1376
|
|
|
}
|
1377
|
|
|
|
1378
|
|
|
/**
|
1379
|
|
|
* Gets value(s) of attribute(s)
|
1380
|
|
|
* @param string|int $attr Negative int to count from end
|
1381
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1382
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1383
|
|
|
* @return string|array
|
1384
|
|
|
*/
|
1385
|
12 |
|
function getAttribute($attr, $compare = 'total', $case_sensitive = false) {
|
|
|
|
|
1386
|
12 |
|
$f = $this->findAttribute($attr, $compare, $case_sensitive);
|
1387
|
12 |
|
if (is_array($f) && $f){
|
|
|
|
|
1388
|
12 |
|
if (count($f) === 1) {
|
1389
|
12 |
|
return $this->attributes[$f[0][2]];
|
1390
|
|
|
} else {
|
1391
|
|
|
$res = array();
|
1392
|
|
|
foreach($f as $a) {
|
1393
|
|
|
$res[] = $this->attributes[$a[2]];
|
1394
|
|
|
}
|
1395
|
|
|
return $res;
|
1396
|
|
|
}
|
1397
|
|
|
} else {
|
1398
|
8 |
|
return null;
|
1399
|
|
|
}
|
1400
|
|
|
}
|
1401
|
|
|
|
1402
|
|
|
/**
|
1403
|
|
|
* Sets value(s) of attribute(s)
|
1404
|
|
|
* @param string|int $attr Negative int to count from end
|
1405
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1406
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1407
|
|
|
*/
|
1408
|
9 |
|
function setAttribute($attr, $val, $compare = 'total', $case_sensitive = false) {
|
|
|
|
|
1409
|
9 |
|
if ($val === null) {
|
1410
|
|
|
return $this->deleteAttribute($attr, $compare, $case_sensitive);
|
1411
|
|
|
}
|
1412
|
|
|
|
1413
|
9 |
|
$f = $this->findAttribute($attr, $compare, $case_sensitive);
|
1414
|
9 |
|
if (is_array($f) && $f) {
|
|
|
|
|
1415
|
6 |
|
foreach($f as $a) {
|
1416
|
6 |
|
$this->attributes[$a[2]] = (string) $val;
|
1417
|
6 |
|
}
|
1418
|
6 |
|
} else {
|
1419
|
6 |
|
$this->attributes[$attr] = (string) $val;
|
1420
|
|
|
}
|
1421
|
9 |
|
}
|
1422
|
|
|
|
1423
|
|
|
/**
|
1424
|
|
|
* Add new attribute
|
1425
|
|
|
* @param string $attr
|
1426
|
|
|
* @param string $val
|
1427
|
|
|
*/
|
1428
|
|
|
function addAttribute($attr, $val) {
|
|
|
|
|
1429
|
|
|
$this->setAttribute($attr, $val, 'total', true);
|
1430
|
|
|
}
|
1431
|
|
|
|
1432
|
|
|
/**
|
1433
|
|
|
* Delete attribute(s)
|
1434
|
|
|
* @param string|int $attr Negative int to count from end
|
1435
|
|
|
* @param string $compare Find node using "namespace", "name" or "total"
|
1436
|
|
|
* @param bool $case_sensitive Compare with case sensitivity
|
1437
|
|
|
*/
|
1438
|
5 |
|
function deleteAttribute($attr, $compare = 'total', $case_sensitive = false) {
|
|
|
|
|
1439
|
5 |
|
$f = $this->findAttribute($attr, $compare, $case_sensitive);
|
1440
|
5 |
|
if (is_array($f) && $f) {
|
|
|
|
|
1441
|
5 |
|
foreach($f as $a) {
|
1442
|
5 |
|
unset($this->attributes[$a[2]]);
|
1443
|
5 |
|
if ($this->attributes_ns !== null) {
|
1444
|
|
|
unset($this->attributes_ns[$a[1]]);
|
1445
|
|
|
}
|
1446
|
5 |
|
}
|
1447
|
5 |
|
}
|
1448
|
5 |
|
}
|
1449
|
|
|
|
1450
|
|
|
/**
|
1451
|
|
|
* Determine if node has a certain class
|
1452
|
|
|
* @param string $className
|
1453
|
|
|
* @return bool
|
1454
|
|
|
*/
|
1455
|
2 |
|
function hasClass($className) {
|
|
|
|
|
1456
|
2 |
|
return ($className && preg_match('`\b'.preg_quote($className).'\b`si', $this->class));
|
|
|
|
|
1457
|
|
|
}
|
1458
|
|
|
|
1459
|
|
|
/**
|
1460
|
|
|
* Add new class(es)
|
1461
|
|
|
* @param string|array $className
|
1462
|
|
|
*/
|
1463
|
4 |
|
function addClass($className) {
|
|
|
|
|
1464
|
4 |
|
if (!is_array($className)) {
|
1465
|
4 |
|
$className = array($className);
|
1466
|
4 |
|
}
|
1467
|
4 |
|
$class = $this->class;
|
|
|
|
|
1468
|
4 |
|
foreach ($className as $c) {
|
1469
|
4 |
|
if (!(preg_match('`\b'.preg_quote($c).'\b`si', $class) > 0)) {
|
1470
|
4 |
|
$class .= ' '.$c;
|
1471
|
4 |
|
}
|
1472
|
4 |
|
}
|
1473
|
4 |
|
$this->class = trim($class);
|
|
|
|
|
1474
|
4 |
|
}
|
1475
|
|
|
|
1476
|
|
|
/**
|
1477
|
|
|
* Remove clas(ses)
|
1478
|
|
|
* @param string|array $className
|
1479
|
|
|
*/
|
1480
|
2 |
|
function removeClass($className) {
|
|
|
|
|
1481
|
2 |
|
if (!is_array($className)) {
|
1482
|
2 |
|
$className = array($className);
|
1483
|
2 |
|
}
|
1484
|
2 |
|
$class = $this->class;
|
|
|
|
|
1485
|
2 |
|
foreach ($className as $c) {
|
1486
|
2 |
|
$class = preg_replace('`\b'.preg_quote($c).'\b`si', '', $class);
|
1487
|
2 |
|
}
|
1488
|
2 |
|
if ($class) {
|
1489
|
2 |
|
$this->class = $class;
|
|
|
|
|
1490
|
2 |
|
} else {
|
1491
|
1 |
|
unset($this->class);
|
1492
|
|
|
}
|
1493
|
2 |
|
}
|
1494
|
|
|
|
1495
|
|
|
/**
|
1496
|
|
|
* Finds children using a callback function
|
1497
|
|
|
* @param callable $callback Function($node) that returns a bool
|
1498
|
|
|
* @param bool|int $recursive Check recursively
|
1499
|
|
|
* @param bool $check_self Include this node in search?
|
1500
|
|
|
* @return array
|
1501
|
|
|
*/
|
1502
|
|
|
function getChildrenByCallback($callback, $recursive = true, $check_self = false) {
|
|
|
|
|
1503
|
|
|
$count = $this->childCount();
|
1504
|
|
|
if ($check_self && $callback($this)) {
|
1505
|
|
|
$res = array($this);
|
1506
|
|
|
} else {
|
1507
|
|
|
$res = array();
|
1508
|
|
|
}
|
1509
|
|
|
|
1510
|
|
|
if ($count > 0) {
|
1511
|
|
|
if (is_int($recursive)) {
|
1512
|
|
|
$recursive = (($recursive > 1) ? $recursive - 1 : false);
|
1513
|
|
|
}
|
1514
|
|
|
|
1515
|
|
|
for ($i = 0; $i < $count; $i++) {
|
1516
|
|
|
if ($callback($this->children[$i])) {
|
1517
|
|
|
$res[] = $this->children[$i];
|
1518
|
|
|
}
|
1519
|
|
|
if ($recursive) {
|
1520
|
|
|
$res = array_merge($res, $this->children[$i]->getChildrenByCallback($callback, $recursive));
|
1521
|
|
|
}
|
1522
|
|
|
}
|
1523
|
|
|
}
|
1524
|
|
|
|
1525
|
|
|
return $res;
|
1526
|
|
|
}
|
1527
|
|
|
|
1528
|
|
|
/**
|
1529
|
|
|
* Finds children using the {$link match()} function
|
1530
|
|
|
* @param $conditions See {$link match()}
|
1531
|
|
|
* @param $custom_filters See {$link match()}
|
1532
|
|
|
* @param bool|int $recursive Check recursively
|
1533
|
|
|
* @param bool $check_self Include this node in search?
|
1534
|
|
|
* @return array
|
1535
|
|
|
*/
|
1536
|
37 |
|
function getChildrenByMatch($conditions, $recursive = true, $check_self = false, $custom_filters = array()) {
|
|
|
|
|
1537
|
37 |
|
$count = $this->childCount();
|
1538
|
37 |
|
if ($check_self && $this->match($conditions, true, $custom_filters)) {
|
1539
|
|
|
$res = array($this);
|
1540
|
|
|
} else {
|
1541
|
37 |
|
$res = array();
|
1542
|
|
|
}
|
1543
|
|
|
|
1544
|
37 |
|
if ($count > 0) {
|
1545
|
37 |
|
if (is_int($recursive)) {
|
1546
|
|
|
$recursive = (($recursive > 1) ? $recursive - 1 : false);
|
1547
|
|
|
}
|
1548
|
|
|
|
1549
|
37 |
|
for ($i = 0; $i < $count; $i++) {
|
1550
|
37 |
|
if ($this->children[$i]->match($conditions, true, $custom_filters)) {
|
1551
|
37 |
|
$res[] = $this->children[$i];
|
1552
|
37 |
|
}
|
1553
|
37 |
|
if ($recursive) {
|
1554
|
37 |
|
$res = array_merge($res, $this->children[$i]->getChildrenByMatch($conditions, $recursive, false, $custom_filters));
|
1555
|
37 |
|
}
|
1556
|
37 |
|
}
|
1557
|
37 |
|
}
|
1558
|
|
|
|
1559
|
37 |
|
return $res;
|
1560
|
|
|
}
|
1561
|
|
|
|
1562
|
|
|
/**
|
1563
|
|
|
* Checks if tag matches certain conditions
|
1564
|
|
|
* @param array $tags array('tag1', 'tag2') or array(array(
|
1565
|
|
|
* 'tag' => 'tag1',
|
1566
|
|
|
* 'operator' => 'or'/'and',
|
1567
|
|
|
* 'compare' => 'total'/'namespace'/'name',
|
1568
|
|
|
* 'case_sensitive' => true))
|
1569
|
|
|
* @return bool
|
1570
|
|
|
* @internal Used by selector class
|
1571
|
|
|
* @see match()
|
1572
|
|
|
* @access private
|
1573
|
|
|
*/
|
1574
|
23 |
|
protected function match_tags($tags) {
|
1575
|
23 |
|
$res = false;
|
1576
|
|
|
|
1577
|
23 |
|
foreach($tags as $tag => $match) {
|
1578
|
23 |
|
if (!is_array($match)) {
|
1579
|
|
|
$match = array(
|
1580
|
|
|
'match' => $match,
|
1581
|
|
|
'operator' => 'or',
|
1582
|
|
|
'compare' => 'total',
|
1583
|
|
|
'case_sensitive' => false
|
1584
|
|
|
);
|
1585
|
|
|
} else {
|
1586
|
23 |
|
if (is_int($tag)) {
|
1587
|
22 |
|
$tag = $match['tag'];
|
1588
|
22 |
|
}
|
1589
|
23 |
|
if (!isset($match['match'])) {
|
1590
|
|
|
$match['match'] = true;
|
1591
|
|
|
}
|
1592
|
23 |
|
if (!isset($match['operator'])) {
|
1593
|
23 |
|
$match['operator'] = 'or';
|
1594
|
23 |
|
}
|
1595
|
23 |
|
if (!isset($match['compare'])) {
|
1596
|
23 |
|
$match['compare'] = 'total';
|
1597
|
23 |
|
}
|
1598
|
23 |
|
if (!isset($match['case_sensitive'])) {
|
1599
|
23 |
|
$match['case_sensitive'] = false;
|
1600
|
23 |
|
}
|
1601
|
|
|
}
|
1602
|
|
|
|
1603
|
23 |
|
if (($match['operator'] === 'and') && (!$res)) {
|
1604
|
|
|
return false;
|
1605
|
23 |
|
} elseif (!($res && ($match['operator'] === 'or'))) {
|
1606
|
23 |
|
if ($match['compare'] === 'total') {
|
1607
|
23 |
|
$a = $this->tag;
|
1608
|
23 |
|
} elseif ($match['compare'] === 'namespace') {
|
1609
|
|
|
$a = $this->getNamespace();
|
1610
|
|
|
} elseif ($match['compare'] === 'name') {
|
1611
|
|
|
$a = $this->getTag();
|
1612
|
|
|
}
|
1613
|
|
|
|
1614
|
23 |
|
if ($match['case_sensitive']) {
|
1615
|
|
|
$res = (($a === $tag) === $match['match']);
|
|
|
|
|
1616
|
|
|
} else {
|
1617
|
23 |
|
$res = ((strcasecmp($a, $tag) === 0) === $match['match']);
|
1618
|
|
|
}
|
1619
|
23 |
|
}
|
1620
|
23 |
|
}
|
1621
|
|
|
|
1622
|
23 |
|
return $res;
|
1623
|
|
|
}
|
1624
|
|
|
|
1625
|
|
|
/**
|
1626
|
|
|
* Checks if attributes match certain conditions
|
1627
|
|
|
* @param array $attributes array('attr' => 'val') or array(array(
|
1628
|
|
|
* 'operator_value' => 'equals'/'='/'contains_regex'/etc
|
1629
|
|
|
* 'attribute' => 'attr',
|
1630
|
|
|
* 'value' => 'val',
|
1631
|
|
|
* 'match' => true,
|
1632
|
|
|
* 'operator_result' => 'or'/'and',
|
1633
|
|
|
* 'compare' => 'total'/'namespace'/'name',
|
1634
|
|
|
* 'case_sensitive' => true))
|
1635
|
|
|
* @return bool
|
1636
|
|
|
* @internal Used by selector class
|
1637
|
|
|
* @see match()
|
1638
|
|
|
* @access private
|
1639
|
|
|
*/
|
1640
|
19 |
|
protected function match_attributes($attributes) {
|
1641
|
19 |
|
$res = false;
|
1642
|
|
|
|
1643
|
19 |
|
foreach($attributes as $attribute => $match) {
|
1644
|
19 |
|
if (!is_array($match)) {
|
1645
|
|
|
$match = array(
|
1646
|
|
|
'operator_value' => 'equals',
|
1647
|
|
|
'value' => $match,
|
1648
|
|
|
'match' => true,
|
1649
|
|
|
'operator_result' => 'or',
|
1650
|
|
|
'compare' => 'total',
|
1651
|
|
|
'case_sensitive' => false
|
1652
|
|
|
);
|
1653
|
|
|
} else {
|
1654
|
19 |
|
if (is_int($attribute)) {
|
1655
|
19 |
|
$attribute = $match['attribute'];
|
1656
|
19 |
|
}
|
1657
|
19 |
|
if (!isset($match['match'])) {
|
1658
|
13 |
|
$match['match'] = true;
|
1659
|
13 |
|
}
|
1660
|
19 |
|
if (!isset($match['operator_result'])) {
|
1661
|
|
|
$match['operator_result'] = 'or';
|
1662
|
|
|
}
|
1663
|
19 |
|
if (!isset($match['compare'])) {
|
1664
|
13 |
|
$match['compare'] = 'total';
|
1665
|
13 |
|
}
|
1666
|
19 |
|
if (!isset($match['case_sensitive'])) {
|
1667
|
19 |
|
$match['case_sensitive'] = false;
|
1668
|
19 |
|
}
|
1669
|
|
|
}
|
1670
|
|
|
|
1671
|
19 |
|
if (is_string($match['value']) && (!$match['case_sensitive'])) {
|
1672
|
19 |
|
$match['value'] = strtolower($match['value']);
|
1673
|
19 |
|
}
|
1674
|
|
|
|
1675
|
19 |
|
if (($match['operator_result'] === 'and') && (!$res)) {
|
1676
|
|
|
return false;
|
1677
|
19 |
|
} elseif (!($res && ($match['operator_result'] === 'or'))) {
|
1678
|
19 |
|
$possibles = $this->findAttribute($attribute, $match['compare'], $match['case_sensitive']);
|
1679
|
|
|
|
1680
|
19 |
|
$has = (is_array($possibles) && $possibles);
|
|
|
|
|
1681
|
19 |
|
$res = (($match['value'] === $has) || (($match['match'] === false) && ($has === $match['match'])));
|
1682
|
|
|
|
1683
|
19 |
|
if ((!$res) && $has && is_string($match['value'])) {
|
1684
|
19 |
|
foreach($possibles as $a) {
|
1685
|
19 |
|
$val = $this->attributes[$a[2]];
|
1686
|
19 |
|
if (is_string($val) && (!$match['case_sensitive'])) {
|
1687
|
19 |
|
$val = strtolower($val);
|
1688
|
19 |
|
}
|
1689
|
|
|
|
1690
|
19 |
|
switch($match['operator_value']) {
|
1691
|
19 |
|
case '%=':
|
1692
|
19 |
|
case 'contains_regex':
|
|
|
|
|
1693
|
|
|
$res = ((preg_match('`'.$match['value'].'`s', $val) > 0) === $match['match']);
|
1694
|
|
|
if ($res) break 1; else break 2;
|
1695
|
|
|
|
1696
|
19 |
|
case '|=':
|
1697
|
19 |
|
case 'contains_prefix':
|
|
|
|
|
1698
|
1 |
|
$res = ((preg_match('`\b'.preg_quote($match['value']).'[\-\s]`s', $val) > 0) === $match['match']);
|
1699
|
1 |
|
if ($res) break 1; else break 2;
|
1700
|
|
|
|
1701
|
18 |
|
case '~=':
|
1702
|
18 |
|
case 'contains_word':
|
|
|
|
|
1703
|
12 |
|
$res = ((preg_match('`\s'.preg_quote($match['value']).'\s`s', " $val ") > 0) === $match['match']);
|
1704
|
12 |
|
if ($res) break 1; else break 2;
|
1705
|
|
|
|
1706
|
6 |
|
case '*=':
|
1707
|
6 |
|
case 'contains':
|
|
|
|
|
1708
|
|
|
$res = ((strpos($val, $match['value']) !== false) === $match['match']);
|
1709
|
|
|
if ($res) break 1; else break 2;
|
1710
|
|
|
|
1711
|
6 |
|
case '$=':
|
1712
|
6 |
|
case 'ends_with':
|
|
|
|
|
1713
|
|
|
$res = ((substr($val, -strlen($match['value'])) === $match['value']) === $match['match']);
|
1714
|
|
|
if ($res) break 1; else break 2;
|
1715
|
|
|
|
1716
|
6 |
|
case '^=':
|
1717
|
6 |
|
case 'starts_with':
|
|
|
|
|
1718
|
|
|
$res = ((substr($val, 0, strlen($match['value'])) === $match['value']) === $match['match']);
|
1719
|
|
|
if ($res) break 1; else break 2;
|
1720
|
|
|
|
1721
|
6 |
|
case '!=':
|
1722
|
6 |
|
case 'not_equal':
|
|
|
|
|
1723
|
|
|
$res = (($val !== $match['value']) === $match['match']);
|
1724
|
|
|
if ($res) break 1; else break 2;
|
1725
|
|
|
|
1726
|
6 |
|
case '=':
|
1727
|
6 |
|
case 'equals':
|
|
|
|
|
1728
|
6 |
|
$res = (($val === $match['value']) === $match['match']);
|
1729
|
6 |
|
if ($res) break 1; else break 2;
|
1730
|
|
|
|
1731
|
|
|
case '>=':
|
1732
|
|
|
case 'bigger_than':
|
|
|
|
|
1733
|
|
|
$res = (($val >= $match['value']) === $match['match']);
|
1734
|
|
|
if ($res) break 1; else break 2;
|
1735
|
|
|
|
1736
|
|
|
case '<=':
|
1737
|
|
|
case 'smaller_than':
|
|
|
|
|
1738
|
|
|
$res = (($val >= $match['value']) === $match['match']);
|
1739
|
|
|
if ($res) break 1; else break 2;
|
1740
|
|
|
|
1741
|
|
|
default:
|
1742
|
|
|
trigger_error('Unknown operator "'.$match['operator_value'].'" to match attributes!');
|
1743
|
|
|
return false;
|
1744
|
19 |
|
}
|
1745
|
19 |
|
}
|
1746
|
19 |
|
}
|
1747
|
19 |
|
}
|
1748
|
19 |
|
}
|
1749
|
|
|
|
1750
|
19 |
|
return $res;
|
1751
|
|
|
}
|
1752
|
|
|
|
1753
|
|
|
/**
|
1754
|
|
|
* Checks if node matches certain filters
|
1755
|
|
|
* @param array $tags array(array(
|
|
|
|
|
1756
|
|
|
* 'filter' => 'last-child',
|
1757
|
|
|
* 'params' => '123'))
|
1758
|
|
|
* @param array $custom_filters Custom map next to {@link $filter_map}
|
1759
|
|
|
* @return bool
|
1760
|
|
|
* @internal Used by selector class
|
1761
|
|
|
* @see match()
|
1762
|
|
|
* @access private
|
1763
|
|
|
*/
|
1764
|
3 |
|
protected function match_filters($conditions, $custom_filters = array()) {
|
1765
|
3 |
|
foreach($conditions as $c) {
|
1766
|
3 |
|
$c['filter'] = strtolower($c['filter']);
|
1767
|
3 |
|
if (isset($this->filter_map[$c['filter']])) {
|
1768
|
3 |
|
if (!$this->{$this->filter_map[$c['filter']]}($c['params'])) {
|
1769
|
3 |
|
return false;
|
1770
|
|
|
}
|
1771
|
3 |
|
} elseif (isset($custom_filters[$c['filter']])) {
|
1772
|
|
|
if (!call_user_func($custom_filters[$c['filter']], $this, $c['params'])) {
|
1773
|
|
|
return false;
|
1774
|
|
|
}
|
1775
|
|
|
} else {
|
1776
|
|
|
trigger_error('Unknown filter "'.$c['filter'].'"!');
|
1777
|
|
|
return false;
|
1778
|
|
|
}
|
1779
|
3 |
|
}
|
1780
|
|
|
|
1781
|
3 |
|
return true;
|
1782
|
|
|
}
|
1783
|
|
|
|
1784
|
|
|
/**
|
1785
|
|
|
* Checks if node matches certain conditions
|
1786
|
|
|
* @param array $tags array('tags' => array(tag_conditions), 'attributes' => array(attr_conditions), 'filters' => array(filter_conditions))
|
|
|
|
|
1787
|
|
|
* @param array $match Should conditions evaluate to true?
|
1788
|
|
|
* @param array $custom_filters Custom map next to {@link $filter_map}
|
1789
|
|
|
* @return bool
|
1790
|
|
|
* @internal Used by selector class
|
1791
|
|
|
* @see match_tags();
|
1792
|
|
|
* @see match_attributes();
|
1793
|
|
|
* @see match_filters();
|
1794
|
|
|
* @access private
|
1795
|
|
|
*/
|
1796
|
37 |
|
function match($conditions, $match = true, $custom_filters = array()) {
|
|
|
|
|
1797
|
37 |
|
$t = isset($conditions['tags']);
|
1798
|
37 |
|
$a = isset($conditions['attributes']);
|
1799
|
37 |
|
$f = isset($conditions['filters']);
|
1800
|
|
|
|
1801
|
37 |
|
if (!($t || $a || $f)) {
|
1802
|
37 |
|
if (is_array($conditions) && $conditions) {
|
|
|
|
|
1803
|
37 |
|
foreach($conditions as $c) {
|
1804
|
37 |
|
if ($this->match($c, $match)) {
|
1805
|
37 |
|
return true;
|
1806
|
|
|
}
|
1807
|
36 |
|
}
|
1808
|
36 |
|
}
|
1809
|
|
|
|
1810
|
36 |
|
return false;
|
1811
|
|
|
} else {
|
1812
|
37 |
|
if (($t && (!$this->match_tags($conditions['tags']))) === $match) {
|
1813
|
22 |
|
return false;
|
1814
|
|
|
}
|
1815
|
|
|
|
1816
|
37 |
|
if (($a && (!$this->match_attributes($conditions['attributes']))) === $match) {
|
1817
|
19 |
|
return false;
|
1818
|
|
|
}
|
1819
|
|
|
|
1820
|
37 |
|
if (($f && (!$this->match_filters($conditions['filters'], $custom_filters))) === $match) {
|
1821
|
3 |
|
return false;
|
1822
|
|
|
}
|
1823
|
|
|
|
1824
|
37 |
|
return true;
|
1825
|
|
|
}
|
1826
|
|
|
}
|
1827
|
|
|
|
1828
|
|
|
/**
|
1829
|
|
|
* Finds children that match a certain attribute
|
1830
|
|
|
* @param string $attribute
|
1831
|
|
|
* @param string $value
|
1832
|
|
|
* @param string $mode Compare mode, "equals", "|=", "contains_regex", etc.
|
1833
|
|
|
* @param string $compare "total"/"namespace"/"name"
|
1834
|
|
|
* @param bool|int $recursive
|
1835
|
|
|
* @return array
|
1836
|
|
|
*/
|
1837
|
|
|
function getChildrenByAttribute($attribute, $value, $mode = 'equals', $compare = 'total', $recursive = true) {
|
|
|
|
|
1838
|
|
|
if ($this->childCount() < 1) {
|
1839
|
|
|
return array();
|
1840
|
|
|
}
|
1841
|
|
|
|
1842
|
|
|
$mode = explode(' ', strtolower($mode));
|
1843
|
|
|
$match = ((isset($mode[1]) && ($mode[1] === 'not')) ? 'false' : 'true');
|
1844
|
|
|
|
1845
|
|
|
return $this->getChildrenByMatch(
|
1846
|
|
|
array(
|
1847
|
|
|
'attributes' => array(
|
1848
|
|
|
$attribute => array(
|
1849
|
|
|
'operator_value' => $mode[0],
|
1850
|
|
|
'value' => $value,
|
1851
|
|
|
'match' => $match,
|
1852
|
|
|
'compare' => $compare
|
1853
|
|
|
)
|
1854
|
|
|
)
|
1855
|
|
|
),
|
1856
|
|
|
$recursive
|
1857
|
|
|
);
|
1858
|
|
|
}
|
1859
|
|
|
|
1860
|
|
|
/**
|
1861
|
|
|
* Finds children that match a certain tag
|
1862
|
|
|
* @param string $tag
|
1863
|
|
|
* @param string $compare "total"/"namespace"/"name"
|
1864
|
|
|
* @param bool|int $recursive
|
1865
|
|
|
* @return array
|
1866
|
|
|
*/
|
1867
|
|
|
function getChildrenByTag($tag, $compare = 'total', $recursive = true) {
|
|
|
|
|
1868
|
|
|
if ($this->childCount() < 1) {
|
1869
|
|
|
return array();
|
1870
|
|
|
}
|
1871
|
|
|
|
1872
|
|
|
$tag = explode(' ', strtolower($tag));
|
1873
|
|
|
$match = ((isset($tag[1]) && ($tag[1] === 'not')) ? 'false' : 'true');
|
1874
|
|
|
|
1875
|
|
|
return $this->getChildrenByMatch(
|
1876
|
|
|
array(
|
1877
|
|
|
'tags' => array(
|
1878
|
|
|
$tag[0] => array(
|
1879
|
|
|
'match' => $match,
|
1880
|
|
|
'compare' => $compare
|
1881
|
|
|
)
|
1882
|
|
|
)
|
1883
|
|
|
),
|
1884
|
|
|
$recursive
|
1885
|
|
|
);
|
1886
|
|
|
}
|
1887
|
|
|
|
1888
|
|
|
/**
|
1889
|
|
|
* Finds all children using ID attribute
|
1890
|
|
|
* @param string $id
|
1891
|
|
|
* @param bool|int $recursive
|
1892
|
|
|
* @return array
|
1893
|
|
|
*/
|
1894
|
|
|
function getChildrenByID($id, $recursive = true) {
|
|
|
|
|
1895
|
|
|
return $this->getChildrenByAttribute('id', $id, 'equals', 'total', $recursive);
|
1896
|
|
|
}
|
1897
|
|
|
|
1898
|
|
|
/**
|
1899
|
|
|
* Finds all children using class attribute
|
1900
|
|
|
* @param string $class
|
1901
|
|
|
* @param bool|int $recursive
|
1902
|
|
|
* @return array
|
1903
|
|
|
*/
|
1904
|
|
|
function getChildrenByClass($class, $recursive = true) {
|
|
|
|
|
1905
|
|
|
return $this->getChildrenByAttribute('class', $class, 'equals', 'total', $recursive);
|
1906
|
|
|
}
|
1907
|
|
|
|
1908
|
|
|
/**
|
1909
|
|
|
* Finds all children using name attribute
|
1910
|
|
|
* @param string $name
|
1911
|
|
|
* @param bool|int $recursive
|
1912
|
|
|
* @return array
|
1913
|
|
|
*/
|
1914
|
|
|
function getChildrenByName($name, $recursive = true) {
|
|
|
|
|
1915
|
|
|
return $this->getChildrenByAttribute('name', $name, 'equals', 'total', $recursive);
|
1916
|
|
|
}
|
1917
|
|
|
|
1918
|
|
|
/**
|
1919
|
|
|
* Performs a css query on the node.
|
1920
|
|
|
* @param string $query
|
1921
|
|
|
* @return IQuery Returns the matching nodes from the query.
|
1922
|
|
|
*/
|
1923
|
36 |
|
public function query($query = '*') {
|
1924
|
36 |
|
$select = $this->select($query);
|
1925
|
36 |
|
$result = new \pQuery((array)$select);
|
1926
|
36 |
|
return $result;
|
1927
|
|
|
}
|
1928
|
|
|
|
1929
|
|
|
/**
|
1930
|
|
|
* Performs css query on node
|
1931
|
|
|
* @param string $query
|
1932
|
|
|
* @param int|bool $index True to return node instead of array if only 1 match,
|
1933
|
|
|
* false to return array, int to return match at index, negative int to count from end
|
1934
|
|
|
* @param bool|int $recursive
|
1935
|
|
|
* @param bool $check_self Include this node in search or only search child nodes
|
1936
|
|
|
* @return DomNode[]|DomNode Returns an array of matching {@link DomNode} objects
|
1937
|
|
|
* or a single {@link DomNode} if `$index` is not false.
|
1938
|
|
|
*/
|
1939
|
37 |
|
function select($query = '*', $index = false, $recursive = true, $check_self = false) {
|
|
|
|
|
1940
|
37 |
|
$s = new $this->selectClass($this, $query, $check_self, $recursive);
|
1941
|
37 |
|
$res = $s->result;
|
1942
|
37 |
|
unset($s);
|
1943
|
37 |
|
if (is_array($res) && ($index === true) && (count($res) === 1)) {
|
1944
|
|
|
return $res[0];
|
1945
|
37 |
|
} elseif (is_int($index) && is_array($res)) {
|
1946
|
|
|
if ($index < 0) {
|
1947
|
|
|
$index += count($res);
|
1948
|
|
|
}
|
1949
|
|
|
return ($index < count($res)) ? $res[$index] : null;
|
1950
|
|
|
} else {
|
1951
|
37 |
|
return $res;
|
1952
|
|
|
}
|
1953
|
|
|
}
|
1954
|
|
|
|
1955
|
|
|
/**
|
1956
|
|
|
* Checks if node matches css query filter ":root"
|
1957
|
|
|
* @return bool
|
1958
|
|
|
* @see match()
|
1959
|
|
|
* @access private
|
1960
|
|
|
*/
|
1961
|
|
|
protected function filter_root() {
|
1962
|
|
|
return (strtolower($this->tag) === 'html');
|
1963
|
|
|
}
|
1964
|
|
|
|
1965
|
|
|
/**
|
1966
|
|
|
* Checks if node matches css query filter ":nth-child(n)"
|
1967
|
|
|
* @param string $n 1-based index
|
1968
|
|
|
* @return bool
|
1969
|
|
|
* @see match()
|
1970
|
|
|
* @access private
|
1971
|
|
|
*/
|
1972
|
|
|
protected function filter_nchild($n) {
|
1973
|
|
|
return ($this->index(false)+1 === (int) $n);
|
1974
|
|
|
}
|
1975
|
|
|
|
1976
|
|
|
/**
|
1977
|
|
|
* Checks if node matches css query filter ":gt(n)"
|
1978
|
|
|
* @param string $n 0-based index
|
1979
|
|
|
* @return bool
|
1980
|
|
|
* @see match()
|
1981
|
|
|
* @access private
|
1982
|
|
|
*/
|
1983
|
|
|
protected function filter_gt($n) {
|
1984
|
|
|
return ($this->index(false) > (int) $n);
|
1985
|
|
|
}
|
1986
|
|
|
|
1987
|
|
|
/**
|
1988
|
|
|
* Checks if node matches css query filter ":lt(n)"
|
1989
|
|
|
* @param string $n 0-based index
|
1990
|
|
|
* @return bool
|
1991
|
|
|
* @see match()
|
1992
|
|
|
* @access private
|
1993
|
|
|
*/
|
1994
|
|
|
protected function filter_lt($n) {
|
1995
|
|
|
return ($this->index(false) < (int) $n);
|
1996
|
|
|
}
|
1997
|
|
|
|
1998
|
|
|
/**
|
1999
|
|
|
* Checks if node matches css query filter ":nth-last-child(n)"
|
2000
|
|
|
* @param string $n 1-based index
|
2001
|
|
|
* @return bool
|
2002
|
|
|
* @see match()
|
2003
|
|
|
* @access private
|
2004
|
|
|
*/
|
2005
|
|
|
protected function filter_nlastchild($n) {
|
2006
|
|
|
if ($this->parent === null) {
|
2007
|
|
|
return false;
|
2008
|
|
|
} else {
|
2009
|
|
|
return ($this->parent->childCount(true) - $this->index(false) === (int) $n);
|
2010
|
|
|
}
|
2011
|
|
|
}
|
2012
|
|
|
|
2013
|
|
|
/**
|
2014
|
|
|
* Checks if node matches css query filter ":nth-of-type(n)"
|
2015
|
|
|
* @param string $n 1-based index
|
2016
|
|
|
* @return bool
|
2017
|
|
|
* @see match()
|
2018
|
|
|
* @access private
|
2019
|
|
|
*/
|
2020
|
|
|
protected function filter_ntype($n) {
|
2021
|
|
|
return ($this->typeIndex()+1 === (int) $n);
|
2022
|
|
|
}
|
2023
|
|
|
|
2024
|
|
|
/**
|
2025
|
|
|
* Checks if node matches css query filter ":nth-last-of-type(n)"
|
2026
|
|
|
* @param string $n 1-based index
|
2027
|
|
|
* @return bool
|
2028
|
|
|
* @see match()
|
2029
|
|
|
* @access private
|
2030
|
|
|
*/
|
2031
|
|
|
protected function filter_nlastype($n) {
|
2032
|
|
|
if ($this->parent === null) {
|
2033
|
|
|
return false;
|
2034
|
|
|
} else {
|
2035
|
|
|
return (count($this->parent->getChildrenByTag($this->tag, 'total', false)) - $this->typeIndex() === (int) $n);
|
2036
|
|
|
}
|
2037
|
|
|
}
|
2038
|
|
|
|
2039
|
|
|
/**
|
2040
|
|
|
* Checks if node matches css query filter ":odd"
|
2041
|
|
|
* @return bool
|
2042
|
|
|
* @see match()
|
2043
|
|
|
* @access private
|
2044
|
|
|
*/
|
2045
|
|
|
protected function filter_odd() {
|
2046
|
|
|
return (($this->index(false) & 1) === 1);
|
2047
|
|
|
}
|
2048
|
|
|
|
2049
|
|
|
/**
|
2050
|
|
|
* Checks if node matches css query filter ":even"
|
2051
|
|
|
* @return bool
|
2052
|
|
|
* @see match()
|
2053
|
|
|
* @access private
|
2054
|
|
|
*/
|
2055
|
|
|
protected function filter_even() {
|
2056
|
|
|
return (($this->index(false) & 1) === 0);
|
2057
|
|
|
}
|
2058
|
|
|
|
2059
|
|
|
/**
|
2060
|
|
|
* Checks if node matches css query filter ":every(n)"
|
2061
|
|
|
* @return bool
|
2062
|
|
|
* @see match()
|
2063
|
|
|
* @access private
|
2064
|
|
|
*/
|
2065
|
|
|
protected function filter_every($n) {
|
2066
|
|
|
return (($this->index(false) % (int) $n) === 0);
|
2067
|
|
|
}
|
2068
|
|
|
|
2069
|
|
|
/**
|
2070
|
|
|
* Checks if node matches css query filter ":first"
|
2071
|
|
|
* @return bool
|
2072
|
|
|
* @see match()
|
2073
|
|
|
* @access private
|
2074
|
|
|
*/
|
2075
|
|
|
protected function filter_first() {
|
2076
|
|
|
return ($this->index(false) === 0);
|
2077
|
|
|
}
|
2078
|
|
|
|
2079
|
|
|
/**
|
2080
|
|
|
* Checks if node matches css query filter ":last"
|
2081
|
|
|
* @return bool
|
2082
|
|
|
* @see match()
|
2083
|
|
|
* @access private
|
2084
|
|
|
*/
|
2085
|
|
|
protected function filter_last() {
|
2086
|
|
|
if ($this->parent === null) {
|
2087
|
|
|
return false;
|
2088
|
|
|
} else {
|
2089
|
|
|
return ($this->parent->childCount(true) - 1 === $this->index(false));
|
2090
|
|
|
}
|
2091
|
|
|
}
|
2092
|
|
|
|
2093
|
|
|
/**
|
2094
|
|
|
* Checks if node matches css query filter ":first-of-type"
|
2095
|
|
|
* @return bool
|
2096
|
|
|
* @see match()
|
2097
|
|
|
* @access private
|
2098
|
|
|
*/
|
2099
|
|
|
protected function filter_firsttype() {
|
2100
|
|
|
return ($this->typeIndex() === 0);
|
2101
|
|
|
}
|
2102
|
|
|
|
2103
|
|
|
/**
|
2104
|
|
|
* Checks if node matches css query filter ":last-of-type"
|
2105
|
|
|
* @return bool
|
2106
|
|
|
* @see match()
|
2107
|
|
|
* @access private
|
2108
|
|
|
*/
|
2109
|
|
|
protected function filter_lasttype() {
|
2110
|
|
|
if ($this->parent === null) {
|
2111
|
|
|
return false;
|
2112
|
|
|
} else {
|
2113
|
|
|
return (count($this->parent->getChildrenByTag($this->tag, 'total', false)) - 1 === $this->typeIndex());
|
2114
|
|
|
}
|
2115
|
|
|
}
|
2116
|
|
|
|
2117
|
|
|
/**
|
2118
|
|
|
* Checks if node matches css query filter ":only-child"
|
2119
|
|
|
* @return bool
|
2120
|
|
|
* @see match()
|
2121
|
|
|
* @access private
|
2122
|
|
|
*/
|
2123
|
|
|
protected function filter_onlychild() {
|
2124
|
|
|
if ($this->parent === null) {
|
2125
|
|
|
return false;
|
2126
|
|
|
} else {
|
2127
|
|
|
return ($this->parent->childCount(true) === 1);
|
2128
|
|
|
}
|
2129
|
|
|
}
|
2130
|
|
|
|
2131
|
|
|
/**
|
2132
|
|
|
* Checks if node matches css query filter ":only-of-type"
|
2133
|
|
|
* @return bool
|
2134
|
|
|
* @see match()
|
2135
|
|
|
* @access private
|
2136
|
|
|
*/
|
2137
|
|
|
protected function filter_onlytype() {
|
2138
|
|
|
if ($this->parent === null) {
|
2139
|
|
|
return false;
|
2140
|
|
|
} else {
|
2141
|
|
|
return (count($this->parent->getChildrenByTag($this->tag, 'total', false)) === 1);
|
2142
|
|
|
}
|
2143
|
|
|
}
|
2144
|
|
|
|
2145
|
|
|
/**
|
2146
|
|
|
* Checks if node matches css query filter ":empty"
|
2147
|
|
|
* @return bool
|
2148
|
|
|
* @see match()
|
2149
|
|
|
* @access private
|
2150
|
|
|
*/
|
2151
|
|
|
protected function filter_empty() {
|
2152
|
|
|
return ($this->childCount() === 0);
|
2153
|
|
|
}
|
2154
|
|
|
|
2155
|
|
|
/**
|
2156
|
|
|
* Checks if node matches css query filter ":not-empty"
|
2157
|
|
|
* @return bool
|
2158
|
|
|
* @see match()
|
2159
|
|
|
* @access private
|
2160
|
|
|
*/
|
2161
|
|
|
protected function filter_notempty() {
|
2162
|
|
|
return ($this->childCount() !== 0);
|
2163
|
|
|
}
|
2164
|
|
|
|
2165
|
|
|
/**
|
2166
|
|
|
* Checks if node matches css query filter ":has-text"
|
2167
|
|
|
* @return bool
|
2168
|
|
|
* @see match()
|
2169
|
|
|
* @access private
|
2170
|
|
|
*/
|
2171
|
|
|
protected function filter_hastext() {
|
2172
|
|
|
return ($this->getPlainText() !== '');
|
2173
|
|
|
}
|
2174
|
|
|
|
2175
|
|
|
/**
|
2176
|
|
|
* Checks if node matches css query filter ":no-text"
|
2177
|
|
|
* @return bool
|
2178
|
|
|
* @see match()
|
2179
|
|
|
* @access private
|
2180
|
|
|
*/
|
2181
|
|
|
protected function filter_notext() {
|
2182
|
|
|
return ($this->getPlainText() === '');
|
2183
|
|
|
}
|
2184
|
|
|
|
2185
|
|
|
/**
|
2186
|
|
|
* Checks if node matches css query filter ":lang(s)"
|
2187
|
|
|
* @param string $lang
|
2188
|
|
|
* @return bool
|
2189
|
|
|
* @see match()
|
2190
|
|
|
* @access private
|
2191
|
|
|
*/
|
2192
|
|
|
protected function filter_lang($lang) {
|
2193
|
|
|
return ($this->lang === $lang);
|
|
|
|
|
2194
|
|
|
}
|
2195
|
|
|
|
2196
|
|
|
/**
|
2197
|
|
|
* Checks if node matches css query filter ":contains(s)"
|
2198
|
|
|
* @param string $text
|
2199
|
|
|
* @return bool
|
2200
|
|
|
* @see match()
|
2201
|
|
|
* @access private
|
2202
|
|
|
*/
|
2203
|
|
|
protected function filter_contains($text) {
|
2204
|
|
|
return (strpos($this->getPlainTextUTF8(), $text) !== false);
|
2205
|
|
|
}
|
2206
|
|
|
|
2207
|
|
|
/**
|
2208
|
|
|
* Checks if node matches css query filter ":has(s)"
|
2209
|
|
|
* @param string $selector
|
2210
|
|
|
* @return bool
|
2211
|
|
|
* @see match()
|
2212
|
|
|
* @access private
|
2213
|
|
|
*/
|
2214
|
|
|
protected function filter_has($selector) {
|
2215
|
|
|
$s = $this->select((string) $selector, false);
|
2216
|
|
|
return (is_array($s) && (count($s) > 0));
|
2217
|
|
|
}
|
2218
|
|
|
|
2219
|
|
|
/**
|
2220
|
|
|
* Checks if node matches css query filter ":not(s)"
|
2221
|
|
|
* @param string $selector
|
2222
|
|
|
* @return bool
|
2223
|
|
|
* @see match()
|
2224
|
|
|
* @access private
|
2225
|
|
|
*/
|
2226
|
|
|
protected function filter_not($selector) {
|
2227
|
|
|
$s = $this->select((string) $selector, false, true, true);
|
2228
|
|
|
return ((!is_array($s)) || (array_search($this, $s, true) === false));
|
2229
|
|
|
}
|
2230
|
|
|
|
2231
|
|
|
/**
|
2232
|
|
|
* Checks if node matches css query filter ":element"
|
2233
|
|
|
* @return bool
|
2234
|
|
|
* @see match()
|
2235
|
|
|
* @access private
|
2236
|
|
|
*/
|
2237
|
|
|
protected function filter_element() {
|
2238
|
|
|
return true;
|
2239
|
|
|
}
|
2240
|
|
|
|
2241
|
|
|
/**
|
2242
|
|
|
* Checks if node matches css query filter ":text"
|
2243
|
|
|
* @return bool
|
2244
|
|
|
* @see match()
|
2245
|
|
|
* @access private
|
2246
|
|
|
*/
|
2247
|
|
|
protected function filter_text() {
|
2248
|
|
|
return false;
|
2249
|
|
|
}
|
2250
|
|
|
|
2251
|
|
|
/**
|
2252
|
|
|
* Checks if a node matches css query filter ":checked"
|
2253
|
|
|
* @return bool
|
2254
|
|
|
* @see match()
|
2255
|
|
|
*/
|
2256
|
1 |
|
protected function filter_checked() {
|
2257
|
1 |
|
$attr = $this->getAttribute('checked');
|
2258
|
1 |
|
if (is_array($attr))
|
2259
|
1 |
|
$attr = reset($attr);
|
2260
|
1 |
|
return strcasecmp($attr, 'checked') === 0;
|
2261
|
|
|
}
|
2262
|
|
|
|
2263
|
|
|
/**
|
2264
|
|
|
* Checks if node matches css query filter ":comment"
|
2265
|
|
|
* @return bool
|
2266
|
|
|
* @see match()
|
2267
|
|
|
* @access private
|
2268
|
|
|
*/
|
2269
|
|
|
protected function filter_comment() {
|
2270
|
|
|
return false;
|
2271
|
|
|
}
|
2272
|
|
|
|
2273
|
|
|
/**
|
2274
|
|
|
* Checks if a node matches css query filter ":selected"
|
2275
|
|
|
* @return bool
|
2276
|
|
|
* @see match()
|
2277
|
|
|
*/
|
2278
|
2 |
|
protected function filter_selected() {
|
2279
|
2 |
|
$attr = $this->getAttribute('selected');
|
2280
|
2 |
|
if (is_array($attr))
|
2281
|
2 |
|
$attr = reset($attr);
|
2282
|
|
|
|
2283
|
2 |
|
return strcasecmp($attr, 'selected') === 0;
|
2284
|
|
|
}
|
2285
|
|
|
|
2286
|
1 |
|
public function after($content) {
|
2287
|
1 |
|
$offset = $this->index() + 1;
|
2288
|
1 |
|
$parent = $this->parent;
|
2289
|
1 |
|
$nodes = $this->createNodes($content);
|
2290
|
|
|
|
2291
|
1 |
|
foreach ($nodes as $node) {
|
2292
|
1 |
|
$node->changeParent($parent, $offset);
|
2293
|
1 |
|
}
|
2294
|
1 |
|
return $this;
|
2295
|
|
|
}
|
2296
|
|
|
|
2297
|
|
|
|
2298
|
|
|
/**
|
2299
|
|
|
* Create a {@link DomNode} from its string representation.
|
2300
|
|
|
* @param string|DomNode $content
|
2301
|
|
|
* @return DomNode
|
2302
|
|
|
*/
|
2303
|
2 |
|
protected function createNode($content) {
|
2304
|
2 |
|
$nodes = $this->createNodes($content);
|
2305
|
2 |
|
return reset($nodes);
|
|
|
|
|
2306
|
|
|
}
|
2307
|
|
|
|
2308
|
|
|
/**
|
2309
|
|
|
* Create an array of {@link DomNode} objects from their string representation.
|
2310
|
|
|
* @param string|DomNode $content
|
2311
|
|
|
* @return DomNode[]
|
2312
|
|
|
*/
|
2313
|
9 |
|
protected function createNodes($content) {
|
2314
|
9 |
|
if (is_string($content)) {
|
2315
|
9 |
|
if (strpos($content, ' ') === false) {
|
2316
|
1 |
|
$nodes = array(new $this->childClass($content, $this));
|
2317
|
1 |
|
} else {
|
2318
|
8 |
|
$node = new $this->parserClass($content);
|
2319
|
8 |
|
$nodes = $node->root->children;
|
2320
|
|
|
}
|
2321
|
9 |
|
} else {
|
2322
|
|
|
$nodes = (array)$content;
|
2323
|
|
|
}
|
2324
|
9 |
|
return $nodes;
|
2325
|
|
|
}
|
2326
|
|
|
|
2327
|
1 |
|
public function append($content) {
|
2328
|
1 |
|
$nodes = $this->createNodes($content);
|
2329
|
1 |
|
foreach ($nodes as $node) {
|
2330
|
1 |
|
$node->changeParent($this);
|
2331
|
1 |
|
}
|
2332
|
1 |
|
return $this;
|
2333
|
|
|
}
|
2334
|
|
|
|
2335
|
9 |
|
public function attr($name, $value = null) {
|
2336
|
9 |
|
if ($value === null)
|
2337
|
9 |
|
return $this->getAttribute($name);
|
|
|
|
|
2338
|
|
|
|
2339
|
4 |
|
$this->setAttribute($name, $value);
|
2340
|
4 |
|
return $this;
|
2341
|
|
|
}
|
2342
|
|
|
|
2343
|
1 |
|
public function before($content) {
|
2344
|
1 |
|
$offset = $this->index();
|
2345
|
1 |
|
$parent = $this->parent;
|
2346
|
1 |
|
$nodes = $this->createNodes($content);
|
2347
|
|
|
|
2348
|
1 |
|
foreach ($nodes as $node) {
|
2349
|
1 |
|
$node->changeParent($parent, $offset);
|
2350
|
1 |
|
}
|
2351
|
|
|
|
2352
|
1 |
|
return $this;
|
2353
|
|
|
}
|
2354
|
|
|
|
2355
|
|
|
public function count() {
|
2356
|
|
|
return 1;
|
2357
|
|
|
}
|
2358
|
|
|
|
2359
|
|
|
// public function css($name, $value = null) {
|
|
|
|
|
2360
|
|
|
//
|
2361
|
|
|
// }
|
2362
|
|
|
|
2363
|
1 |
|
public function prepend($content = null) {
|
2364
|
1 |
|
$offset = 0;
|
2365
|
1 |
|
$parent = $this;
|
2366
|
1 |
|
$nodes = $this->createNodes($content);
|
2367
|
|
|
|
2368
|
1 |
|
foreach ($nodes as $node) {
|
2369
|
1 |
|
$node->changeParent($parent, $offset);
|
2370
|
1 |
|
}
|
2371
|
|
|
|
2372
|
1 |
|
return $this;
|
2373
|
|
|
}
|
2374
|
|
|
|
2375
|
4 |
|
public function prop($name, $value = null) {
|
2376
|
4 |
|
switch (strtolower($name)) {
|
2377
|
4 |
|
case 'checked':
|
2378
|
4 |
|
case 'disabled':
|
2379
|
4 |
|
case 'selected':
|
2380
|
3 |
|
if ($value !== null) {
|
2381
|
2 |
|
if ($value) {
|
2382
|
1 |
|
$this->attr($name, $name);
|
2383
|
1 |
|
} else {
|
2384
|
1 |
|
$this->removeAttr($name);
|
2385
|
|
|
}
|
2386
|
2 |
|
return $this;
|
2387
|
|
|
}
|
2388
|
3 |
|
return $this->attr($name) == $name;
|
2389
|
1 |
|
case 'tagname':
|
2390
|
1 |
|
return $this->tagName($value);
|
|
|
|
|
2391
|
|
|
}
|
2392
|
|
|
// The property is not supported, degrade gracefully
|
2393
|
|
|
if ($value === null)
|
2394
|
|
|
return $this;
|
2395
|
|
|
else
|
2396
|
|
|
return null;
|
2397
|
|
|
}
|
2398
|
|
|
|
2399
|
4 |
|
public function remove($selector = null) {
|
2400
|
4 |
|
if ($selector == null) {
|
|
|
|
|
2401
|
3 |
|
$this->delete();
|
2402
|
3 |
|
} else {
|
2403
|
1 |
|
$nodes = (array)$this->select($selector);
|
2404
|
1 |
|
foreach ($nodes as $node) {
|
2405
|
1 |
|
$node->delete();
|
2406
|
1 |
|
}
|
2407
|
|
|
}
|
2408
|
4 |
|
}
|
2409
|
|
|
|
2410
|
3 |
|
public function removeAttr($name) {
|
2411
|
3 |
|
$this->deleteAttribute($name);
|
2412
|
|
|
|
2413
|
3 |
|
return $this;
|
2414
|
|
|
}
|
2415
|
|
|
|
2416
|
2 |
|
function replaceWith($content) {
|
|
|
|
|
2417
|
2 |
|
$node_index = $this->index();
|
2418
|
|
|
|
2419
|
|
|
// Add the new node.
|
2420
|
2 |
|
$node = $this->createNode($content);
|
2421
|
2 |
|
$node->changeParent($this->parent, $node_index);
|
2422
|
|
|
|
2423
|
|
|
// Remove this node.
|
2424
|
2 |
|
$this->remove();
|
2425
|
|
|
|
2426
|
2 |
|
return $node;
|
2427
|
|
|
}
|
2428
|
|
|
|
2429
|
|
|
/**
|
2430
|
|
|
* @param type $value
|
2431
|
|
|
* @return string|DomNode
|
2432
|
|
|
*/
|
2433
|
2 |
|
public function tagName($value = null) {
|
2434
|
2 |
|
if ($value !== null) {
|
2435
|
2 |
|
$this->setTag($value);
|
2436
|
2 |
|
return $this;
|
2437
|
|
|
}
|
2438
|
1 |
|
return $this->getTag();
|
2439
|
|
|
}
|
2440
|
|
|
|
2441
|
2 |
|
public function text($value = null) {
|
2442
|
2 |
|
if ($value === null)
|
2443
|
2 |
|
return $this->getPlainText();
|
2444
|
|
|
|
2445
|
1 |
|
$this->setPlainText($value);
|
2446
|
1 |
|
return $this;
|
2447
|
|
|
}
|
2448
|
|
|
|
2449
|
1 |
|
public function toggleClass($classname, $switch = null) {
|
2450
|
1 |
|
if ($switch === true) {
|
2451
|
1 |
|
$this->addClass($classname);
|
2452
|
1 |
|
} elseif ($switch === false) {
|
2453
|
1 |
|
$this->removeClass($classname);
|
2454
|
1 |
|
} else {
|
2455
|
1 |
|
if ($this->hasClass($classname))
|
2456
|
1 |
|
$this->removeClass($classname);
|
2457
|
|
|
else
|
2458
|
1 |
|
$this->addClass($classname);
|
2459
|
|
|
}
|
2460
|
1 |
|
return $this;
|
2461
|
|
|
}
|
2462
|
|
|
|
2463
|
1 |
|
public function unwrap() {
|
2464
|
1 |
|
$this->parent->detach(true);
|
2465
|
1 |
|
return $this;
|
2466
|
|
|
}
|
2467
|
|
|
|
2468
|
5 |
|
public function val($value = null) {
|
2469
|
5 |
|
switch (strtolower($this->tag)) {
|
2470
|
5 |
|
case 'select':
|
|
|
|
|
2471
|
2 |
|
if ($value === null) {
|
2472
|
|
|
// Return the value of a selected child.
|
2473
|
2 |
|
return $this->query('option:selected')->attr('value');
|
2474
|
|
|
} else {
|
2475
|
|
|
// Select the option with the right value and deselect the others.
|
2476
|
1 |
|
foreach ($this->query('option') as $option) {
|
2477
|
1 |
|
if ($option->attr('value') == $value) {
|
2478
|
1 |
|
$option->attr('selected', 'selected');
|
2479
|
1 |
|
} else {
|
2480
|
1 |
|
$option->removeAttr('selected');
|
2481
|
|
|
}
|
2482
|
1 |
|
}
|
2483
|
1 |
|
return $this;
|
2484
|
|
|
}
|
2485
|
3 |
|
case 'textarea':
|
|
|
|
|
2486
|
1 |
|
if ($value === null) {
|
2487
|
|
|
// Return the contents of the textarea.
|
2488
|
1 |
|
return $this->getInnerText();
|
2489
|
|
|
} else {
|
2490
|
|
|
// Set the contents of the textarea.
|
2491
|
1 |
|
$this->setInnerText($value);
|
2492
|
1 |
|
return $this;
|
2493
|
|
|
}
|
2494
|
2 |
|
case 'input':
|
2495
|
2 |
|
switch (strtolower($this->getAttribute('type'))) {
|
2496
|
2 |
|
case 'checkbox':
|
2497
|
1 |
|
if ($value === null)
|
2498
|
1 |
|
return $this->prop('checked') ? $this->getAttribute('value') : null;
|
2499
|
|
|
else {
|
2500
|
1 |
|
if (!$value) {
|
2501
|
1 |
|
$this->deleteAttribute('checked');
|
2502
|
1 |
|
} else {
|
2503
|
1 |
|
$this->setAttribute('value', $value);
|
2504
|
1 |
|
$this->setAttribute('checked', 'checked');
|
2505
|
|
|
}
|
2506
|
1 |
|
return $this;
|
2507
|
|
|
}
|
2508
|
1 |
|
}
|
2509
|
1 |
|
}
|
2510
|
|
|
|
2511
|
|
|
// Other node types can just get/set the value attribute.
|
2512
|
1 |
|
if ($value !== null) {
|
2513
|
1 |
|
$this->setAttribute('value', $value);
|
2514
|
1 |
|
return $this;
|
2515
|
|
|
}
|
2516
|
1 |
|
return $this->getAttribute('value');
|
2517
|
|
|
}
|
2518
|
|
|
|
2519
|
|
|
}
|
2520
|
|
|
|
2521
|
|
|
/**
|
2522
|
|
|
* Node subclass for text
|
2523
|
|
|
*/
|
2524
|
|
|
class TextNode extends DomNode {
|
|
|
|
|
2525
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2526
|
|
|
#static $NODE_TYPE = self::NODE_TEXT;
|
|
|
|
|
2527
|
|
|
#php4e
|
2528
|
|
|
#php5
|
2529
|
|
|
const NODE_TYPE = self::NODE_TEXT;
|
2530
|
|
|
#php5e
|
2531
|
|
|
var $tag = '~text~';
|
|
|
|
|
2532
|
|
|
|
2533
|
|
|
/**
|
2534
|
|
|
* @var string
|
2535
|
|
|
*/
|
2536
|
|
|
var $text = '';
|
|
|
|
|
2537
|
|
|
|
2538
|
|
|
/**
|
2539
|
|
|
* Class constructor
|
2540
|
|
|
* @param DomNode $parent
|
2541
|
|
|
* @param string $text
|
2542
|
|
|
*/
|
2543
|
37 |
|
function __construct($parent, $text = '') {
|
|
|
|
|
2544
|
37 |
|
$this->parent = $parent;
|
|
|
|
|
2545
|
37 |
|
$this->text = $text;
|
2546
|
37 |
|
}
|
2547
|
|
|
|
2548
|
|
|
#php4 PHP4 class constructor compatibility
|
2549
|
|
|
#function TextNode($parent, $text = '') {return $this->__construct($parent, $text);}
|
|
|
|
|
2550
|
|
|
#php4e
|
2551
|
|
|
|
2552
|
|
|
function isText() {return true;}
|
|
|
|
|
2553
|
|
|
function isTextOrComment() {return true;}
|
|
|
|
|
2554
|
|
|
protected function filter_element() {return false;}
|
2555
|
|
|
protected function filter_text() {return true;}
|
2556
|
|
|
function toString_attributes() {return '';}
|
|
|
|
|
2557
|
|
|
function toString_content($attributes = true, $recursive = true, $content_only = false) {return $this->text;}
|
|
|
|
|
2558
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {return $this->text;}
|
|
|
|
|
2559
|
|
|
|
2560
|
|
|
/**
|
2561
|
|
|
* {@inheritdoc}
|
2562
|
|
|
*/
|
2563
|
1 |
|
public function text($value = null) {
|
2564
|
1 |
|
if ($value !== null) {
|
2565
|
1 |
|
$this->text = $value;
|
2566
|
1 |
|
return $this;
|
2567
|
|
|
}
|
2568
|
1 |
|
return $this->text;
|
2569
|
|
|
}
|
2570
|
|
|
|
2571
|
|
|
/**
|
2572
|
|
|
* {@inheritdoc}
|
2573
|
|
|
*/
|
2574
|
1 |
|
public function html($value = null) {
|
2575
|
1 |
|
if ($value !== null) {
|
2576
|
|
|
$this->text = $value;
|
2577
|
|
|
return $this;
|
2578
|
|
|
}
|
2579
|
1 |
|
return $this->text;
|
2580
|
|
|
}
|
2581
|
|
|
}
|
2582
|
|
|
|
2583
|
|
|
/**
|
2584
|
|
|
* Node subclass for comments
|
2585
|
|
|
*/
|
2586
|
|
|
class CommentNode extends DomNode {
|
|
|
|
|
2587
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2588
|
|
|
#static $NODE_TYPE = self::NODE_COMMENT;
|
|
|
|
|
2589
|
|
|
#php4e
|
2590
|
|
|
#php5
|
2591
|
|
|
const NODE_TYPE = self::NODE_COMMENT;
|
2592
|
|
|
#php5e
|
2593
|
|
|
var $tag = '~comment~';
|
|
|
|
|
2594
|
|
|
|
2595
|
|
|
/**
|
2596
|
|
|
* @var string
|
2597
|
|
|
*/
|
2598
|
|
|
var $text = '';
|
|
|
|
|
2599
|
|
|
|
2600
|
|
|
/**
|
2601
|
|
|
* Class constructor
|
2602
|
|
|
* @param DomNode $parent
|
2603
|
|
|
* @param string $text
|
2604
|
|
|
*/
|
2605
|
9 |
|
function __construct($parent, $text = '') {
|
|
|
|
|
2606
|
9 |
|
$this->parent = $parent;
|
|
|
|
|
2607
|
9 |
|
$this->text = $text;
|
2608
|
9 |
|
}
|
2609
|
|
|
|
2610
|
|
|
#php4 PHP4 class constructor compatibility
|
2611
|
|
|
#function CommentNode($parent, $text = '') {return $this->__construct($parent, $text);}
|
|
|
|
|
2612
|
|
|
#php4e
|
2613
|
|
|
|
2614
|
|
|
function isComment() {return true;}
|
|
|
|
|
2615
|
|
|
function isTextOrComment() {return true;}
|
|
|
|
|
2616
|
|
|
protected function filter_element() {return false;}
|
2617
|
|
|
protected function filter_comment() {return true;}
|
2618
|
|
|
function toString_attributes() {return '';}
|
|
|
|
|
2619
|
|
|
function toString_content($attributes = true, $recursive = true, $content_only = false) {return $this->text;}
|
|
|
|
|
2620
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {return '<!--'.$this->text.'-->';}
|
|
|
|
|
2621
|
|
|
}
|
2622
|
|
|
|
2623
|
|
|
/**
|
2624
|
|
|
* Node subclass for conditional tags
|
2625
|
|
|
*/
|
2626
|
|
|
class ConditionalTagNode extends DomNode {
|
|
|
|
|
2627
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2628
|
|
|
#static $NODE_TYPE = self::NODE_CONDITIONAL;
|
|
|
|
|
2629
|
|
|
#php4e
|
2630
|
|
|
#php5
|
2631
|
|
|
const NODE_TYPE = self::NODE_CONDITIONAL;
|
2632
|
|
|
#php5e
|
2633
|
|
|
var $tag = '~conditional~';
|
|
|
|
|
2634
|
|
|
|
2635
|
|
|
/**
|
2636
|
|
|
* @var string
|
2637
|
|
|
*/
|
2638
|
|
|
var $condition = '';
|
|
|
|
|
2639
|
|
|
|
2640
|
|
|
/**
|
2641
|
|
|
* Class constructor
|
2642
|
|
|
* @param DomNode $parent
|
2643
|
|
|
* @param string $condition e.g. "if IE"
|
2644
|
|
|
* @param bool $hidden <!--[if if true, <![if if false
|
2645
|
|
|
*/
|
2646
|
|
|
function __construct($parent, $condition = '', $hidden = true) {
|
|
|
|
|
2647
|
|
|
$this->parent = $parent;
|
|
|
|
|
2648
|
|
|
$this->hidden = $hidden;
|
|
|
|
|
2649
|
|
|
$this->condition = $condition;
|
2650
|
|
|
}
|
2651
|
|
|
|
2652
|
|
|
#php4 PHP4 class constructor compatibility
|
2653
|
|
|
#function ConditionalTagNode($parent, $condition = '', $hidden = true) {return $this->__construct($parent, $condition, $hidden);}
|
|
|
|
|
2654
|
|
|
#php4e
|
2655
|
|
|
|
2656
|
|
|
protected function filter_element() {return false;}
|
2657
|
|
|
function toString_attributes() {return '';}
|
|
|
|
|
2658
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {
|
|
|
|
|
2659
|
|
|
if ($content_only) {
|
2660
|
|
|
if (is_int($content_only)) {
|
2661
|
|
|
--$content_only;
|
2662
|
|
|
}
|
2663
|
|
|
return $this->toString_content($attributes, $recursive, $content_only);
|
|
|
|
|
2664
|
|
|
}
|
2665
|
|
|
|
2666
|
|
|
$s = '<!'.(($this->hidden) ? '--' : '').'['.$this->condition.']>';
|
|
|
|
|
2667
|
|
|
if($recursive) {
|
2668
|
|
|
$s .= $this->toString_content($attributes);
|
2669
|
|
|
}
|
2670
|
|
|
$s .= '<![endif]'.(($this->hidden) ? '--' : '').'>';
|
|
|
|
|
2671
|
|
|
return $s;
|
2672
|
|
|
}
|
2673
|
|
|
}
|
2674
|
|
|
|
2675
|
|
|
/**
|
2676
|
|
|
* Node subclass for CDATA tags
|
2677
|
|
|
*/
|
2678
|
|
|
class CdataNode extends DomNode {
|
|
|
|
|
2679
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2680
|
|
|
#static $NODE_TYPE = self::NODE_CDATA;
|
|
|
|
|
2681
|
|
|
#php4e
|
2682
|
|
|
#php5
|
2683
|
|
|
const NODE_TYPE = self::NODE_CDATA;
|
2684
|
|
|
#php5e
|
2685
|
|
|
var $tag = '~cdata~';
|
|
|
|
|
2686
|
|
|
|
2687
|
|
|
/**
|
2688
|
|
|
* @var string
|
2689
|
|
|
*/
|
2690
|
|
|
var $text = '';
|
|
|
|
|
2691
|
|
|
|
2692
|
|
|
/**
|
2693
|
|
|
* Class constructor
|
2694
|
|
|
* @param DomNode $parent
|
2695
|
|
|
* @param string $text
|
2696
|
|
|
*/
|
2697
|
|
|
function __construct($parent, $text = '') {
|
|
|
|
|
2698
|
|
|
$this->parent = $parent;
|
|
|
|
|
2699
|
|
|
$this->text = $text;
|
2700
|
|
|
}
|
2701
|
|
|
|
2702
|
|
|
#php4 PHP4 class constructor compatibility
|
2703
|
|
|
#function CdataNode($parent, $text = '') {return $this->__construct($parent, $text);}
|
|
|
|
|
2704
|
|
|
#php4e
|
2705
|
|
|
|
2706
|
|
|
protected function filter_element() {return false;}
|
2707
|
|
|
function toString_attributes() {return '';}
|
|
|
|
|
2708
|
|
|
function toString_content($attributes = true, $recursive = true, $content_only = false) {return $this->text;}
|
|
|
|
|
2709
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {return '<![CDATA['.$this->text.']]>';}
|
|
|
|
|
2710
|
|
|
}
|
2711
|
|
|
|
2712
|
|
|
/**
|
2713
|
|
|
* Node subclass for doctype tags
|
2714
|
|
|
*/
|
2715
|
|
|
class DoctypeNode extends DomNode {
|
|
|
|
|
2716
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2717
|
|
|
#static $NODE_TYPE = self::NODE_DOCTYPE;
|
|
|
|
|
2718
|
|
|
#php4e
|
2719
|
|
|
#php5
|
2720
|
|
|
const NODE_TYPE = self::NODE_DOCTYPE;
|
2721
|
|
|
#php5e
|
2722
|
|
|
var $tag = '!DOCTYPE';
|
|
|
|
|
2723
|
|
|
|
2724
|
|
|
/**
|
2725
|
|
|
* @var string
|
2726
|
|
|
*/
|
2727
|
|
|
var $dtd = '';
|
|
|
|
|
2728
|
|
|
|
2729
|
|
|
/**
|
2730
|
|
|
* Class constructor
|
2731
|
|
|
* @param DomNode $parent
|
2732
|
|
|
* @param string $dtd
|
2733
|
|
|
*/
|
2734
|
9 |
|
function __construct($parent, $dtd = '') {
|
|
|
|
|
2735
|
9 |
|
$this->parent = $parent;
|
|
|
|
|
2736
|
9 |
|
$this->dtd = $dtd;
|
2737
|
9 |
|
}
|
2738
|
|
|
|
2739
|
|
|
#php4 PHP4 class constructor compatibility
|
2740
|
|
|
#function DoctypeNode($parent, $dtd = '') {return $this->__construct($parent, $dtd);}
|
|
|
|
|
2741
|
|
|
#php4e
|
2742
|
|
|
|
2743
|
|
|
protected function filter_element() {return false;}
|
2744
|
|
|
function toString_attributes() {return '';}
|
|
|
|
|
2745
|
|
|
function toString_content($attributes = true, $recursive = true, $content_only = false) {return $this->text;}
|
|
|
|
|
2746
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {return '<'.$this->tag.' '.$this->dtd.'>';}
|
|
|
|
|
2747
|
|
|
}
|
2748
|
|
|
|
2749
|
|
|
/**
|
2750
|
|
|
* Node subclass for embedded tags like xml, php and asp
|
2751
|
|
|
*/
|
2752
|
|
|
class EmbeddedNode extends DomNode {
|
|
|
|
|
2753
|
|
|
|
2754
|
|
|
/**
|
2755
|
|
|
* @var string
|
2756
|
|
|
* @internal specific char for tags, like ? for php and % for asp
|
2757
|
|
|
* @access private
|
2758
|
|
|
*/
|
2759
|
|
|
var $tag_char = '';
|
|
|
|
|
2760
|
|
|
|
2761
|
|
|
/**
|
2762
|
|
|
* @var string
|
2763
|
|
|
*/
|
2764
|
|
|
var $text = '';
|
|
|
|
|
2765
|
|
|
|
2766
|
|
|
/**
|
2767
|
|
|
* Class constructor
|
2768
|
|
|
* @param DomNode $parent
|
2769
|
|
|
* @param string $tag_char {@link $tag_char}
|
2770
|
|
|
* @param string $tag {@link $tag}
|
2771
|
|
|
* @param string $text
|
2772
|
|
|
* @param array $attributes array('attr' => 'val')
|
2773
|
|
|
*/
|
2774
|
|
|
function __construct($parent, $tag_char = '', $tag = '', $text = '', $attributes = array()) {
|
|
|
|
|
2775
|
|
|
$this->parent = $parent;
|
|
|
|
|
2776
|
|
|
$this->tag_char = $tag_char;
|
2777
|
|
|
if ($tag[0] !== $this->tag_char) {
|
2778
|
|
|
$tag = $this->tag_char.$tag;
|
2779
|
|
|
}
|
2780
|
|
|
$this->tag = $tag;
|
|
|
|
|
2781
|
|
|
$this->text = $text;
|
2782
|
|
|
$this->attributes = $attributes;
|
|
|
|
|
2783
|
|
|
$this->self_close_str = $tag_char;
|
|
|
|
|
2784
|
|
|
}
|
2785
|
|
|
|
2786
|
|
|
#php4 PHP4 class constructor compatibility
|
2787
|
|
|
#function EmbeddedNode($parent, $tag_char = '', $tag = '', $text = '', $attributes = array()) {return $this->__construct($parent, $tag_char, $tag, $text, $attributes);}
|
|
|
|
|
2788
|
|
|
#php4e
|
2789
|
|
|
|
2790
|
|
|
protected function filter_element() {return false;}
|
2791
|
|
|
function toString($attributes = true, $recursive = true, $content_only = false) {
|
|
|
|
|
2792
|
|
|
$s = '<'.$this->tag;
|
|
|
|
|
2793
|
|
|
if ($attributes) {
|
2794
|
|
|
$s .= $this->toString_attributes();
|
2795
|
|
|
}
|
2796
|
|
|
$s .= $this->text.$this->self_close_str.'>';
|
|
|
|
|
2797
|
|
|
return $s;
|
2798
|
|
|
}
|
2799
|
|
|
}
|
2800
|
|
|
|
2801
|
|
|
/**
|
2802
|
|
|
* Node subclass for "?" tags, like php and xml
|
2803
|
|
|
*/
|
2804
|
|
|
class XmlNode extends EmbeddedNode {
|
|
|
|
|
2805
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2806
|
|
|
#static $NODE_TYPE = self::NODE_XML;
|
|
|
|
|
2807
|
|
|
#php4e
|
2808
|
|
|
#php5
|
2809
|
|
|
const NODE_TYPE = self::NODE_XML;
|
2810
|
|
|
#php5e
|
2811
|
|
|
|
2812
|
|
|
/**
|
2813
|
|
|
* Class constructor
|
2814
|
|
|
* @param DomNode $parent
|
2815
|
|
|
* @param string $tag {@link $tag}
|
2816
|
|
|
* @param string $text
|
2817
|
|
|
* @param array $attributes array('attr' => 'val')
|
2818
|
|
|
*/
|
2819
|
|
|
function __construct($parent, $tag = 'xml', $text = '', $attributes = array()) {
|
|
|
|
|
2820
|
|
|
return parent::__construct($parent, '?', $tag, $text, $attributes);
|
|
|
|
|
2821
|
|
|
}
|
2822
|
|
|
|
2823
|
|
|
#php4 PHP4 class constructor compatibility
|
2824
|
|
|
#function XmlNode($parent, $tag = 'xml', $text = '', $attributes = array()) {return $this->__construct($parent, $tag, $text, $attributes);}
|
|
|
|
|
2825
|
|
|
#php4e
|
2826
|
|
|
}
|
2827
|
|
|
|
2828
|
|
|
/**
|
2829
|
|
|
* Node subclass for asp tags
|
2830
|
|
|
*/
|
2831
|
|
|
class AspEmbeddedNode extends EmbeddedNode {
|
|
|
|
|
2832
|
|
|
#php4 Compatibility with PHP4, this gets changed to a regular var in release tool
|
2833
|
|
|
#static $NODE_TYPE = self::NODE_ASP;
|
|
|
|
|
2834
|
|
|
#php4e
|
2835
|
|
|
#php5
|
2836
|
|
|
const NODE_TYPE = self::NODE_ASP;
|
2837
|
|
|
#php5e
|
2838
|
|
|
|
2839
|
|
|
/**
|
2840
|
|
|
* Class constructor
|
2841
|
|
|
* @param DomNode $parent
|
2842
|
|
|
* @param string $tag {@link $tag}
|
2843
|
|
|
* @param string $text
|
2844
|
|
|
* @param array $attributes array('attr' => 'val')
|
2845
|
|
|
*/
|
2846
|
|
|
function __construct($parent, $tag = '', $text = '', $attributes = array()) {
|
|
|
|
|
2847
|
|
|
return parent::__construct($parent, '%', $tag, $text, $attributes);
|
|
|
|
|
2848
|
|
|
}
|
2849
|
|
|
|
2850
|
|
|
#php4 PHP4 class constructor compatibility
|
2851
|
|
|
#function AspEmbeddedNode($parent, $tag = '', $text = '', $attributes = array()) {return $this->__construct($parent, $tag, $text, $attributes);}
|
|
|
|
|
2852
|
|
|
#php4e
|
2853
|
|
|
}
|
2854
|
|
|
|
2855
|
|
|
?> |
|
|
|
|
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.