1 """SelectorList is a list of CSS Selector objects.
2
3 TODO
4 - remove duplicate Selectors. -> CSSOM canonicalize
5
6 - ??? CSS2 gives a special meaning to the comma (,) in selectors.
7 However, since it is not known if the comma may acquire other
8 meanings in future versions of CSS, the whole statement should be
9 ignored if there is an error anywhere in the selector, even though
10 the rest of the selector may look reasonable in CSS2.
11
12 Illegal example(s):
13
14 For example, since the "&" is not a valid token in a CSS2 selector,
15 a CSS2 user agent must ignore the whole second line, and not set
16 the color of H3 to red:
17 """
18 __all__ = ['SelectorList']
19 __docformat__ = 'restructuredtext'
20 __author__ = '$LastChangedBy: cthedot $'
21 __date__ = '$LastChangedDate: 2008-01-29 20:11:57 +0100 (Di, 29 Jan 2008) $'
22 __version__ = '$LastChangedRevision: 959 $'
23
24 import xml.dom
25 import cssutils
26 from selector import Selector
27
28 -class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
29 """
30 (cssutils) a list of Selectors of a CSSStyleRule
31
32 Properties
33 ==========
34 length: of type unsigned long, readonly
35 The number of Selector elements in the list.
36 parentRule: of type CSSRule, readonly
37 The CSS rule that contains this selector list or None if this
38 list is not attached to a CSSRule.
39 selectorText: of type DOMString
40 The textual representation of the selector for the rule set. The
41 implementation may have stripped out insignificant whitespace while
42 parsing the selector.
43 seq: (internal use!)
44 A list of Selector objects
45 wellformed
46 if this selectorlist is wellformed regarding the Selector spec
47 """
48 - def __init__(self, selectorText=None, parentRule=None,
49 readonly=False):
50 """
51 initializes SelectorList with optional selectorText
52
53 :Parameters:
54 selectorText
55 parsable list of Selectors
56 parentRule
57 the parent CSSRule if available
58 """
59 super(SelectorList, self).__init__()
60
61 self._parentRule = parentRule
62 self.wellformed = False
63
64 if selectorText:
65 self.selectorText = selectorText
66
67 self._readonly = readonly
68
80
82 """
83 overwrites ListSeq.__setitem__
84
85 Any duplicate Selectors are **not** removed.
86 """
87 newSelector = self.__prepareset(newSelector)
88 if newSelector:
89 self.seq[index] = newSelector
90
91 - def append(self, newSelector):
94
95 length = property(lambda self: len(self),
96 doc="The number of Selector elements in the list.")
97
98
108
109 _namespaces = property(__getNamespaces, doc="""if this SelectorList is
110 attached to a CSSStyleSheet the namespaces of that sheet are mirrored
111 here. While the SelectorList (or parentRule(s) are
112 not attached the namespaces of all children Selectors are used.""")
113
114 parentRule = property(lambda self: self._parentRule,
115 doc="(DOM) The CSS rule that contains this SelectorList or\
116 None if this SelectorList is not attached to a CSSRule.")
117
119 "returns serialized format"
120 return cssutils.ser.do_css_SelectorList(self)
121
122 - def _setSelectorText(self, selectorText):
123 """
124 :param selectorText:
125 comma-separated list of selectors or a tuple of
126 (selectorText, dict-of-namespaces)
127 :Exceptions:
128 - `NAMESPACE_ERR`: (Selector)
129 Raised if the specified selector uses an unknown namespace
130 prefix.
131 - `SYNTAX_ERR`: (self)
132 Raised if the specified CSS string value has a syntax error
133 and is unparsable.
134 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
135 Raised if this rule is readonly.
136 """
137 self._checkReadonly()
138
139
140 selectorText, namespaces = self._splitNamespacesOff(selectorText)
141 try:
142
143 namespaces = self.parentRule.parentStyleSheet.namespaces
144 except AttributeError:
145 pass
146
147 wellformed = True
148 tokenizer = self._tokenize2(selectorText)
149 newseq = []
150
151 expected = True
152 while True:
153
154 selectortokens = self._tokensupto2(tokenizer, listseponly=True)
155 if selectortokens:
156 if self._tokenvalue(selectortokens[-1]) == ',':
157 expected = selectortokens.pop()
158 else:
159 expected = None
160
161 selector = Selector((selectortokens, namespaces),
162 parentList=self)
163 if selector.wellformed:
164 newseq.append(selector)
165 else:
166 wellformed = False
167 self._log.error(u'SelectorList: Invalid Selector: %s' %
168 self._valuestr(selectortokens))
169 else:
170 break
171
172
173 if u',' == expected:
174 wellformed = False
175 self._log.error(u'SelectorList: Cannot end with ",": %r' %
176 self._valuestr(selectorText))
177 elif expected:
178 wellformed = False
179 self._log.error(u'SelectorList: Unknown Syntax: %r' %
180 self._valuestr(selectorText))
181 if wellformed:
182 self.seq = newseq
183 self.wellformed = wellformed
184
185
186
187 selectorText = property(_getSelectorText, _setSelectorText,
188 doc="""(cssutils) The textual representation of the selector for
189 a rule set.""")
190
191
193 """
194 Append newSelector (a string will be converted to a new
195 Selector).
196
197 :param newSelector:
198 comma-separated list of selectors or a tuple of
199 (selectorText, dict-of-namespaces)
200 :returns: New Selector or None if newSelector is not wellformed.
201 :Exceptions:
202 - `NAMESPACE_ERR`: (self)
203 Raised if the specified selector uses an unknown namespace
204 prefix.
205 - `SYNTAX_ERR`: (self)
206 Raised if the specified CSS string value has a syntax error
207 and is unparsable.
208 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
209 Raised if this rule is readonly.
210 """
211 self._checkReadonly()
212
213
214 newSelector, namespaces = self._splitNamespacesOff(newSelector)
215 try:
216
217 namespaces = self.parentRule.parentStyleSheet.namespaces
218 except AttributeError:
219
220 _namespaces = self._namespaces
221 _namespaces.update(namespaces)
222 namespaces = _namespaces
223
224 newSelector = self.__prepareset(newSelector, namespaces)
225 if newSelector:
226 self.wellformed = True
227 seq = self.seq[:]
228 del self.seq[:]
229 for s in seq:
230 if s.selectorText != newSelector.selectorText:
231 self.seq.append(s)
232 self.seq.append(newSelector)
233 return newSelector
234
242
244 return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
245 self.__class__.__name__, self.selectorText, self._namespaces,
246 id(self))
247
249 "used by CSSStyleSheet to check if @namespace rules are needed"
250 uris = set()
251 for s in self:
252 uris.update(s._getUsedUris())
253 return uris
254