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 __version__ = '$Id: selectorlist.py 1174 2008-03-20 17:43:07Z cthedot $'
21
22 import xml.dom
23 import cssutils
24 from selector import Selector
25
26 -class SelectorList(cssutils.util.Base, cssutils.util.ListSeq):
27 """
28 (cssutils) a list of Selectors of a CSSStyleRule
29
30 Properties
31 ==========
32 length: of type unsigned long, readonly
33 The number of Selector elements in the list.
34 parentRule: of type CSSRule, readonly
35 The CSS rule that contains this selector list or None if this
36 list is not attached to a CSSRule.
37 selectorText: of type DOMString
38 The textual representation of the selector for the rule set. The
39 implementation may have stripped out insignificant whitespace while
40 parsing the selector.
41 seq: (internal use!)
42 A list of Selector objects
43 wellformed
44 if this selectorlist is wellformed regarding the Selector spec
45 """
46 - def __init__(self, selectorText=None, parentRule=None,
47 readonly=False):
48 """
49 initializes SelectorList with optional selectorText
50
51 :Parameters:
52 selectorText
53 parsable list of Selectors
54 parentRule
55 the parent CSSRule if available
56 """
57 super(SelectorList, self).__init__()
58
59 self._parentRule = parentRule
60
61 if selectorText:
62 self.selectorText = selectorText
63
64 self._readonly = readonly
65
77
79 """
80 overwrites ListSeq.__setitem__
81
82 Any duplicate Selectors are **not** removed.
83 """
84 newSelector = self.__prepareset(newSelector)
85 if newSelector:
86 self.seq[index] = newSelector
87
88 - def append(self, newSelector):
89 "same as appendSelector(newSelector)"
90 self.appendSelector(newSelector)
91
92 length = property(lambda self: len(self),
93 doc="The number of Selector elements in the list.")
94
95
105
106 _namespaces = property(__getNamespaces, doc="""if this SelectorList is
107 attached to a CSSStyleSheet the namespaces of that sheet are mirrored
108 here. While the SelectorList (or parentRule(s) are
109 not attached the namespaces of all children Selectors are used.""")
110
111 parentRule = property(lambda self: self._parentRule,
112 doc="(DOM) The CSS rule that contains this SelectorList or\
113 None if this SelectorList is not attached to a CSSRule.")
114
116 "returns serialized format"
117 return cssutils.ser.do_css_SelectorList(self)
118
119 - def _setSelectorText(self, selectorText):
120 """
121 :param selectorText:
122 comma-separated list of selectors or a tuple of
123 (selectorText, dict-of-namespaces)
124 :Exceptions:
125 - `NAMESPACE_ERR`: (Selector)
126 Raised if the specified selector uses an unknown namespace
127 prefix.
128 - `SYNTAX_ERR`: (self)
129 Raised if the specified CSS string value has a syntax error
130 and is unparsable.
131 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
132 Raised if this rule is readonly.
133 """
134 self._checkReadonly()
135
136
137 selectorText, namespaces = self._splitNamespacesOff(selectorText)
138 try:
139
140 namespaces = self.parentRule.parentStyleSheet.namespaces
141 except AttributeError:
142 pass
143
144 wellformed = True
145 tokenizer = self._tokenize2(selectorText)
146 newseq = []
147
148 expected = True
149 while True:
150
151 selectortokens = self._tokensupto2(tokenizer, listseponly=True)
152 if selectortokens:
153 if self._tokenvalue(selectortokens[-1]) == ',':
154 expected = selectortokens.pop()
155 else:
156 expected = None
157
158 selector = Selector((selectortokens, namespaces),
159 parentList=self)
160 if selector.wellformed:
161 newseq.append(selector)
162 else:
163 wellformed = False
164 self._log.error(u'SelectorList: Invalid Selector: %s' %
165 self._valuestr(selectortokens))
166 else:
167 break
168
169
170 if u',' == expected:
171 wellformed = False
172 self._log.error(u'SelectorList: Cannot end with ",": %r' %
173 self._valuestr(selectorText))
174 elif expected:
175 wellformed = False
176 self._log.error(u'SelectorList: Unknown Syntax: %r' %
177 self._valuestr(selectorText))
178 if wellformed:
179 self.seq = newseq
180
181
182
183 selectorText = property(_getSelectorText, _setSelectorText,
184 doc="""(cssutils) The textual representation of the selector for
185 a rule set.""")
186
187 wellformed = property(lambda self: bool(len(self.seq)))
188
190 """
191 Append newSelector (a string will be converted to a new
192 Selector).
193
194 :param newSelector:
195 comma-separated list of selectors or a tuple of
196 (selectorText, dict-of-namespaces)
197 :returns: New Selector or None if newSelector is not wellformed.
198 :Exceptions:
199 - `NAMESPACE_ERR`: (self)
200 Raised if the specified selector uses an unknown namespace
201 prefix.
202 - `SYNTAX_ERR`: (self)
203 Raised if the specified CSS string value has a syntax error
204 and is unparsable.
205 - `NO_MODIFICATION_ALLOWED_ERR`: (self)
206 Raised if this rule is readonly.
207 """
208 self._checkReadonly()
209
210
211 newSelector, namespaces = self._splitNamespacesOff(newSelector)
212 try:
213
214 namespaces = self.parentRule.parentStyleSheet.namespaces
215 except AttributeError:
216
217 _namespaces = self._namespaces
218 _namespaces.update(namespaces)
219 namespaces = _namespaces
220
221 newSelector = self.__prepareset(newSelector, namespaces)
222 if newSelector:
223 seq = self.seq[:]
224 del self.seq[:]
225 for s in seq:
226 if s.selectorText != newSelector.selectorText:
227 self.seq.append(s)
228 self.seq.append(newSelector)
229 return newSelector
230
238
240 return "<cssutils.css.%s object selectorText=%r _namespaces=%r at 0x%x>" % (
241 self.__class__.__name__, self.selectorText, self._namespaces,
242 id(self))
243
245 "used by CSSStyleSheet to check if @namespace rules are needed"
246 uris = set()
247 for s in self:
248 uris.update(s._getUsedUris())
249 return uris
250