Package cssutils :: Package css :: Module cssstylesheet
[hide private]
[frames] | no frames]

Source Code for Module cssutils.css.cssstylesheet

  1  """ 
  2  CSSStyleSheet implements DOM Level 2 CSS CSSStyleSheet. 
  3   
  4  Partly also: 
  5      http://www.w3.org/TR/2006/WD-css3-namespace-20060828/ 
  6   
  7  TODO: 
  8      - ownerRule and ownerNode 
  9  """ 
 10  __all__ = ['CSSStyleSheet'] 
 11  __docformat__ = 'restructuredtext' 
 12  __author__ = '$LastChangedBy: doerwalter $' 
 13  __date__ = '$LastChangedDate: 2007-08-02 22:58:23 +0200 (Do, 02 Aug 2007) $' 
 14  __version__ = '0.9.2a5 $LastChangedRevision: 160 $' 
 15   
 16  import xml.dom 
 17   
 18  import cssutils.stylesheets 
 19  import cssutils.tokenize 
 20   
 21   
22 -class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
23 """ 24 The CSSStyleSheet interface is a concrete interface used to represent 25 a CSS style sheet i.e., a style sheet whose content type is 26 "text/css". 27 28 Properties 29 ========== 30 cssRules: of type CSSRuleList, (DOM readonly) 31 The list of all CSS rules contained within the style sheet. This 32 includes both rule sets and at-rules. 33 namespaces: set 34 A set of declared namespaces via @namespace rules. Each 35 CSSStyleRule is checked if it uses additional prefixes which are 36 not declared. If they are "invalidated". 37 ownerRule: of type CSSRule, readonly 38 If this style sheet comes from an @import rule, the ownerRule 39 attribute will contain the CSSImportRule. In that case, the 40 ownerNode attribute in the StyleSheet interface will be None. If 41 the style sheet comes from an element or a processing instruction, 42 the ownerRule attribute will be None and the ownerNode attribute 43 will contain the Node. 44 45 Inherits properties from stylesheet.StyleSheet 46 47 Format 48 ====== 49 stylesheet 50 : [ CHARSET_SYM S* STRING S* ';' ]? 51 [S|CDO|CDC]* [ import [S|CDO|CDC]* ]* 52 [ namespace [S|CDO|CDC]* ]* # according to @namespace WD 53 [ [ ruleset | media | page ] [S|CDO|CDC]* ]* 54 """ 55 type = 'text/css' 56
57 - def __init__(self, href=None, media=None, 58 title=u'', disabled=None, 59 ownerNode=None, parentStyleSheet=None, 60 readonly=False):
61 62 super(CSSStyleSheet, self).__init__( 63 self.type, href, media, title, disabled, 64 ownerNode, parentStyleSheet) 65 66 self.cssRules = cssutils.css.CSSRuleList() 67 self.namespaces = set() 68 self._readonly = readonly
69 70
71 - def __checknamespaces(self, stylerule, namespaces):
72 """ 73 checks if all namespaces used in stylerule have been declared 74 """ 75 notdeclared = set() 76 for s in stylerule.selectorList: 77 for prefix in s.namespaces: 78 if not prefix in namespaces: 79 notdeclared.add(prefix) 80 return notdeclared
81 82
83 - def _getCssText(self):
84 return cssutils.ser.do_stylesheet(self)
85
86 - def _setCssText(self, cssText):
87 """ 88 (cssutils) 89 Parses cssText and overwrites the whole stylesheet. 90 91 cssText 92 textual text to set 93 94 DOMException on setting 95 96 - NO_MODIFICATION_ALLOWED_ERR: (self) 97 Raised if the rule is readonly. 98 - SYNTAX_ERR: 99 Raised if the specified CSS string value has a syntax error and 100 is unparsable. 101 - NAMESPACE_ERR: 102 If a namespace prefix is found which is not declared. 103 """ 104 self._checkReadonly() 105 106 tokens = self._tokenize(cssText, _fullSheet=True) 107 108 newcssRules = cssutils.css.CSSRuleList() 109 newnamespaces = set() 110 111 # @charset only once at beginning 112 if tokens and tokens[0].type == self._ttypes.CHARSET_SYM: 113 charsetruletokens, endi = self._tokensupto(tokens) 114 charsetrule = cssutils.css.CSSCharsetRule() 115 charsetrule.cssText = charsetruletokens 116 newcssRules.append(charsetrule) 117 i = endi + 1 # ? 118 else: 119 i = 0 120 expected = '@import' # @namespace | any 121 imax = len(tokens) 122 while i < imax: 123 t = tokens[i] 124 125 if t.type == self._ttypes.EOF: 126 break 127 128 # ignore 129 if t.type in (self._ttypes.S, self._ttypes.CDO, self._ttypes.CDC): 130 pass 131 132 # unexpected: } ; 133 elif t.type in (self._ttypes.SEMICOLON, 134 self._ttypes.LBRACE, self._ttypes.RBRACE): 135 self._log.error(u'CSSStyleSheet: Syntax Error',t) 136 137 # simply add 138 elif self._ttypes.COMMENT == t.type: 139 newcssRules.append(cssutils.css.CSSComment(t)) 140 141 # @charset only at beginning 142 elif self._ttypes.CHARSET_SYM == t.type: 143 atruletokens, endi = self._tokensupto(tokens[i:]) 144 i += endi 145 self._log.error(u'CSSStylesheet: CSSCharsetRule only allowed at beginning of stylesheet.', 146 t, xml.dom.HierarchyRequestErr) 147 148 # @import before any StyleRule and @rule except @charset 149 elif self._ttypes.IMPORT_SYM == t.type: 150 atruletokens, endi = self._tokensupto(tokens[i:]) 151 if expected == '@import': 152 atrule = cssutils.css.CSSImportRule() 153 atrule.cssText = atruletokens 154 newcssRules.append(atrule) 155 else: 156 self._log.error( 157 u'CSSStylesheet: CSSImportRule not allowed here.', 158 t, xml.dom.HierarchyRequestErr) 159 i += endi 160 161 # @namespace before any StyleRule and 162 # before @rule except @charset, @import 163 elif self._ttypes.NAMESPACE_SYM == t.type: 164 atruletokens, endi = self._tokensupto(tokens[i:]) 165 if expected in ('@import', '@namespace'): 166 atrule = cssutils.css.CSSNamespaceRule() 167 atrule.cssText = atruletokens 168 newcssRules.append(atrule) 169 newnamespaces.add(atrule.prefix) 170 else: 171 self._log.error( 172 u'CSSStylesheet: CSSNamespaceRule not allowed here.', 173 t, xml.dom.HierarchyRequestErr) 174 i += endi 175 expected = '@namespace' # now no @import anymore! 176 177 # @media 178 elif self._ttypes.MEDIA_SYM == t.type: 179 atruletokens, endi = self._tokensupto(tokens[i:]) 180 atrule = cssutils.css.CSSMediaRule() 181 atrule.cssText = atruletokens 182 newcssRules.append(atrule) 183 i += endi 184 expected = 'any' 185 186 # @page 187 elif self._ttypes.PAGE_SYM == t.type: 188 atruletokens, endi = self._tokensupto(tokens[i:]) 189 atrule = cssutils.css.CSSPageRule() 190 atrule.cssText = atruletokens 191 newcssRules.append(atrule) 192 i += endi 193 expected = 'any' 194 195 # @unknown, insert in any case (but after @charset) 196 elif self._ttypes.ATKEYWORD == t.type: 197 atruletokens, endi = self._tokensupto(tokens[i:]) 198 atrule = cssutils.css.CSSUnknownRule() 199 atrule.cssText = atruletokens 200 newcssRules.append(atrule) 201 i += endi 202 203 else: # probable StyleRule 204 ruletokens, endi = self._tokensupto( 205 tokens[i:], blockendonly=True) 206 rule = cssutils.css.CSSStyleRule() 207 rule.cssText = ruletokens 208 notdeclared = self.__checknamespaces(rule, newnamespaces) 209 if notdeclared: 210 rule.valid = False 211 self._log.error( 212 u'CSSStylesheet: CSSStyleRule uses undeclared namespace prefixes: %s.' % 213 ', '.join(notdeclared), 214 t, xml.dom.NamespaceErr) 215 newcssRules.append(rule) 216 i += endi 217 expected = 'any' 218 219 i += 1 220 221 self.cssRules = newcssRules 222 for r in self.cssRules: 223 r.parentStyleSheet = self 224 self.namespaces = newnamespaces
225 226 cssText = property(_getCssText, _setCssText, 227 "(cssutils) a textual representation of the stylesheet") 228 229
230 - def deleteRule(self, index):
231 """ 232 Used to delete a rule from the style sheet. 233 234 index 235 of the rule to remove in the StyleSheet's rule list 236 237 DOMException 238 239 - INDEX_SIZE_ERR: (self) 240 Raised if the specified index does not correspond to a rule in 241 the style sheet's rule list. 242 - NO_MODIFICATION_ALLOWED_ERR: (self) 243 Raised if this style sheet is readonly. 244 """ 245 self._checkReadonly() 246 247 try: 248 self.cssRules[index].parentStyleSheet = None # detach 249 del self.cssRules[index] # delete from StyleSheet 250 except IndexError: 251 raise xml.dom.IndexSizeErr( 252 u'CSSStyleSheet: %s is not a valid index in the rulelist of length %i' % ( 253 index, self.cssRules.length))
254 255
256 - def insertRule(self, rule, index=None):
257 """ 258 Used to insert a new rule into the style sheet. The new rule now 259 becomes part of the cascade. 260 261 Rule may be a string or a valid CSSRule. 262 263 rule 264 a parsable DOMString (cssutils: or Rule object) 265 index 266 of the rule before the new rule will be inserted. 267 If the specified index is equal to the length of the 268 StyleSheet's rule collection, the rule will be added to the end 269 of the style sheet. 270 If index is not given or None rule will be appended to rule 271 list. 272 273 returns the index within the stylesheet's rule collection 274 275 DOMException 276 277 - HIERARCHY_REQUEST_ERR: (self) 278 Raised if the rule cannot be inserted at the specified index 279 e.g. if an @import rule is inserted after a standard rule set 280 or other at-rule. 281 - INDEX_SIZE_ERR: (not raised at all) 282 Raised if the specified index is not a valid insertion point. 283 - NO_MODIFICATION_ALLOWED_ERR: (self) 284 Raised if this style sheet is readonly. 285 - SYNTAX_ERR: (rule) 286 Raised if the specified rule has a syntax error and is 287 unparsable. 288 """ 289 self._checkReadonly() 290 291 # check position 292 if index is None: 293 index = len(self.cssRules) 294 elif index < 0 or index > self.cssRules.length: 295 raise xml.dom.IndexSizeErr( 296 u'CSSStyleSheet: Invalid index %s for CSSRuleList with a length of %s.' % ( 297 index, self.cssRules.length)) 298 299 # parse 300 if isinstance(rule, basestring): 301 tempsheet = CSSStyleSheet() 302 tempsheet.cssText = rule 303 if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and 304 not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)): 305 self._log.error(u'CSSStyleSheet: Invalid Rule: %s' % rule) 306 return 307 rule = tempsheet.cssRules[0] 308 elif not isinstance(rule, cssutils.css.CSSRule): 309 self._log.error(u'CSSStyleSheet: Not a CSSRule: %s' % rule) 310 return 311 312 # CHECK HIERARCHY 313 # @charset 314 if isinstance(rule, cssutils.css.CSSCharsetRule): 315 if index != 0 or (self.cssRules and 316 isinstance(self.cssRules[0], cssutils.css.CSSCharsetRule)): 317 self._log.error( 318 u'CSSStylesheet: @charset only allowed once at the beginning of a stylesheet.', 319 error=xml.dom.HierarchyRequestErr) 320 return 321 else: 322 self.cssRules.insert(index, rule) 323 rule.parentStyleSheet = self 324 325 # @unknown or comment 326 elif isinstance(rule, cssutils.css.CSSUnknownRule) or \ 327 isinstance(rule, cssutils.css.CSSComment): 328 if index == 0 and self.cssRules and \ 329 isinstance(self.cssRules[0], cssutils.css.CSSCharsetRule): 330 self._log.error( 331 u'CSSStylesheet: @charset must be the first rule.', 332 error=xml.dom.HierarchyRequestErr) 333 return 334 else: 335 self.cssRules.insert(index, rule) 336 rule.parentStyleSheet = self 337 338 # @import 339 elif isinstance(rule, cssutils.css.CSSImportRule): 340 # after @charset 341 if index == 0 and self.cssRules and \ 342 isinstance(self.cssRules[0], cssutils.css.CSSCharsetRule): 343 self._log.error( 344 u'CSSStylesheet: Found @charset at index 0.', 345 error=xml.dom.HierarchyRequestErr) 346 return 347 # before @namespace, @media and stylerule 348 for r in self.cssRules[:index]: 349 if isinstance(r, cssutils.css.CSSNamespaceRule) or \ 350 isinstance(r, cssutils.css.CSSMediaRule) or \ 351 isinstance(r, cssutils.css.CSSPageRule) or \ 352 isinstance(r, cssutils.css.CSSStyleRule): 353 self._log.error( 354 u'CSSStylesheet: Found @namespace, @media, @page or StyleRule before index %s.' % 355 index, 356 error=xml.dom.HierarchyRequestErr) 357 return 358 self.cssRules.insert(index, rule) 359 rule.parentStyleSheet = self 360 361 # @namespace 362 elif isinstance(rule, cssutils.css.CSSNamespaceRule): 363 # after @charset and @import 364 for r in self.cssRules[index:]: 365 if isinstance(r, cssutils.css.CSSCharsetRule) or \ 366 isinstance(r, cssutils.css.CSSImportRule): 367 self._log.error( 368 u'CSSStylesheet: Found @charset or @import after index %s.' % 369 index, 370 error=xml.dom.HierarchyRequestErr) 371 return 372 # before @media and stylerule 373 for r in self.cssRules[:index]: 374 if isinstance(r, cssutils.css.CSSMediaRule) or \ 375 isinstance(r, cssutils.css.CSSPageRule) or \ 376 isinstance(r, cssutils.css.CSSStyleRule): 377 self._log.error( 378 u'CSSStylesheet: Found @media, @page or StyleRule before index %s.' % 379 index, 380 error=xml.dom.HierarchyRequestErr) 381 return 382 self.cssRules.insert(index, rule) 383 rule.parentStyleSheet = self 384 self.namespaces.add(rule.prefix) 385 386 # all other 387 else: 388 for r in self.cssRules[index:]: 389 if isinstance(r, cssutils.css.CSSCharsetRule) or \ 390 isinstance(r, cssutils.css.CSSImportRule) or \ 391 isinstance(r, cssutils.css.CSSNamespaceRule): 392 self._log.error( 393 u'CSSStylesheet: Found @charset, @import or @namespace before index %s.' % 394 index, 395 error=xml.dom.HierarchyRequestErr) 396 return 397 self.cssRules.insert(index, rule) 398 rule.parentStyleSheet = self 399 400 return index
401 402
403 - def addRule(self, rule):
404 "DEPRECATED, use appendRule instead" 405 import warnings 406 warnings.warn( 407 '``addRule`` is deprecated: Use ``insertRule(rule)``.', 408 DeprecationWarning) 409 return self.insertRule(rule)
410 411
412 - def _getsetOwnerRuleDummy(self):
413 """ 414 NOT IMPLEMENTED YET 415 If this style sheet comes from an @import rule, the ownerRule 416 attribute will contain the CSSImportRule. In that case, the 417 ownerNode attribute in the StyleSheet interface will be null. If 418 the style sheet comes from an element or a processing instruction, 419 the ownerRule attribute will be null and the ownerNode attribute 420 will contain the Node. 421 """ 422 raise NotImplementedError()
423 424 ownerRule = property(_getsetOwnerRuleDummy, _getsetOwnerRuleDummy, 425 doc="(DOM attribute) NOT IMPLEMENTED YET") 426 427
428 - def setSerializer(self, cssserializer):
429 """ 430 Sets Serializer used for output of this stylesheet 431 """ 432 if isinstance(cssserializer, cssutils.CSSSerializer): 433 cssutils.ser = cssserializer 434 else: 435 raise ValueError(u'Serializer must be an instance of cssutils.CSSSerializer.')
436
437 - def setSerializerPref(self, pref, value):
438 """ 439 Sets Preference of CSSSerializer used for output of this 440 stylesheet. See cssutils.serialize.Preferences for possible 441 preferences to be set. 442 """ 443 cssutils.ser.prefs.__setattr__(pref, value)
444 445 446 if __name__ == '__main__': 447 print "CSSStyleSheet" 448 c = CSSStyleSheet(href=None, 449 # 450 media="all", 451 title="test", 452 disabled=None, 453 ownerNode=None, 454 parentStyleSheet=None) 455 c.cssText = u'''@\\namespace n1 "utf-8"; 456 |a[n1|b], n2|c {} 457 ''' 458 print c.cssText 459 ## print "title: ", c.title 460 ## print "type: ", c.type 461 ## print c.cssRules 462 ## c.addRule(cssutils.css.CSSImportRule('x')) 463