PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 2.2.0
Page Builder: Pagelayer – Drag and Drop website builder v2.2.0
2.2.1 2.2.0 2.1.9 2.1.8 2.1.7 2.1.6 2.1.5 2.1.4 2.1.3 trunk 0.9.0 0.9.1 0.9.2 0.9.3 0.9.4 0.9.5 0.9.6 0.9.7 0.9.8 0.9.9 1.0.0 1.0.2 1.0.3 1.0.4 1.0.5 All 129 releases
pagelayer / lib / pquery / gan_node_html.php

gan_node_html.php in Page Builder: Pagelayer – Drag and Drop website builder 2.2.0, at lib/pquery/gan_node_html.php

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