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

Source Code for Module cssutils.css.cssstyledeclaration

  1  """CSSStyleDeclaration implements DOM Level 2 CSS CSSStyleDeclaration and 
  2  inherits CSS2Properties 
  3   
  4  see 
  5      http://www.w3.org/TR/1998/REC-CSS2-19980512/syndata.html#parsing-errors 
  6   
  7  Unknown properties 
  8  ------------------ 
  9  User agents must ignore a declaration with an unknown property. 
 10  For example, if the style sheet is:: 
 11   
 12      H1 { color: red; rotation: 70minutes } 
 13   
 14  the user agent will treat this as if the style sheet had been:: 
 15   
 16      H1 { color: red } 
 17   
 18  Cssutils gives a WARNING about an unknown CSS2 Property "rotation" but 
 19  keeps any property (if syntactical correct). 
 20   
 21  Illegal values 
 22  -------------- 
 23  User agents must ignore a declaration with an illegal value. For example:: 
 24   
 25      IMG { float: left }       /* correct CSS2 */ 
 26      IMG { float: left here }  /* "here" is not a value of 'float' */ 
 27      IMG { background: "red" } /* keywords cannot be quoted in CSS2 */ 
 28      IMG { border-width: 3 }   /* a unit must be specified for length values */ 
 29   
 30  A CSS2 parser would honor the first rule and ignore the rest, as if the 
 31  style sheet had been:: 
 32   
 33      IMG { float: left } 
 34      IMG { } 
 35      IMG { } 
 36      IMG { } 
 37   
 38  Cssutils again will issue WARNING about invalid CSS2 property values. 
 39   
 40  TODO: 
 41      This interface is also used to provide a read-only access to the 
 42      computed values of an element. See also the ViewCSS interface. 
 43   
 44      - return computed values and not literal values 
 45      - simplify unit pairs/triples/quadruples 
 46        2px 2px 2px 2px -> 2px for border/padding... 
 47      - normalize compound properties like: 
 48        background: no-repeat left url()  #fff 
 49        -> background: #fff url() no-repeat left 
 50  """ 
 51  __all__ = ['CSSStyleDeclaration', 'Property'] 
 52  __docformat__ = 'restructuredtext' 
 53  __author__ = '$LastChangedBy: cthedot $' 
 54  __date__ = '$LastChangedDate: 2007-10-27 22:27:40 +0200 (Sa, 27 Okt 2007) $' 
 55  __version__ = '$LastChangedRevision: 590 $' 
 56   
 57  import xml.dom 
 58  import cssutils 
 59  from cssproperties import CSS2Properties 
 60  from property import Property 
 61   
