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  extends 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 message about any unknown properties but 
 19  keeps any property (if syntactically 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 a message (WARNING in this case) about invalid  
 39  CSS2 property values. 
 40   
 41  TODO: 
 42      This interface is also used to provide a read-only access to the 
 43      computed values of an element. See also the ViewCSS interface. 
 44   
 45      - return computed values and not literal values 
 46      - simplify unit pairs/triples/quadruples 
 47        2px 2px 2px 2px -> 2px for border/padding... 
 48      - normalize compound properties like: 
 49        background: no-repeat left url()  #fff 
 50        -> background: #fff url() no-repeat left 
 51  """ 
 52  __all__ = ['CSSStyleDeclaration', 'Property'] 
 53  __docformat__ = 'restructuredtext' 
 54  __version__ = '$Id: cssstyledeclaration.py 1284 2008-06-05 16:29:17Z cthedot $' 
 55   
 56  import xml.dom 
 57  import cssutils 
 58  from cssproperties import CSS2Properties 
 59  from property import Property 
 60   
61 -class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base2):
62 """ 63 The CSSStyleDeclaration class represents a single CSS declaration 64 block. This class may be used to determine the style properties 65 currently set in a block or to set style properties explicitly 66 within the block. 67 68 While an implementation may not recognize all CSS properties within 69 a CSS declaration block, it is expected to provide access to all 70 specified properties in the style sheet through the 71 CSSStyleDeclaration interface. 72 Furthermore, implementations that support a specific level of CSS 73 should correctly handle CSS shorthand properties for that level. For 74 a further discussion of shorthand properties, see the CSS2Properties 75 interface. 76 77 Additionally the CSS2Properties interface is implemented. 78 79 Properties 80 ========== 81 cssText 82 The parsable textual representation of the declaration block 83 (excluding the surrounding curly braces). Setting this attribute 84 will result in the parsing of the new value and resetting of the 85 properties in the declaration block. It also allows the insertion 86 of additional properties and their values into the block. 87 length: of type unsigned long, readonly 88 The number of properties that have been explicitly set in this 89 declaration block. The range of valid indices is 0 to length-1 90 inclusive. 91 parentRule: of type CSSRule, readonly 92 The CSS rule that contains this declaration block or None if this 93 CSSStyleDeclaration is not attached to a CSSRule. 94 seq: a list (cssutils) 95 All parts of this style declaration including CSSComments 96 97 $css2propertyname 98 All properties defined in the CSS2Properties class are available 99 as direct properties of CSSStyleDeclaration with their respective 100 DOM name, so e.g. ``fontStyle`` for property 'font-style'. 101 102 These may be used as:: 103 104 >>> style = CSSStyleDeclaration(cssText='color: red') 105 >>> style.color = 'green' 106 >>> print style.color 107 green 108 >>> del style.color 109 >>> print style.color # print empty string 110 111 Format 112 ====== 113 [Property: Value Priority?;]* [Property: Value Priority?]? 114 """
115 - def __init__(self, cssText=u'', parentRule=None, readonly=False):
116 """ 117 cssText 118 Shortcut, sets CSSStyleDeclaration.cssText 119 parentRule 120 The CSS rule that contains this declaration block or 121 None if this CSSStyleDeclaration is not attached to a CSSRule. 122 readonly 123 defaults to False 124 """ 125 super(CSSStyleDeclaration, self).__init__() 126 self._parentRule = parentRule 127 #self._seq = self._tempSeq() 128 self.cssText = cssText 129 self._readonly = readonly
130
131 - def __contains__(self, nameOrProperty):
132 """ 133 checks if a property (or a property with given name is in style 134 135 name 136 a string or Property, uses normalized name and not literalname 137 """ 138 if isinstance(nameOrProperty, Property): 139 name = nameOrProperty.name 140 else: 141 name = self._normalize(nameOrProperty) 142 return name in self.__nnames()
143
144 - def __iter__(self):
145 """ 146 iterator of set Property objects with different normalized names. 147 """ 148 def properties(): 149 for name in self.__nnames(): 150 yield self.getProperty(name)
151 return properties()
152
153 - def __setattr__(self, n, v):
154 """ 155 Prevent setting of unknown properties on CSSStyleDeclaration 156 which would not work anyway. For these 157 ``CSSStyleDeclaration.setProperty`` MUST be called explicitly! 158 159 TODO: 160 implementation of known is not really nice, any alternative? 161 """ 162 known = ['_tokenizer', '_log', '_ttypes', 163 '_seq', 'seq', 'parentRule', '_parentRule', 'cssText', 164 'valid', 'wellformed', 165 '_readonly'] 166 known.extend(CSS2Properties._properties) 167 if n in known: 168 super(CSSStyleDeclaration, self).__setattr__(n, v) 169 else: 170 raise AttributeError( 171 'Unknown CSS Property, ``CSSStyleDeclaration.setProperty("%s", ...)`` MUST be used.' 172 % n)
173
174 - def __nnames(self):
175 """ 176 returns iterator for all different names in order as set 177 if names are set twice the last one is used (double reverse!) 178 """ 179 names = [] 180 for item in reversed(self.seq): 181 val = item.value 182 if isinstance(val, Property) and not val.name in names: 183 names.append(val.name) 184 return reversed(names)
185
186 - def __getitem__(self, CSSName):
187 """Retrieve the value of property ``CSSName`` from this declaration. 188 189 ``CSSName`` will be always normalized. 190 """ 191 return self.getPropertyValue(CSSName)
192
193 - def __setitem__(self, CSSName, value):
194 """Set value of property ``CSSName``. ``value`` may also be a tuple of 195 (value, priority), e.g. style['color'] = ('red', 'important') 196 197 ``CSSName`` will be always normalized. 198 """ 199 priority = None 200 if type(value) == tuple: 201 value, priority = value 202 203 return self.setProperty(CSSName, value, priority)
204
205 - def __delitem__(self, CSSName):
206 """Delete property ``CSSName`` from this declaration. 207 If property is not in this declaration return u'' just like 208 removeProperty. 209 210 ``CSSName`` will be always normalized. 211 """ 212 return self.removeProperty(CSSName)
213 214 # overwritten accessor functions for CSS2Properties' properties
215 - def _getP(self, CSSName):
216 """ 217 (DOM CSS2Properties) 218 Overwritten here and effectively the same as 219 ``self.getPropertyValue(CSSname)``. 220 221 Parameter is in CSSname format ('font-style'), see CSS2Properties. 222 223 Example:: 224 225 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 226 >>> print style.fontStyle 227 italic 228 """ 229 return self.getPropertyValue(CSSName)
230
231 - def _setP(self, CSSName, value):
232 """ 233 (DOM CSS2Properties) 234 Overwritten here and effectively the same as 235 ``self.setProperty(CSSname, value)``. 236 237 Only known CSS2Properties may be set this way, otherwise an 238 AttributeError is raised. 239 For these unknown properties ``setPropertyValue(CSSname, value)`` 240 has to be called explicitly. 241 Also setting the priority of properties needs to be done with a 242 call like ``setPropertyValue(CSSname, value, priority)``. 243 244 Example:: 245 246 >>> style = CSSStyleDeclaration() 247 >>> style.fontStyle = 'italic' 248 >>> # or 249 >>> style.setProperty('font-style', 'italic', '!important') 250 """ 251 self.setProperty(CSSName, value)
252 # TODO: Shorthand ones 253
254 - def _delP(self, CSSName):
255 """ 256 (cssutils only) 257 Overwritten here and effectively the same as 258 ``self.removeProperty(CSSname)``. 259 260 Example:: 261 262 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 263 >>> del style.fontStyle 264 >>> print style.fontStyle # prints u'' 265 266 """ 267 self.removeProperty(CSSName)
268
269 - def _getCssText(self):
270 """ 271 returns serialized property cssText 272 """ 273 return cssutils.ser.do_css_CSSStyleDeclaration(self)
274
275 - def _setCssText(self, cssText):
276 """ 277 Setting this attribute will result in the parsing of the new value 278 and resetting of all the properties in the declaration block 279 including the removal or addition of properties. 280 281 DOMException on setting 282 283 - NO_MODIFICATION_ALLOWED_ERR: (self) 284 Raised if this declaration is readonly or a property is readonly. 285 - SYNTAX_ERR: (self) 286 Raised if the specified CSS string value has a syntax error and 287 is unparsable. 288 """ 289 self._checkReadonly() 290 tokenizer = self._tokenize2(cssText) 291 292 # for closures: must be a mutable 293 new = {'wellformed': True} 294 def ident(expected, seq, token, tokenizer=None): 295 # a property 296 297 tokens = self._tokensupto2(tokenizer, starttoken=token, 298 semicolon=True) 299 if self._tokenvalue(tokens[-1]) == u';': 300 tokens.pop() 301 property = Property() 302 property.cssText = tokens 303 if property.wellformed: 304 seq.append(property, 'Property') 305 else: 306 self._log.error(u'CSSStyleDeclaration: Syntax Error in Property: %s' 307 % self._valuestr(tokens)) 308 # does not matter in this case 309 return expected
310 311 def unexpected(expected, seq, token, tokenizer=None): 312 # error, find next ; or } to omit upto next property 313 ignored = self._tokenvalue(token) + self._valuestr( 314 self._tokensupto2(tokenizer, propertyvalueendonly=True)) 315 self._log.error(u'CSSStyleDeclaration: Unexpected token, ignoring upto %r.' % 316 ignored,token) 317 # does not matter in this case 318 return expected 319 320 # [Property: Value;]* Property: Value? 321 newseq = self._tempSeq() 322 wellformed, expected = self._parse(expected=None, 323 seq=newseq, tokenizer=tokenizer, 324 productions={'IDENT': ident},#, 'CHAR': char}, 325 default=unexpected) 326 # wellformed set by parse 327 # post conditions 328 329 # do not check wellformed as invalid things are removed anyway 330 #if wellformed: 331 self._setSeq(newseq) 332 333 cssText = property(_getCssText, _setCssText, 334 doc="(DOM) A parsable textual representation of the declaration\ 335 block excluding the surrounding curly braces.") 336
337 - def getCssText(self, separator=None):
338 """ 339 returns serialized property cssText, each property separated by 340 given ``separator`` which may e.g. be u'' to be able to use 341 cssText directly in an HTML style attribute. ";" is always part of 342 each property (except the last one) and can **not** be set with 343 separator! 344 """ 345 return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
346
347 - def _getParentRule(self):
348 return self._parentRule
349
350 - def _setParentRule(self, parentRule):
351 self._parentRule = parentRule
352 353 parentRule = property(_getParentRule, _setParentRule, 354 doc="(DOM) The CSS rule that contains this declaration block or\ 355 None if this CSSStyleDeclaration is not attached to a CSSRule.") 356
357 - def getProperties(self, name=None, all=False):
358 """ 359 Returns a list of Property objects set in this declaration. 360 361 name 362 optional name of properties which are requested (a filter). 363 Only properties with this **always normalized** name are returned. 364 all=False 365 if False (DEFAULT) only the effective properties (the ones set 366 last) are returned. If name is given a list with only one property 367 is returned. 368 369 if True all properties including properties set multiple times with 370 different values or priorities for different UAs are returned. 371 The order of the properties is fully kept as in the original 372 stylesheet. 373 """ 374 if name and not all: 375 # single prop but list 376 p = self.getProperty(name) 377 if p: 378 return [p] 379 else: 380 return [] 381 elif not all: 382 # effective Properties in name order 383 return [self.getProperty(name)for name in self.__nnames()] 384 else: 385 # all properties or all with this name 386 nname = self._normalize(name) 387 properties = [] 388 for item in self.seq: 389 val = item.value 390 if isinstance(val, Property) and ( 391 (bool(nname) == False) or (val.name == nname)): 392 properties.append(val) 393 return properties
394
395 - def getProperty(self, name, normalize=True):
396 """ 397 Returns the effective Property object. 398 399 name 400 of the CSS property, always lowercase (even if not normalized) 401 normalize 402 if True (DEFAULT) name will be normalized (lowercase, no simple 403 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 404 405 If False may return **NOT** the effective value but the effective 406 for the unnormalized name. 407 """ 408 nname = self._normalize(name) 409 found = None 410 for item in reversed(self.seq): 411 val = item.value 412 if isinstance(val, Property): 413 if (normalize and nname == val.name) or name == val.literalname: 414 if val.priority: 415 return val 416 elif not found: 417 found = val 418 return found
419
420 - def getPropertyCSSValue(self, name, normalize=True):
421 """ 422 Returns CSSValue, the value of the effective property if it has been 423 explicitly set for this declaration block. 424 425 name 426 of the CSS property, always lowercase (even if not normalized) 427 normalize 428 if True (DEFAULT) name will be normalized (lowercase, no simple 429 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 430 431 If False may return **NOT** the effective value but the effective 432 for the unnormalized name. 433 434 (DOM) 435 Used to retrieve the object representation of the value of a CSS 436 property if it has been explicitly set within this declaration 437 block. Returns None if the property has not been set. 438 439 (This method returns None if the property is a shorthand 440 property. Shorthand property values can only be accessed and 441 modified as strings, using the getPropertyValue and setProperty 442 methods.) 443 444 **cssutils currently always returns a CSSValue if the property is 445 set.** 446 447 for more on shorthand properties see 448 http://www.dustindiaz.com/css-shorthand/ 449 """ 450 nname = self._normalize(name) 451 if nname in self._SHORTHANDPROPERTIES: 452 self._log.info( 453 u'CSSValue for shorthand property "%s" should be None, this may be implemented later.' % 454 nname, neverraise=True) 455 456 p = self.getProperty(name, normalize) 457 if p: 458 return p.cssValue 459 else: 460 return None
461
462 - def getPropertyValue(self, name, normalize=True):
463 """ 464 Returns the value of the effective property if it has been explicitly 465 set for this declaration block. Returns the empty string if the 466 property has not been set. 467 468 name 469 of the CSS property, always lowercase (even if not normalized) 470 normalize 471 if True (DEFAULT) name will be normalized (lowercase, no simple 472 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 473 474 If False may return **NOT** the effective value but the effective 475 for the unnormalized name. 476 """ 477 p = self.getProperty(name, normalize) 478 if p: 479 return p.value 480 else: 481 return u''
482
483 - def getPropertyPriority(self, name, normalize=True):
484 """ 485 Returns the priority of the effective CSS property (e.g. the 486 "important" qualifier) if the property has been explicitly set in 487 this declaration block. The empty string if none exists. 488 489 name 490 of the CSS property, always lowercase (even if not normalized) 491 normalize 492 if True (DEFAULT) name will be normalized (lowercase, no simple 493 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 494 495 If False may return **NOT** the effective value but the effective 496 for the unnormalized name. 497 """ 498 p = self.getProperty(name, normalize) 499 if p: 500 return p.priority 501 else: 502 return u''
503
504 - def removeProperty(self, name, normalize=True):
505 """ 506 (DOM) 507 Used to remove a CSS property if it has been explicitly set within 508 this declaration block. 509 510 Returns the value of the property if it has been explicitly set for 511 this declaration block. Returns the empty string if the property 512 has not been set or the property name does not correspond to a 513 known CSS property 514 515 name 516 of the CSS property 517 normalize 518 if True (DEFAULT) name will be normalized (lowercase, no simple 519 escapes) so "color", "COLOR" or "C\olor" will all be equivalent. 520 The effective Property value is returned and *all* Properties 521 with ``Property.name == name`` are removed. 522 523 If False may return **NOT** the effective value but the effective 524 for the unnormalized ``name`` only. Also only the Properties with 525 the literal name ``name`` are removed. 526 527 raises DOMException 528 529 - NO_MODIFICATION_ALLOWED_ERR: (self) 530 Raised if this declaration is readonly or the property is 531 readonly. 532 """ 533 self._checkReadonly() 534 r = self.getPropertyValue(name, normalize=normalize) 535 newseq = self._tempSeq() 536 if normalize: 537 # remove all properties with name == nname 538 nname = self._normalize(name) 539 for item in self.seq: 540 if not (isinstance(item.value, Property) and item.value.name == nname): 541 newseq.appendItem(item) 542 else: 543 # remove all properties with literalname == name 544 for item in self.seq: 545 if not (isinstance(item.value, Property) and item.value.literalname == name): 546 newseq.appendItem(item) 547 self._setSeq(newseq) 548 return r
549
550 - def setProperty(self, name, value=None, priority=u'', normalize=True):
551 """ 552 (DOM) 553 Used to set a property value and priority within this declaration 554 block. 555 556 name 557 of the CSS property to set (in W3C DOM the parameter is called 558 "propertyName"), always lowercase (even if not normalized) 559 560 If a property with this name is present it will be reset 561 562 cssutils also allowed name to be a Property object, all other 563 parameter are ignored in this case 564 565 value 566 the new value of the property, omit if name is already a Property 567 priority 568 the optional priority of the property (e.g. "important") 569 normalize 570 if True (DEFAULT) name will be normalized (lowercase, no simple 571 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 572 573 DOMException on setting 574 575 - SYNTAX_ERR: (self) 576 Raised if the specified value has a syntax error and is 577 unparsable. 578 - NO_MODIFICATION_ALLOWED_ERR: (self) 579 Raised if this declaration is readonly or the property is 580 readonly. 581 """ 582 self._checkReadonly() 583 584 if isinstance(name, Property): 585 newp = name 586 name = newp.literalname 587 else: 588 newp = Property(name, value, priority) 589 if not newp.wellformed: 590 self._log.warn(u'Invalid Property: %s: %s %s' 591 % (name, value, priority)) 592 else: 593 nname = self._normalize(name) 594 properties = self.getProperties(name, all=(not normalize)) 595 for property in reversed(properties): 596 if normalize and property.name == nname: 597 property.cssValue = newp.cssValue.cssText 598 property.priority = newp.priority 599 break 600 elif property.literalname == name: 601 property.cssValue = newp.cssValue.cssText 602 property.priority = newp.priority 603 break 604 else: 605 self.seq._readonly = False 606 self.seq.append(newp, 'Property') 607 self.seq._readonly = True
608
609 - def item(self, index):
610 """ 611 (DOM) 612 Used to retrieve the properties that have been explicitly set in 613 this declaration block. The order of the properties retrieved using 614 this method does not have to be the order in which they were set. 615 This method can be used to iterate over all properties in this 616 declaration block. 617 618 index 619 of the property to retrieve, negative values behave like 620 negative indexes on Python lists, so -1 is the last element 621 622 returns the name of the property at this ordinal position. The 623 empty string if no property exists at this position. 624 625 ATTENTION: 626 Only properties with a different name are counted. If two 627 properties with the same name are present in this declaration 628 only the effective one is included. 629 630 ``item()`` and ``length`` work on the same set here. 631 """ 632 names = list(self.__nnames()) 633 try: 634 return names[index] 635 except IndexError: 636 return u''
637 638 length = property(lambda self: len(self.__nnames()), 639 doc="(DOM) The number of distinct properties that have been explicitly\ 640 in this declaration block. The range of valid indices is 0 to\ 641 length-1 inclusive. These are properties with a different ``name``\ 642 only. ``item()`` and ``length`` work on the same set here.") 643
644 - def __repr__(self):
645 return "cssutils.css.%s(cssText=%r)" % ( 646 self.__class__.__name__, self.getCssText(separator=u' '))
647
648 - def __str__(self):
649 return "<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % ( 650 self.__class__.__name__, self.length, 651 len(self.getProperties(all=True)), id(self))
652