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-02-12 21:49:54 +0100 (Di, 12 Feb 2008) $'
22 __version__ = '$LastChangedRevision: 1043 $'
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
63 if selectorText:
64 self.selectorText = selectorText
65
66 self._readonly = readonly
67
79
81 """
82 overwrites ListSeq.__setitem__
83
84 Any duplicate Selectors are **not** removed.
85 """
86 newSelector = self.__prepareset(newSelector)
87 if newSelector:
88 self.seq[index] = newSelector
89
90 - def append(self, newSelector):
93
94 length = property(lambda self: len(self),
95 doc="The number of Selector elements in the list.")
96
97
107
108 _namespaces = property(__getNamespaces, doc="""if this SelectorList is
109 attached to a CSSStyleSheet the namespaces of that sheet are mirrored
110 here. While the SelectorList (or parentRule(s) are
111 not attached the namespaces of all children Selectors are used.""")
112
113 parentRule = property(lambda self: self._parentRule,
114 doc="(DOM) The CSS rule that contains this SelectorList or\
115 None if this SelectorList is not attached to a CSSRule.")
116
118 "returns serialized format"
119 return cssutils.ser.do_css_SelectorList(self)
120
121 - def _setSelectorText(self, selectorText):
122 """
123 :param selectorText:
124 comma-separated list of selectors or a tuple of
125 (selectorText, dict-of-namespaces)
126 :Exceptions:
127 - `NAMESPACE_ERR`: (Selector)
128 Raised if the specified selector uses an unknown namespace
129 prefix.
130 - `SYNTAX_ERR`: (self)
131 Raised if the specified CSS string value has a syntax error
132 and is unparsable.
133 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
134 Raised if this rule is readonly.
135 """
136 self._checkReadonly()
137
138
139 selectorText, namespaces = self._splitNamespacesOff(selectorText)
140 try:
141
142 namespaces = self.parentRule.parentStyleSheet.namespaces
143 except AttributeError:
144 pass
145
146 wellformed = True
147 tokenizer = self._tokenize2(selectorText)
148 newseq = []
149
150 expected = True
151 while True:
152
153 selectortokens = self._tokensupto2(tokenizer, listseponly=True)
154 if selectortokens:
155 if self._tokenvalue(selectortokens[-1]) == ',':
156 expected = selectortokens.pop()
157 else:
158 expected = None
159
160 selector = Selector((selectortokens, namespaces),
161 parentList=self)
162 if selector.wellformed:
163 newseq.append(selector)
164 else:
165 wellformed = False
166 self._log.error(u'SelectorList: Invalid Selector: %s' %
167 self._valuestr(selectortokens))
168 else:
169 break
170
171
172 if u',' == expected:
173 wellformed = False
174 self._log.error(u'SelectorList: Cannot end with ",": %r' %
175 self._valuestr(selectorText))
176 elif expected:
177 wellformed = False
178 self._log.error(u'SelectorList: Unknown Syntax: %r' %
179 self._valuestr(selectorText))
180 if wellformed:
181 self.seq = newseq
182
183
184
185 selectorText = property(_getSelectorText, _setSelectorText,
186 doc="""(cssutils) The textual representation of the selector for
187 a rule set.""")
188
189 wellformed = property(lambda self: bool(len(self.seq)))
190
192 """
193 Append newSelector (a string will be converted to a new
194 Selector).
195
196 :param newSelector:
197 comma-separated list of selectors or a tuple of
198 (selectorText, dict-of-namespaces)
199 :returns: New Selector or None if newSelector is not wellformed.
200 :Exceptions:
201 - `NAMESPACE_ERR`: (self)
202 Raised if the specified selector uses an unknown namespace
203 prefix.
204 - `SYNTAX_ERR`: (self)
205 Raised if the specified CSS string value has a syntax error
206 and is unparsable.
207 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
208 Raised if this rule is readonly.
209 """
210 self._checkReadonly()
211
212
213 newSelector, namespaces = self._splitNamespacesOff(newSelector)
214 try:
215
216 namespaces = self.parentRule.parentStyleSheet.namespaces
217 except AttributeError:
218
219 _namespaces = self._namespaces
220 _namespaces.update(namespaces)
221 namespaces = _namespaces
222
223 newSelector = self.__prepareset(newSelector, namespaces)
224 if newSelector:
225 seq = self.seq[:]
226 del self.seq[:]
227 for s in seq:
228 if s.selectorText != newSelector.selectorText:
229 self.seq.append(s)
230 self.seq.append(newSelector)
231 return newSelector
232
240
242 return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
243 self.__class__.__name__, self.selectorText, self._namespaces,
244 id(self))
245
247 "used by CSSStyleSheet to check if @namespace rules are needed"
248 uris = set()
249 for s in self:
250 uris.update(s._getUsedUris())
251 return uris
252