PluginProbe
Page Builder: Pagelayer – Drag and Drop website builder / 1.0.2
Page Builder: Pagelayer – Drag and Drop website builder v1.0.2
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_selector_html.php

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

950 lines 23.9 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 pQuery;
11
12 /**
13 * Tokenizes a css selector query
14 */
15 class CSSQueryTokenizer extends TokenizerBase {
16
17 /**
18 * Opening bracket token, used for "["
19 */
20 const TOK_BRACKET_OPEN = 100;
21 /**
22 * Closing bracket token, used for "]"
23 */
24 const TOK_BRACKET_CLOSE = 101;
25 /**
26 * Opening brace token, used for "("
27 */
28 const TOK_BRACE_OPEN = 102;
29 /**
30 * Closing brace token, used for ")"
31 */
32 const TOK_BRACE_CLOSE = 103;
33 /**
34 * String token
35 */
36 const TOK_STRING = 104;
37 /**
38 * Colon token, used for ":"
39 */
40 const TOK_COLON = 105;
41 /**
42 * Comma token, used for ","
43 */
44 const TOK_COMMA = 106;
45 /**
46 * "Not" token, used for "!"
47 */
48 const TOK_NOT = 107;
49
50 /**
51 * "All" token, used for "*" in query
52 */
53 const TOK_ALL = 108;
54 /**
55 * Pipe token, used for "|"
56 */
57 const TOK_PIPE = 109;
58 /**
59 * Plus token, used for "+"
60 */
61 const TOK_PLUS = 110;
62 /**
63 * "Sibling" token, used for "~" in query
64 */
65 const TOK_SIBLING = 111;
66 /**
67 * Class token, used for "." in query
68 */
69 const TOK_CLASS = 112;
70 /**
71 * ID token, used for "#" in query
72 */
73 const TOK_ID = 113;
74 /**
75 * Child token, used for ">" in query
76 */
77 const TOK_CHILD = 114;
78
79 /**
80 * Attribute compare prefix token, used for "|="
81 */
82 const TOK_COMPARE_PREFIX = 115;
83 /**
84 * Attribute contains token, used for "*="
85 */
86 const TOK_COMPARE_CONTAINS = 116;
87 /**
88 * Attribute contains word token, used for "~="
89 */
90 const TOK_COMPARE_CONTAINS_WORD = 117;
91 /**
92 * Attribute compare end token, used for "$="
93 */
94 const TOK_COMPARE_ENDS = 118;
95 /**
96 * Attribute equals token, used for "="
97 */
98 const TOK_COMPARE_EQUALS = 119;
99 /**
100 * Attribute not equal token, used for "!="
101 */
102 const TOK_COMPARE_NOT_EQUAL = 120;
103 /**
104 * Attribute compare bigger than token, used for ">="
105 */
106 const TOK_COMPARE_BIGGER_THAN = 121;
107 /**
108 * Attribute compare smaller than token, used for "<="
109 */
110 const TOK_COMPARE_SMALLER_THAN = 122;
111 /**
112 * Attribute compare with regex, used for "%="
113 */
114 const TOK_COMPARE_REGEX = 123;
115 /**
116 * Attribute compare start token, used for "^="
117 */
118 const TOK_COMPARE_STARTS = 124;
119
120 /**
121 * Sets query identifiers
122 * @see TokenizerBase::$identifiers
123 * @access private
124 */
125 var $identifiers = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890_-?';
126
127 /**
128 * Map characters to match their tokens
129 * @see TokenizerBase::$custom_char_map
130 * @access private
131 */
132 var $custom_char_map = array(
133 '.' => self::TOK_CLASS,
134 '#' => self::TOK_ID,
135 ',' => self::TOK_COMMA,
136 '>' => 'parse_gt',//self::TOK_CHILD,
137
138 '+' => self::TOK_PLUS,
139 '~' => 'parse_sibling',
140
141 '|' => 'parse_pipe',
142 '*' => 'parse_star',
143 '$' => 'parse_compare',
144 '=' => self::TOK_COMPARE_EQUALS,
145 '!' => 'parse_not',
146 '%' => 'parse_compare',
147 '^' => 'parse_compare',
148 '<' => 'parse_compare',
149
150 '"' => 'parse_string',
151 "'" => 'parse_string',
152 '(' => self::TOK_BRACE_OPEN,
153 ')' => self::TOK_BRACE_CLOSE,
154 '[' => self::TOK_BRACKET_OPEN,
155 ']' => self::TOK_BRACKET_CLOSE,
156 ':' => self::TOK_COLON
157 );
158
159 /**
160 * Parse ">" character
161 * @internal Could be {@link TOK_CHILD} or {@link TOK_COMPARE_BIGGER_THAN}
162 * @return int
163 */
164 protected function parse_gt() {
165 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
166 ++$this->pos;
167 return ($this->token = self::TOK_COMPARE_BIGGER_THAN);
168 } else {
169 return ($this->token = self::TOK_CHILD);
170 }
171 }
172
173 /**
174 * Parse "~" character
175 * @internal Could be {@link TOK_SIBLING} or {@link TOK_COMPARE_CONTAINS_WORD}
176 * @return int
177 */
178 protected function parse_sibling() {
179 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
180 ++$this->pos;
181 return ($this->token = self::TOK_COMPARE_CONTAINS_WORD);
182 } else {
183 return ($this->token = self::TOK_SIBLING);
184 }
185 }
186
187 /**
188 * Parse "|" character
189 * @internal Could be {@link TOK_PIPE} or {@link TOK_COMPARE_PREFIX}
190 * @return int
191 */
192 protected function parse_pipe() {
193 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
194 ++$this->pos;
195 return ($this->token = self::TOK_COMPARE_PREFIX);
196 } else {
197 return ($this->token = self::TOK_PIPE);
198 }
199 }
200
201 /**
202 * Parse "*" character
203 * @internal Could be {@link TOK_ALL} or {@link TOK_COMPARE_CONTAINS}
204 * @return int
205 */
206 protected function parse_star() {
207 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
208 ++$this->pos;
209 return ($this->token = self::TOK_COMPARE_CONTAINS);
210 } else {
211 return ($this->token = self::TOK_ALL);
212 }
213 }
214
215 /**
216 * Parse "!" character
217 * @internal Could be {@link TOK_NOT} or {@link TOK_COMPARE_NOT_EQUAL}
218 * @return int
219 */
220 protected function parse_not() {
221 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
222 ++$this->pos;
223 return ($this->token = self::TOK_COMPARE_NOT_EQUAL);
224 } else {
225 return ($this->token = self::TOK_NOT);
226 }
227 }
228
229 /**
230 * Parse several compare characters
231 * @return int
232 */
233 protected function parse_compare() {
234 if ((($this->pos + 1) < $this->size) && ($this->doc[$this->pos + 1] === '=')) {
235 switch($this->doc[$this->pos++]) {
236 case '$':
237 return ($this->token = self::TOK_COMPARE_ENDS);
238 case '%':
239 return ($this->token = self::TOK_COMPARE_REGEX);
240 case '^':
241 return ($this->token = self::TOK_COMPARE_STARTS);
242 case '<':
243 return ($this->token = self::TOK_COMPARE_SMALLER_THAN);
244 }
245 }
246 return false;
247 }
248
249 /**
250 * Parse strings (" and ')
251 * @return int
252 */
253 protected function parse_string() {
254 $char = $this->doc[$this->pos];
255
256 while (true) {
257 if ($this->next_search($char.'\\', false) !== self::TOK_NULL) {
258 if($this->doc[$this->pos] === $char) {
259 break;
260 } else {
261 ++$this->pos;
262 }
263 } else {
264 $this->pos = $this->size - 1;
265 break;
266 }
267 }
268
269 return ($this->token = self::TOK_STRING);
270 }
271
272 }
273
274 /**
275 * Performs a css select query on HTML nodes
276 */
277 class HtmlSelector {
278
279 /**
280 * Parser object
281 * @internal If string, then it will create a new instance as parser
282 * @var CSSQueryTokenizer
283 */
284 var $parser = 'pQuery\\CSSQueryTokenizer';
285
286 /**
287 * Target of queries
288 * @var DomNode
289 */
290 var $root = null;
291
292 /**
293 * Last performed query, result in {@link $result}
294 * @var string
295 */
296 var $query = '';
297
298 /**
299 * Array of matching nodes
300 * @var array
301 */
302 var $result = array();
303
304 /**
305 * Include root in search, if false the only child nodes are evaluated
306 * @var bool
307 */
308 var $search_root = false;
309
310 /**
311 * Search recursively
312 * @var bool
313 */
314 var $search_recursive = true;
315
316 /**
317 * Extra function map for custom filters
318 * @var array
319 * @internal array('root' => 'filter_root') will cause the
320 * selector to call $this->filter_root at :root
321 * @see DomNode::$filter_map
322 */
323 var $custom_filter_map = array();
324
325 /**
326 * Class constructor
327 * @param DomNode $root {@link $root}
328 * @param string $query
329 * @param bool $search_root {@link $search_root}
330 * @param bool $search_recursive {@link $search_recursive}
331 * @param CSSQueryTokenizer $parser If null, then default class will be used
332 */
333 function __construct($root, $query = '*', $search_root = false, $search_recursive = true, $parser = null) {
334 if ($parser === null) {
335 $parser = new $this->parser();
336 }
337 $this->parser = $parser;
338 $this->root =& $root;
339
340 $this->search_root = $search_root;
341 $this->search_recursive = $search_recursive;
342
343 $this->select($query);
344 }
345
346 #php4 PHP4 class constructor compatibility
347 #function HtmlSelector($root, $query = '*', $search_root = false, $search_recursive = true, $parser = null) {return $this->__construct($root, $query, $search_root, $search_recursive, $parser);}
348 #php4e
349
350 /**
351 * toString method, returns {@link $query}
352 * @return string
353 * @access private
354 */
355 function __toString() {
356 return $this->query;
357 }
358
359 /**
360 * Class magic invoke method, performs {@link select()}
361 * @return array
362 * @access private
363 */
364 function __invoke($query = '*') {
365 return $this->select($query);
366 }
367
368 /**
369 * Perform query
370 * @param string $query
371 * @return array False on failure
372 */
373 function select($query = '*') {
374 $this->parser->setDoc($query);
375 $this->query = $query;
376 return (($this->parse()) ? $this->result : false);
377 }
378
379 /**
380 * Trigger error
381 * @param string $error
382 * @internal %pos% and %tok% will be replace in string with position and token(string)
383 * @access private
384 */
385 protected function error($error) {
386 $error = htmlentities(str_replace(
387 array('%tok%', '%pos%'),
388 array($this->parser->getTokenString(), (int) $this->parser->getPos()),
389 $error
390 ));
391
392 trigger_error($error);
393 }
394
395 /**
396 * Get identifier (parse identifier or string)
397 * @param bool $do_error Error on failure
398 * @return string False on failure
399 * @access private
400 */
401 protected function parse_getIdentifier($do_error = true) {
402 $p =& $this->parser;
403 $tok = $p->token;
404
405 if ($tok === CSSQueryTokenizer::TOK_IDENTIFIER) {
406 return $p->getTokenString();
407 } elseif($tok === CSSQueryTokenizer::TOK_STRING) {
408 return str_replace(array('\\\'', '\\"', '\\\\'), array('\'', '"', '\\'), $p->getTokenString(1, -1));
409 } elseif ($do_error) {
410 $this->error('Expected identifier at %pos%!');
411 }
412 return false;
413 }
414
415 /**
416 * Get query conditions (tag, attribute and filter conditions)
417 * @return array False on failure
418 * @see DomNode::match()
419 * @access private
420 */
421 protected function parse_conditions() {
422 $p =& $this->parser;
423 $tok = $p->token;
424
425 if ($tok === CSSQueryTokenizer::TOK_NULL) {
426 $this->error('Invalid search pattern(1): Empty string!');
427 return false;
428 }
429 $conditions_all = array();
430
431 //Tags
432 while ($tok !== CSSQueryTokenizer::TOK_NULL) {
433 $conditions = array('tags' => array(), 'attributes' => array());
434
435 if ($tok === CSSQueryTokenizer::TOK_ALL) {
436 $tok = $p->next();
437 if (($tok === CSSQueryTokenizer::TOK_PIPE) && ($tok = $p->next()) && ($tok !== CSSQueryTokenizer::TOK_ALL)) {
438 if (($tag = $this->parse_getIdentifier()) === false) {
439 return false;
440 }
441 $conditions['tags'][] = array(
442 'tag' => $tag,
443 'compare' => 'name'
444 );
445 $tok = $p->next_no_whitespace();
446 } else {
447 $conditions['tags'][''] = array(
448 'tag' => '',
449 'match' => false
450 );
451 if ($tok === CSSQueryTokenizer::TOK_ALL) {
452 $tok = $p->next_no_whitespace();
453 }
454 }
455 } elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
456 $tok = $p->next();
457 if ($tok === CSSQueryTokenizer::TOK_ALL) {
458 $conditions['tags'][] = array(
459 'tag' => '',
460 'compare' => 'namespace',
461 );
462 } elseif (($tag = $this->parse_getIdentifier()) !== false) {
463 $conditions['tags'][] = array(
464 'tag' => $tag,
465 'compare' => 'total',
466 );
467 } else {
468 return false;
469 }
470 $tok = $p->next_no_whitespace();
471 } elseif ($tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
472 $tok = $p->next_no_whitespace();
473 $last_mode = 'or';
474
475 while (true) {
476 $match = true;
477 $compare = 'total';
478
479 if ($tok === CSSQueryTokenizer::TOK_NOT) {
480 $match = false;
481 $tok = $p->next_no_whitespace();
482 }
483
484 if ($tok === CSSQueryTokenizer::TOK_ALL) {
485 $tok = $p->next();
486 if ($tok === CSSQueryTokenizer::TOK_PIPE) {
487 $this->next();
488 $compare = 'name';
489 if (($tag = $this->parse_getIdentifier()) === false) {
490 return false;
491 }
492 }
493 } elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
494 $tok = $p->next();
495 if ($tok === CSSQueryTokenizer::TOK_ALL) {
496 $tag = '';
497 $compare = 'namespace';
498 } elseif (($tag = $this->parse_getIdentifier()) === false) {
499 return false;
500 }
501 $tok = $p->next_no_whitespace();
502 } else {
503 if (($tag = $this->parse_getIdentifier()) === false) {
504 return false;
505 }
506 $tok = $p->next();
507 if ($tok === CSSQueryTokenizer::TOK_PIPE) {
508 $tok = $p->next();
509
510 if ($tok === CSSQueryTokenizer::TOK_ALL) {
511 $compare = 'namespace';
512 } elseif (($tag_name = $this->parse_getIdentifier()) !== false) {
513 $tag = $tag.':'.$tag_name;
514 } else {
515 return false;
516 }
517
518 $tok = $p->next_no_whitespace();
519 }
520 }
521 if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
522 $tok = $p->next_no_whitespace();
523 }
524
525 $conditions['tags'][] = array(
526 'tag' => $tag,
527 'match' => $match,
528 'operator' => $last_mode,
529 'compare' => $compare
530 );
531 switch($tok) {
532 case CSSQueryTokenizer::TOK_COMMA:
533 $tok = $p->next_no_whitespace();
534 $last_mode = 'or';
535 continue 2;
536 case CSSQueryTokenizer::TOK_PLUS:
537 $tok = $p->next_no_whitespace();
538 $last_mode = 'and';
539 continue 2;
540 case CSSQueryTokenizer::TOK_BRACE_CLOSE:
541 $tok = $p->next();
542 break 2;
543 default:
544 $this->error('Expected closing brace or comma at pos %pos%!');
545 return false;
546 }
547 }
548 } elseif (($tag = $this->parse_getIdentifier(false)) !== false) {
549 $tok = $p->next();
550 if ($tok === CSSQueryTokenizer::TOK_PIPE) {
551 $tok = $p->next();
552
553 if ($tok === CSSQueryTokenizer::TOK_ALL) {
554 $conditions['tags'][] = array(
555 'tag' => $tag,
556 'compare' => 'namespace'
557 );
558 } elseif (($tag_name = $this->parse_getIdentifier()) !== false) {
559 $tag = $tag.':'.$tag_name;
560 $conditions['tags'][] = array(
561 'tag' => $tag,
562 'match' => true
563 );
564 } else {
565 return false;
566 }
567
568 $tok = $p->next();
569 } elseif ($tag === 'text' && $tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
570 $pos = $p->getPos();
571 $tok = $p->next();
572 if ($tok === CSSQueryTokenizer::TOK_BRACE_CLOSE) {
573 $conditions['tags'][] = array(
574 'tag' => '~text~',
575 'match' => true
576 );
577 $p->next();
578 } else {
579 $p->setPos($pos);
580 }
581 } else {
582 $conditions['tags'][] = array(
583 'tag' => $tag,
584 'match' => true
585 );
586 }
587 } else {
588 unset($conditions['tags']);
589 }
590
591 //Class
592 $last_mode = 'or';
593 if ($tok === CSSQueryTokenizer::TOK_CLASS) {
594 $p->next();
595 if (($class = $this->parse_getIdentifier()) === false) {
596 return false;
597 }
598
599 $conditions['attributes'][] = array(
600 'attribute' => 'class',
601 'operator_value' => 'contains_word',
602 'value' => $class,
603 'operator_result' => $last_mode
604 );
605 $last_mode = 'and';
606 $tok = $p->next();
607 }
608
609 //ID
610 if ($tok === CSSQueryTokenizer::TOK_ID) {
611 $p->next();
612 if (($id = $this->parse_getIdentifier()) === false) {
613 return false;
614 }
615
616 $conditions['attributes'][] = array(
617 'attribute' => 'id',
618 'operator_value' => 'equals',
619 'value' => $id,
620 'operator_result' => $last_mode
621 );
622 $last_mode = 'and';
623 $tok = $p->next();
624 }
625
626 //Attributes
627 if ($tok === CSSQueryTokenizer::TOK_BRACKET_OPEN) {
628 $tok = $p->next_no_whitespace();
629
630 while (true) {
631 $match = true;
632 $compare = 'total';
633 if ($tok === CSSQueryTokenizer::TOK_NOT) {
634 $match = false;
635 $tok = $p->next_no_whitespace();
636 }
637
638 if ($tok === CSSQueryTokenizer::TOK_ALL) {
639 $tok = $p->next();
640 if ($tok === CSSQueryTokenizer::TOK_PIPE) {
641 $tok = $p->next();
642 if (($attribute = $this->parse_getIdentifier()) === false) {
643 return false;
644 }
645 $compare = 'name';
646 $tok = $p->next();
647 } else {
648 $this->error('Expected pipe at pos %pos%!');
649 return false;
650 }
651 } elseif ($tok === CSSQueryTokenizer::TOK_PIPE) {
652 $tok = $p->next();
653 if (($tag = $this->parse_getIdentifier()) === false) {
654 return false;
655 }
656 $tok = $p->next_no_whitespace();
657 } elseif (($attribute = $this->parse_getIdentifier()) !== false) {
658 $tok = $p->next();
659 if ($tok === CSSQueryTokenizer::TOK_PIPE) {
660 $tok = $p->next();
661
662 if (($attribute_name = $this->parse_getIdentifier()) !== false) {
663 $attribute = $attribute.':'.$attribute_name;
664 } else {
665 return false;
666 }
667
668 $tok = $p->next();
669 }
670 } else {
671 return false;
672 }
673 if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
674 $tok = $p->next_no_whitespace();
675 }
676
677 $operator_value = '';
678 $val = '';
679 switch($tok) {
680 case CSSQueryTokenizer::TOK_COMPARE_PREFIX:
681 case CSSQueryTokenizer::TOK_COMPARE_CONTAINS:
682 case CSSQueryTokenizer::TOK_COMPARE_CONTAINS_WORD:
683 case CSSQueryTokenizer::TOK_COMPARE_ENDS:
684 case CSSQueryTokenizer::TOK_COMPARE_EQUALS:
685 case CSSQueryTokenizer::TOK_COMPARE_NOT_EQUAL:
686 case CSSQueryTokenizer::TOK_COMPARE_REGEX:
687 case CSSQueryTokenizer::TOK_COMPARE_STARTS:
688 case CSSQueryTokenizer::TOK_COMPARE_BIGGER_THAN:
689 case CSSQueryTokenizer::TOK_COMPARE_SMALLER_THAN:
690 $operator_value = $p->getTokenString(($tok === CSSQueryTokenizer::TOK_COMPARE_EQUALS) ? 0 : -1);
691 $p->next_no_whitespace();
692
693 if (($val = $this->parse_getIdentifier()) === false) {
694 return false;
695 }
696
697 $tok = $p->next_no_whitespace();
698 break;
699 }
700
701 if ($operator_value && $val) {
702 $conditions['attributes'][] = array(
703 'attribute' => $attribute,
704 'operator_value' => $operator_value,
705 'value' => $val,
706 'match' => $match,
707 'operator_result' => $last_mode,
708 'compare' => $compare
709 );
710 } else {
711 $conditions['attributes'][] = array(
712 'attribute' => $attribute,
713 'value' => $match,
714 'operator_result' => $last_mode,
715 'compare' => $compare
716 );
717 }
718
719 switch($tok) {
720 case CSSQueryTokenizer::TOK_COMMA:
721 $tok = $p->next_no_whitespace();
722 $last_mode = 'or';
723 continue 2;
724 case CSSQueryTokenizer::TOK_PLUS:
725 $tok = $p->next_no_whitespace();
726 $last_mode = 'and';
727 continue 2;
728 case CSSQueryTokenizer::TOK_BRACKET_CLOSE:
729 $tok = $p->next();
730 break 2;
731 default:
732 $this->error('Expected closing bracket or comma at pos %pos%!');
733 return false;
734 }
735 }
736 }
737
738 if (count($conditions['attributes']) < 1) {
739 unset($conditions['attributes']);
740 }
741
742 while($tok === CSSQueryTokenizer::TOK_COLON) {
743 if (count($conditions) < 1) {
744 $conditions['tags'] = array(array(
745 'tag' => '',
746 'match' => false
747 ));
748 }
749
750 $tok = $p->next();
751 if (($filter = $this->parse_getIdentifier()) === false) {
752 return false;
753 }
754
755 if (($tok = $p->next()) === CSSQueryTokenizer::TOK_BRACE_OPEN) {
756 $start = $p->pos;
757 $count = 1;
758 while ((($tok = $p->next()) !== CSSQueryTokenizer::TOK_NULL) && !(($tok === CSSQueryTokenizer::TOK_BRACE_CLOSE) && (--$count === 0))) {
759 if ($tok === CSSQueryTokenizer::TOK_BRACE_OPEN) {
760 ++$count;
761 }
762 }
763
764
765 if ($tok !== CSSQueryTokenizer::TOK_BRACE_CLOSE) {
766 $this->error('Expected closing brace at pos %pos%!');
767 return false;
768 }
769 $len = $p->pos - 1 - $start;
770 $params = (($len > 0) ? substr($p->doc, $start + 1, $len) : '');
771 $tok = $p->next();
772 } else {
773 $params = '';
774 }
775
776 $conditions['filters'][] = array('filter' => $filter, 'params' => $params);
777 }
778 if (count($conditions) < 1) {
779 $this->error('Invalid search pattern(2): No conditions found!');
780 return false;
781 }
782 $conditions_all[] = $conditions;
783
784 if ($tok === CSSQueryTokenizer::TOK_WHITESPACE) {
785 $tok = $p->next_no_whitespace();
786 }
787
788 if ($tok === CSSQueryTokenizer::TOK_COMMA) {
789 $tok = $p->next_no_whitespace();
790 continue;
791 } else {
792 break;
793 }
794 }
795
796 return $conditions_all;
797 }
798
799
800 /**
801 * Evaluate root node using custom callback
802 * @param array $conditions {@link parse_conditions()}
803 * @param bool|int $recursive
804 * @param bool $check_root
805 * @return array
806 * @access private
807 */
808 protected function parse_callback($conditions, $recursive = true, $check_root = false) {
809 return ($this->result = $this->root->getChildrenByMatch(
810 $conditions,
811 $recursive,
812 $check_root,
813 $this->custom_filter_map
814 ));
815 }
816
817 /**
818 * Parse first bit of query, only root node has to be evaluated now
819 * @param bool|int $recursive
820 * @return bool
821 * @internal Result of query is set in {@link $result}
822 * @access private
823 */
824 protected function parse_single($recursive = true) {
825 if (($c = $this->parse_conditions()) === false) {
826 return false;
827 }
828
829 $this->parse_callback($c, $recursive, $this->search_root);
830 return true;
831 }
832
833 /**
834 * Evaluate sibling nodes
835 * @return bool
836 * @internal Result of query is set in {@link $result}
837 * @access private
838 */
839 protected function parse_adjacent() {
840 $tmp = $this->result;
841 $this->result = array();
842 if (($c = $this->parse_conditions()) === false) {
843 return false;
844 }
845
846 foreach($tmp as $t) {
847 if (($sibling = $t->getNextSibling()) !== false) {
848 if ($sibling->match($c, true, $this->custom_filter_map)) {
849 $this->result[] = $sibling;
850 }
851 }
852 }
853
854 return true;
855 }
856
857 /**
858 * Evaluate {@link $result}
859 * @param bool $parent Evaluate parent nodes
860 * @param bool|int $recursive
861 * @return bool
862 * @internal Result of query is set in {@link $result}
863 * @access private
864 */
865 protected function parse_result($parent = false, $recursive = true) {
866 $tmp = $this->result;
867 $tmp_res = array();
868 if (($c = $this->parse_conditions()) === false) {
869 return false;
870 }
871
872 foreach(array_keys($tmp) as $t) {
873 $this->root = (($parent) ? $tmp[$t]->parent : $tmp[$t]);
874 $this->parse_callback($c, $recursive);
875 foreach(array_keys($this->result) as $r) {
876 if (!in_array($this->result[$r], $tmp_res, true)) {
877 $tmp_res[] = $this->result[$r];
878 }
879 }
880 }
881 $this->result = $tmp_res;
882 return true;
883 }
884
885 /**
886 * Parse full query
887 * @return bool
888 * @internal Result of query is set in {@link $result}
889 * @access private
890 */
891 protected function parse() {
892 $p =& $this->parser;
893 $p->setPos(0);
894 $this->result = array();
895
896 if (!$this->parse_single()) {
897 return false;
898 }
899
900 while (count($this->result) > 0) {
901 switch($p->token) {
902 case CSSQueryTokenizer::TOK_CHILD:
903 $this->parser->next_no_whitespace();
904 if (!$this->parse_result(false, 1)) {
905 return false;
906 }
907 break;
908
909 case CSSQueryTokenizer::TOK_SIBLING:
910 $this->parser->next_no_whitespace();
911 if (!$this->parse_result(true, 1)) {
912 return false;
913 }
914 break;
915
916 case CSSQueryTokenizer::TOK_PLUS:
917 $this->parser->next_no_whitespace();
918 if (!$this->parse_adjacent()) {
919 return false;
920 }
921 break;
922
923 case CSSQueryTokenizer::TOK_ALL:
924 case CSSQueryTokenizer::TOK_IDENTIFIER:
925 case CSSQueryTokenizer::TOK_STRING:
926 case CSSQueryTokenizer::TOK_BRACE_OPEN:
927 case CSSQueryTokenizer::TOK_BRACKET_OPEN:
928 case CSSQueryTokenizer::TOK_ID:
929 case CSSQueryTokenizer::TOK_CLASS:
930 case CSSQueryTokenizer::TOK_COLON:
931 if (!$this->parse_result()) {
932 return false;
933 }
934 break;
935
936 case CSSQueryTokenizer::TOK_NULL:
937 break 2;
938
939 default:
940 $this->error('Invalid search pattern(3): No result modifier found!');
941 return false;
942 }
943 }
944
945 return true;
946 }
947 }
948
949 ?>
950