62 -class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base):
63 """ 64 The CSSStyleDeclaration class represents a single CSS declaration 65 block. This class may be used to determine the style properties 66 currently set in a block or to set style properties explicitly 67 within the block. 68 69 While an implementation may not recognize all CSS properties within 70 a CSS declaration block, it is expected to provide access to all 71 specified properties in the style sheet through the 72 CSSStyleDeclaration interface. 73 Furthermore, implementations that support a specific level of CSS 74 should correctly handle CSS shorthand properties for that level. For 75 a further discussion of shorthand properties, see the CSS2Properties 76 interface. 77 78 Additionally the CSS2Properties interface is implemented. 79 80 Properties 81 ========== 82 cssText: of type DOMString 83 The parsable textual representation of the declaration block 84 (excluding the surrounding curly braces). Setting this attribute 85 will result in the parsing of the new value and resetting of the 86 properties in the declaration block. It also allows the insertion 87 of additional properties and their values into the block. 88 length: of type unsigned long, readonly 89 The number of properties that have been explicitly set in this 90 declaration block. The range of valid indices is 0 to length-1 91 inclusive. 92 parentRule: of type CSSRule, readonly 93 The CSS rule that contains this declaration block or None if this 94 CSSStyleDeclaration is not attached to a CSSRule. 95 seq: a list (cssutils) 96 All parts of this style declaration including CSSComments 97 98 $css2propertyname 99 All properties defined in the CSS2Properties class are available 100 as direct properties of CSSStyleDeclaration with their respective 101 DOM name, so e.g. ``fontStyle`` for property 'font-style'. 102 103 These may be used as:: 104 105 >>> style = CSSStyleDeclaration(cssText='color: red') 106 >>> style.color = 'green' 107 >>> print style.color 108 green 109 >>> del style.color 110 >>> print style.color # print empty string 111 112 Format 113 ====== 114 [Property: Value;]* Property: Value? 115 """
116 - def __init__(self, parentRule=None, cssText=u'', readonly=False):
117 """ 118 parentRule 119 The CSS rule that contains this declaration block or 120 None if this CSSStyleDeclaration is not attached to a CSSRule. 121 readonly 122 defaults to False 123 """ 124 super(CSSStyleDeclaration, self).__init__() 125 self.valid = True 126 self.seq = [] 127 self.parentRule = parentRule 128 self.cssText = cssText 129 self._readonly = readonly
130
131 - def __setattr__(self, n, v):
132 """ 133 Prevent setting of unknown properties on CSSStyleDeclaration 134 which would not work anyway. For these 135 ``CSSStyleDeclaration.setProperty`` MUST be called explicitly! 136 137 TODO: 138 implementation of known is not really nice, any alternative? 139 """ 140 known = ['_tokenizer', '_log', '_ttypes', 141 'valid', 'seq', 'parentRule', '_parentRule', 'cssText', 142 '_readonly'] 143 known.extend(CSS2Properties._properties) 144 if n in known: 145 super(CSSStyleDeclaration, self).__setattr__(n, v) 146 else: 147 raise AttributeError( 148 'Unknown CSS Property, ``CSSStyleDeclaration.setProperty("%s")`` MUST be used.' 149 % n)
150
151 - def __iter__(self):
152 "CSSStyleDeclaration is iterable, see __items()" 153 return CSSStyleDeclaration.__items(self)
154
155 - def __items(self):
156 """ 157 the iterator 158 159 returns in contrast to calling item(index) all property objects so 160 effectively the same as ``getProperties(all=True)`` 161 """ 162 properties = self.getProperties(all=True) 163 for property in properties: 164 yield property
165 166 # overwritten accessor functions for CSS2Properties' properties
167 - def _getP(self, CSSName):
168 """ 169 (DOM CSS2Properties) 170 Overwritten here and effectively the same as 171 ``self.getPropertyValue(CSSname)``. 172 173 Parameter is in CSSname format ('font-style'), see CSS2Properties. 174 175 Example:: 176 177 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 178 >>> print style.fontStyle 179 italic 180 181 """ 182 return self.getPropertyValue(CSSName)
183
184 - def _setP(self, CSSName, value):
185 """ 186 (DOM CSS2Properties) 187 Overwritten here and effectively the same as 188 ``self.setProperty(CSSname, value)``. 189 190 Only known CSS2Properties may be set this way, otherwise an 191 AttributeError is raised. 192 For these unknown properties ``setPropertyValue(CSSname, value)`` 193 has to be called explicitly. 194 Also setting the priority of properties needs to be done with a 195 call like ``setPropertyValue(CSSname, value, priority)``. 196 197 Example:: 198 199 >>> style = CSSStyleDeclaration() 200 >>> style.fontStyle = 'italic' 201 >>> # or 202 >>> style.setProperty('font-style', 'italic', '!important') 203 204 """ 205 self.setProperty(CSSName, value)
206 # TODO: 207 # if 'background-image' == CSSName: 208 # for p in self._properties(): 209 # if p.name == 'background': 210 # print p 211 # self.setProperty(CSSName, value) 212 # else: 213 # self.setProperty(CSSName, value) 214
215 - def _delP(self, CSSName):
216 """ 217 (cssutils only) 218 Overwritten here and effectively the same as 219 ``self.removeProperty(CSSname)``. 220 221 Example:: 222 223 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 224 >>> del style.fontStyle 225 >>> print style.fontStyle # prints u'' 226 227 """ 228 self.removeProperty(CSSName)
229
230 - def _getCssText(self):
231 """ 232 returns serialized property cssText 233 """ 234 return cssutils.ser.do_css_CSSStyleDeclaration(self)
235
236 - def _setCssText(self, cssText):
237 """ 238 Setting this attribute will result in the parsing of the new value 239 and resetting of all the properties in the declaration block 240 including the removal or addition of properties. 241 242 DOMException on setting 243 244 - NO_MODIFICATION_ALLOWED_ERR: (self) 245 Raised if this declaration is readonly or a property is readonly. 246 - SYNTAX_ERR: (self) 247 Raised if the specified CSS string value has a syntax error and 248 is unparsable. 249 """ 250 self._checkReadonly() 251 tokenizer = self._tokenize2(cssText) 252 253 def _ident(expected, seq, token, tokenizer=None): 254 # a property 255 tokens = self._tokensupto2(tokenizer, starttoken=token, 256 semicolon=True) 257 if self._tokenvalue(tokens[-1]) == u';': 258 tokens.pop() 259 property = Property() 260 property.cssText = tokens 261 if property.wellformed: 262 seq.append(property) 263 else: 264 self._log.error(u'CSSStyleDeclaration: Syntax Error in Property: %s' 265 % self._valuestr(tokens)) 266 return expected
267 268 # [Property: Value;]* Property: Value? 269 newseq = [] 270 valid, expected = self._parse(expected=None, 271 seq=newseq, tokenizer=tokenizer, 272 productions={'IDENT': _ident}) 273 274 # valid set by parse 275 # no post conditions 276 if valid: 277 self.seq = newseq
278 279 cssText = property(_getCssText, _setCssText, 280 doc="(DOM) A parsable textual representation of the declaration\ 281 block excluding the surrounding curly braces.") 282
283 - def getCssText(self, separator=None):
284 """ 285 returns serialized property cssText, each property separated by 286 given ``separator`` which may e.g. be u'' to be able to use 287 cssText directly in an HTML style attribute. ";" is always part of 288 each property (except the last one) and can **not** be set with 289 separator! 290 """ 291 return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
292
293 - def _getParentRule(self):
294 return self._parentRule
295
296 - def _setParentRule(self, parentRule):
297 self._parentRule = parentRule
298 299 parentRule = property(_getParentRule, _setParentRule, 300 doc="(DOM) The CSS rule that contains this declaration block or\ 301 None if this CSSStyleDeclaration is not attached to a CSSRule.") 302
303 - def getProperties(self, name=None, all=False):
304 """ 305 returns a list of Property objects set in this declaration in order 306 they have been set e.g. in the original stylesheet 307 308 name 309 optional name of properties which are requested (a filter). 310 Only properties with this (normalized) name are returned. 311 all=False 312 if False (DEFAULT) only the effective properties (the ones set 313 last) are returned. So if a name is given only one property 314 is returned. 315 316 if True all properties including properties set multiple times with 317 different values or priorities for different UAs are returned. 318 """ 319 nname = self._normalize(name) 320 properties = [] 321 done = set() 322 for x in reversed(self.seq): 323 if isinstance(x, Property): 324 isname = (bool(nname) == False) or (x.normalname == nname) 325 stilltodo = x.normalname not in done 326 if isname and stilltodo: 327 properties.append(x) 328 if not all: 329 done.add(x.normalname) 330 331 properties.reverse() 332 return properties
333
334 - def getPropertyCSSValue(self, name, normalize=True):
335 """ 336 (DOM) 337 Used to retrieve the object representation of the value of a CSS 338 property if it has been explicitly set within this declaration 339 block. This method returns None if the property is a shorthand 340 property. Shorthand property values can only be accessed and 341 modified as strings, using the getPropertyValue and setProperty 342 methods. 343 344 name 345 of the CSS property 346 347 The name will be normalized (lowercase, no simple escapes) so 348 "color", "COLOR" or "C\olor" are all equivalent 349 350 returns CSSValue, the value of the property if it has been 351 explicitly set for this declaration block. Returns None if the 352 property has not been set. 353 354 for more on shorthand properties see 355 http://www.dustindiaz.com/css-shorthand/ 356 """ 357 nname = self._normalize(name) 358 if nname in self._SHORTHANDPROPERTIES: 359 self._log.debug( 360 u'CSSValue for shorthand property "%s" should be None, this may be implemented later.' % 361 nname, neverraise=True) 362 363 properties = self.getProperties(name, all=(not normalize)) 364 for property in reversed(properties): 365 if normalize and property.normalname == nname: 366 return property.cssValue 367 elif property.name == name: 368 return property.cssValue 369 return None
370
371 - def getPropertyValue(self, name, normalize=True):
372 """ 373 (DOM) 374 Used to retrieve the value of a CSS property if it has been 375 explicitly set within this declaration block. 376 377 name 378 of the CSS property 379 380 The name will be normalized (lowercase, no simple escapes) so 381 "color", "COLOR" or "C\olor" are all equivalent 382 383 returns the value of the property if it has been explicitly set 384 for this declaration block. Returns the empty string if the 385 property has not been set. 386 """ 387 nname = self._normalize(name) 388 properties = self.getProperties(name, all=(not normalize)) 389 for property in reversed(properties): 390 if normalize and property.normalname == nname: 391 return property.value 392 elif property.name == name: 393 return property.value 394 return u''
395
396 - def getPropertyPriority(self, name, normalize=True):
397 """ 398 (DOM) 399 Used to retrieve the priority of a CSS property (e.g. the 400 "important" qualifier) if the property has been explicitly set in 401 this declaration block. 402 403 name 404 of the CSS property 405 406 The name will be normalized (lowercase, no simple escapes) so 407 "color", "COLOR" or "C\olor" are all equivalent 408 409 returns a string representing the priority (e.g. "important") if 410 one exists. The empty string if none exists. 411 """ 412 nname = self._normalize(name) 413 properties = self.getProperties(name, all=(not normalize)) 414 for property in reversed(properties): 415 if normalize and property.normalname == nname: 416 return property.priority 417 elif property.name == name: 418 return property.priority 419 return u''
420
421 - def removeProperty(self, name, normalize=True):
422 """ 423 (DOM) 424 Used to remove a CSS property if it has been explicitly set within 425 this declaration block. 426 427 name 428 of the CSS property to remove 429 430 The name will be normalized (lowercase, no simple escapes) so 431 "color", "COLOR" or "C\olor" are all equivalent 432 433 returns the value of the property if it has been explicitly set for 434 this declaration block. Returns the empty string if the property 435 has not been set or the property name does not correspond to a 436 known CSS property 437 438 raises DOMException 439 440 - NO_MODIFICATION_ALLOWED_ERR: (self) 441 Raised if this declaration is readonly or the property is 442 readonly. 443 """ 444 self._checkReadonly() 445 nname = self._normalize(name) 446 newseq = [] 447 r = u'' 448 isdone = False 449 for x in reversed(self.seq): 450 if isinstance(x, Property): 451 if not isdone and normalize and x.normalname == nname: 452 r = x.cssValue.cssText 453 isdone = True 454 elif not isdone and x.name == name: 455 r = x.cssValue.cssText 456 isdone = True 457 else: 458 newseq.append(x) 459 else: 460 newseq.append(x) 461 newseq.reverse() 462 self.seq = newseq 463 return r
464
465 - def setProperty(self, name, value, priority=u'', normalize=True):
466 """ 467 (DOM) 468 Used to set a property value and priority within this declaration 469 block. 470 471 name 472 of the CSS property to set (in W3C DOM the parameter is called 473 "propertyName") 474 475 If a property with this name is present it will be reset 476 477 The name is normalized if normalize=True 478 479 value 480 the new value of the property 481 priority 482 the optional priority of the property (e.g. "important") 483 484 DOMException on setting 485 486 - SYNTAX_ERR: (self) 487 Raised if the specified value has a syntax error and is 488 unparsable. 489 - NO_MODIFICATION_ALLOWED_ERR: (self) 490 Raised if this declaration is readonly or the property is 491 readonly. 492 """ 493 self._checkReadonly() 494 495 newp = Property(name, value, priority) 496 if not newp.wellformed: 497 self._log.warn(u'Invalid Property: %s: %s %s' 498 % (name, value, priority)) 499 else: 500 nname = self._normalize(name) 501 properties = self.getProperties(name, all=(not normalize)) 502 for property in reversed(properties): 503 if normalize and property.normalname == nname: 504 property.cssValue = newp.cssValue.cssText 505 property.priority = newp.priority 506 break 507 elif property.name == name: 508 property.cssValue = newp.cssValue.cssText 509 property.priority = newp.priority 510 break 511 else: 512 self.seq.append(newp)
513
514 - def __nnames(self):
515 nnames = set() 516 for x in self.seq: 517 if isinstance(x, Property): 518 nnames.add(x.normalname) 519 return nnames
520
521 - def item(self, index):
522 """ 523 **NOTE**: 524 Compare to ``for property in declaration`` which works on **all** 525 properties set in this declaration and not just the effecitve ones. 526 527 (DOM) 528 Used to retrieve the properties that have been explicitly set in 529 this declaration block. The order of the properties retrieved using 530 this method does not have to be the order in which they were set. 531 This method can be used to iterate over all properties in this 532 declaration block. 533 534 index 535 of the property to retrieve, negative values behave like 536 negative indexes on Python lists, so -1 is the last element 537 538 returns the name of the property at this ordinal position. The 539 empty string if no property exists at this position. 540 541 ATTENTION: 542 Only properties with a different normalname are counted. If two 543 properties with the same normalname are present in this declaration 544 only the last set (and effectively *in style*) is used. 545 546 ``item()`` and ``length`` work on the same set here. 547 """ 548 nnames = self.__nnames() 549 orderednnames = [] 550 for x in reversed(self.seq): 551 nname = x.normalname 552 if isinstance(x, Property) and nname in nnames: 553 nnames.remove(nname) 554 orderednnames.append(nname) 555 orderednnames.reverse() 556 try: 557 return orderednnames[index] 558 except IndexError: 559 return u''
560
561 - def _getLength(self):
562 return len(self.__nnames())
563 564 length = property(_getLength, 565 doc="(DOM) The number of distince properties that have been explicitly\ 566 in this declaration block. The range of valid indices is 0 to\ 567 length-1 inclusive. These are properties with the same ``normalname``\ 568 only. ``item()`` and ``length`` work on the same set here.") 569
570 - def __repr__(self):
571 return "cssutils.css.%s()" % ( 572 self.__class__.__name__)
573
574 - def __str__(self):
575 return "<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % ( 576 self.__class__.__name__, self.length, 577 len(self.getProperties(all=True)), id(self))
578