@@ -180,7 +180,7 @@ private function parseCharacter($bIsForIdentifier) {
180
180
}
181
181
182
182
private function parseSelector () {
183
- $ oResult = new CSSSelector ();
183
+ $ oResult = new CSSDeclarationBlock ();
184
184
$ oResult ->setSelector ($ this ->consumeUntil ('{ ' ));
185
185
$ this ->consume ('{ ' );
186
186
$ this ->consumeWhiteSpace ();
@@ -425,12 +425,12 @@ public function getContents() {
425
425
return $ this ->aContents ;
426
426
}
427
427
428
- protected function allSelectors (&$ aResult ) {
428
+ protected function allDeclarationBlocks (&$ aResult ) {
429
429
foreach ($ this ->aContents as $ mContent ) {
430
- if ($ mContent instanceof CSSSelector ) {
430
+ if ($ mContent instanceof CSSDeclarationBlock ) {
431
431
$ aResult [] = $ mContent ;
432
432
} else if ($ mContent instanceof CSSList) {
433
- $ mContent ->allSelectors ($ aResult );
433
+ $ mContent ->allDeclarationBlocks ($ aResult );
434
434
}
435
435
}
436
436
}
@@ -462,14 +462,37 @@ protected function allValues($oElement, &$aResult, $sSearchString = null) {
462
462
}
463
463
}
464
464
}
465
+
466
+ protected function allSelectors (&$ aResult , $ sSpecificitySearch = null ) {
467
+ foreach ($ this ->getAllDeclarationBlocks () as $ oBlock ) {
468
+ foreach ($ oBlock ->getSelectors () as $ oSelector ) {
469
+ if ($ sSpecificitySearch === null ) {
470
+ $ aResult [] = $ oSelector ;
471
+ } else {
472
+ $ sComparison = "\$bRes = {$ oSelector ->getSpecificity ()} $ sSpecificitySearch; " ;
473
+ eval ($ sComparison );
474
+ if ($ bRes ) {
475
+ $ aResult [] = $ oSelector ;
476
+ }
477
+ }
478
+ }
479
+ }
480
+ }
465
481
}
466
482
467
483
class CSSDocument extends CSSList {
468
- public function getAllSelectors () {
484
+ public function getAllDeclarationBlocks () {
469
485
$ aResult = array ();
470
- $ this ->allSelectors ($ aResult );
486
+ $ this ->allDeclarationBlocks ($ aResult );
471
487
return $ aResult ;
472
488
}
489
+
490
+ /**
491
+ * @deprecated use getAllSelectors()
492
+ */
493
+ public function getAllSelectors () {
494
+ return $ this ->getAllDeclarationBlocks ();
495
+ }
473
496
474
497
public function getAllRuleSets () {
475
498
$ aResult = array ();
@@ -489,6 +512,15 @@ public function getAllValues($mElement = null) {
489
512
$ this ->allValues ($ mElement , $ aResult , $ sSearchString );
490
513
return $ aResult ;
491
514
}
515
+
516
+ public function getSelectorsBySpecificity ($ sSpecificitySearch = null ) {
517
+ if (is_numeric ($ sSpecificitySearch ) || is_numeric ($ sSpecificitySearch [0 ])) {
518
+ $ sSpecificitySearch = "== $ sSpecificitySearch " ;
519
+ }
520
+ $ aResult = array ();
521
+ $ this ->allSelectors ($ aResult , $ sSpecificitySearch );
522
+ return $ aResult ;
523
+ }
492
524
}
493
525
494
526
class CSSMediaQuery extends CSSList {
@@ -630,37 +662,120 @@ public function __toString() {
630
662
}
631
663
}
632
664
633
- class CSSSelector extends CSSRuleSet {
634
- private $ aSelector ;
635
-
665
+ class CSSDeclarationBlock extends CSSRuleSet {
666
+ private $ aSelectors ;
667
+
636
668
public function __construct () {
637
669
parent ::__construct ();
638
- $ this ->aSelector = array ();
670
+ $ this ->aSelectors = array ();
639
671
}
640
-
641
- public function setSelector ($ mSelector ) {
672
+
673
+ public function setSelectors ($ mSelector ) {
642
674
if (is_array ($ mSelector )) {
643
- $ this ->aSelector = $ mSelector ;
675
+ $ this ->aSelectors = $ mSelector ;
644
676
} else {
645
- $ this ->aSelector = explode (', ' , $ mSelector );
677
+ $ this ->aSelectors = explode (', ' , $ mSelector );
646
678
}
647
- foreach ($ this ->aSelector as $ iKey => $ sSelector ) {
648
- $ this ->aSelector [$ iKey ] = trim ($ sSelector );
679
+ foreach ($ this ->aSelectors as $ iKey => $ mSelector ) {
680
+ if (!($ mSelector instanceof CSSSelector)) {
681
+ $ this ->aSelectors [$ iKey ] = new CSSSelector ($ mSelector );
682
+ }
649
683
}
650
684
}
651
685
686
+ /**
687
+ * @deprecated use getSelectors()
688
+ */
652
689
public function getSelector () {
653
- return $ this ->aSelector ;
690
+ $ this ->getSelectors ();
691
+ }
692
+
693
+ /**
694
+ * @deprecated use setSelectors()
695
+ */
696
+ public function setSelector ($ mSelector ) {
697
+ $ this ->setSelectors ($ mSelector );
698
+ }
699
+
700
+ public function getSelectors () {
701
+ return $ this ->aSelectors ;
654
702
}
655
703
656
704
public function __toString () {
657
- $ sResult = implode (', ' , $ this ->aSelector ).' { ' ;
705
+ $ sResult = implode (', ' , $ this ->aSelectors ).' { ' ;
658
706
$ sResult .= parent ::__toString ();
659
707
$ sResult .= '} ' ;
660
708
return $ sResult ;
661
709
}
662
710
}
663
711
712
+ class CSSSelector {
713
+ const
714
+ NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
715
+ (\.[\w]+) # classes
716
+ |
717
+ \[(\w+) # attributes
718
+ |
719
+ (\:( # pseudo classes
720
+ link|visited|active
721
+ |hover|focus
722
+ |lang
723
+ |target
724
+ |enabled|disabled|checked|indeterminate
725
+ |root
726
+ |nth-child|nth-last-child|nth-of-type|nth-last-of-type
727
+ |first-child|last-child|first-of-type|last-of-type
728
+ |only-child|only-of-type
729
+ |empty|contains
730
+ ))
731
+ /ix ' ,
732
+ ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
733
+ ((^|[\s\+\>\~]+)[\w]+ # elements
734
+ |
735
+ \:{1,2}( # pseudo-elements
736
+ after|before
737
+ |first-letter|first-line
738
+ |selection
739
+ )
740
+ )/ix ' ;
741
+
742
+ private $ sSelector ;
743
+ private $ iSpecificity ;
744
+
745
+ public function __construct ($ sSelector , $ bCalculateSpecificity = false ) {
746
+ $ this ->setSelector ($ sSelector );
747
+ if ($ bCalculateSpecificity ) {
748
+ $ this ->getSpecificity ();
749
+ }
750
+ }
751
+
752
+ public function getSelector () {
753
+ return $ this ->sSelector ;
754
+ }
755
+
756
+ public function setSelector ($ sSelector ) {
757
+ $ this ->sSelector = trim ($ sSelector );
758
+ $ this ->iSpecificity = null ;
759
+ }
760
+
761
+ public function __toString () {
762
+ return $ this ->getSelector ();
763
+ }
764
+
765
+ public function getSpecificity () {
766
+ if ($ this ->iSpecificity === null ) {
767
+ $ a = 0 ;
768
+ /// @todo should exclude \# as well as "#"
769
+ $ aMatches ;
770
+ $ b = substr_count ($ this ->sSelector , '# ' );
771
+ $ c = preg_match_all (self ::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX , $ this ->sSelector , $ aMatches );
772
+ $ d = preg_match_all (self ::ELEMENTS_AND_PSEUDO_ELEMENTS_RX , $ this ->sSelector , $ aMatches );
773
+ $ this ->iSpecificity = ($ a *1000 ) + ($ b *100 ) + ($ c *10 ) + $ d ;
774
+ }
775
+ return $ this ->iSpecificity ;
776
+ }
777
+ }
778
+
664
779
class CSSRule {
665
780
private $ sRule ;
666
781
private $ aValues ;
0 commit comments