Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
namespace Sabberworm\CSS\Property;
4
 
5
/**
6
 * Class representing a single CSS selector. Selectors have to be split by the comma prior to being passed into this
7
 * class.
8
 */
9
class Selector
10
{
11
    /**
12
     * regexp for specificity calculations
13
     *
14
     * @var string
15
     */
16
    const NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX = '/
17
        (\.[\w]+)                   # classes
18
        |
19
        \[(\w+)                     # attributes
20
        |
21
        (\:(                        # pseudo classes
22
            link|visited|active
23
            |hover|focus
24
            |lang
25
            |target
26
            |enabled|disabled|checked|indeterminate
27
            |root
28
            |nth-child|nth-last-child|nth-of-type|nth-last-of-type
29
            |first-child|last-child|first-of-type|last-of-type
30
            |only-child|only-of-type
31
            |empty|contains
32
        ))
33
        /ix';
34
 
35
    /**
36
     * regexp for specificity calculations
37
     *
38
     * @var string
39
     */
40
    const ELEMENTS_AND_PSEUDO_ELEMENTS_RX = '/
41
        ((^|[\s\+\>\~]+)[\w]+   # elements
42
        |
43
        \:{1,2}(                # pseudo-elements
44
            after|before|first-letter|first-line|selection
45
        ))
46
        /ix';
47
 
48
    /**
49
     * regexp for specificity calculations
50
     *
51
     * @var string
52
     */
53
    const SELECTOR_VALIDATION_RX = '/
54
        ^(
55
            (?:
56
                [a-zA-Z0-9\x{00A0}-\x{FFFF}_^$|*="\'~\[\]()\-\s\.:#+>]* # any sequence of valid unescaped characters
57
                (?:\\\\.)?                                              # a single escaped character
58
                (?:([\'"]).*?(?<!\\\\)\2)?                              # a quoted text like [id="example"]
59
            )*
60
        )$
61
        /ux';
62
 
63
    /**
64
     * @var string
65
     */
66
    private $sSelector;
67
 
68
    /**
69
     * @var int|null
70
     */
71
    private $iSpecificity;
72
 
73
    /**
74
     * @param string $sSelector
75
     *
76
     * @return bool
77
     */
78
    public static function isValid($sSelector)
79
    {
80
        return preg_match(static::SELECTOR_VALIDATION_RX, $sSelector);
81
    }
82
 
83
    /**
84
     * @param string $sSelector
85
     * @param bool $bCalculateSpecificity
86
     */
87
    public function __construct($sSelector, $bCalculateSpecificity = false)
88
    {
89
        $this->setSelector($sSelector);
90
        if ($bCalculateSpecificity) {
91
            $this->getSpecificity();
92
        }
93
    }
94
 
95
    /**
96
     * @return string
97
     */
98
    public function getSelector()
99
    {
100
        return $this->sSelector;
101
    }
102
 
103
    /**
104
     * @param string $sSelector
105
     *
106
     * @return void
107
     */
108
    public function setSelector($sSelector)
109
    {
110
        $this->sSelector = trim($sSelector);
111
        $this->iSpecificity = null;
112
    }
113
 
114
    /**
115
     * @return string
116
     */
117
    public function __toString()
118
    {
119
        return $this->getSelector();
120
    }
121
 
122
    /**
123
     * @return int
124
     */
125
    public function getSpecificity()
126
    {
127
        if ($this->iSpecificity === null) {
128
            $a = 0;
129
            /// @todo should exclude \# as well as "#"
130
            $aMatches = null;
131
            $b = substr_count($this->sSelector, '#');
132
            $c = preg_match_all(self::NON_ID_ATTRIBUTES_AND_PSEUDO_CLASSES_RX, $this->sSelector, $aMatches);
133
            $d = preg_match_all(self::ELEMENTS_AND_PSEUDO_ELEMENTS_RX, $this->sSelector, $aMatches);
134
            $this->iSpecificity = ($a * 1000) + ($b * 100) + ($c * 10) + $d;
135
        }
136
        return $this->iSpecificity;
137
    }
138
}