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 1116 2008-03-05 13:52:23Z 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.Base):
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 = [] 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', '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 x in reversed(self.seq): 181 if isinstance(x, Property) and not x.name in names: 182 names.append(x.name) 183 return reversed(names)
184 185 # overwritten accessor functions for CSS2Properties' properties
186 - def _getP(self, CSSName):
187 """ 188 (DOM CSS2Properties) 189 Overwritten here and effectively the same as 190 ``self.getPropertyValue(CSSname)``. 191 192 Parameter is in CSSname format ('font-style'), see CSS2Properties. 193 194 Example:: 195 196 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 197 >>> print style.fontStyle 198 italic 199 """ 200 return self.getPropertyValue(CSSName)
201
202 - def _setP(self, CSSName, value):
203 """ 204 (DOM CSS2Properties) 205 Overwritten here and effectively the same as 206 ``self.setProperty(CSSname, value)``. 207 208 Only known CSS2Properties may be set this way, otherwise an 209 AttributeError is raised. 210 For these unknown properties ``setPropertyValue(CSSname, value)`` 211 has to be called explicitly. 212 Also setting the priority of properties needs to be done with a 213 call like ``setPropertyValue(CSSname, value, priority)``. 214 215 Example:: 216 217 >>> style = CSSStyleDeclaration() 218 >>> style.fontStyle = 'italic' 219 >>> # or 220 >>> style.setProperty('font-style', 'italic', '!important') 221 """ 222 self.setProperty(CSSName, value)
223 # TODO: Shorthand ones 224
225 - def _delP(self, CSSName):
226 """ 227 (cssutils only) 228 Overwritten here and effectively the same as 229 ``self.removeProperty(CSSname)``. 230 231 Example:: 232 233 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 234 >>> del style.fontStyle 235 >>> print style.fontStyle # prints u'' 236 237 """ 238 self.removeProperty(CSSName)
239
240 - def _getCssText(self):
241 """ 242 returns serialized property cssText 243 """ 244 return cssutils.ser.do_css_CSSStyleDeclaration(self)
245
246 - def _setCssText(self, cssText):
247 """ 248 Setting this attribute will result in the parsing of the new value 249 and resetting of all the properties in the declaration block 250 including the removal or addition of properties. 251 252 DOMException on setting 253 254 - NO_MODIFICATION_ALLOWED_ERR: (self) 255 Raised if this declaration is readonly or a property is readonly. 256 - SYNTAX_ERR: (self) 257 Raised if the specified CSS string value has a syntax error and 258 is unparsable. 259 """ 260 self._checkReadonly() 261 tokenizer = self._tokenize2(cssText) 262 263 # for closures: must be a mutable 264 new = {'valid': True, 265 'wellformed': True, 266 'char': None} # for IE hack 267 def ident(expected, seq, token, tokenizer=None): 268 # a property 269 if new['char']: 270 # maybe an IE hack? 271 token = (token[0], u'%s%s' % (new['char'], token[1]), 272 token[2], token[3]) 273 274 tokens = self._tokensupto2(tokenizer, starttoken=token, 275 semicolon=True) 276 if self._tokenvalue(tokens[-1]) == u';': 277 tokens.pop() 278 property = Property() 279 property.cssText = tokens 280 if property.wellformed: 281 seq.append(property) 282 else: 283 self._log.error(u'CSSStyleDeclaration: Syntax Error in Property: %s' 284 % self._valuestr(tokens)) 285 286 new['char'] = None 287 return expected
288 289 def char(expected, seq, token, tokenizer=None): 290 # maybe an IE hack like "$color" 291 new['valid'] = False # wellformed is set later 292 self._log.error(u'CSSStyleDeclaration: Unexpected CHAR.', token) 293 c = self._tokenvalue(token) 294 if c in u'$': 295 self._log.warn(u'Trying to use (invalid) CHAR %r in Property name' % 296 c) 297 new['char'] = c 298 return expected 299 300 # [Property: Value;]* Property: Value? 301 newseq = [] 302 wellformed, expected = self._parse(expected=None, 303 seq=newseq, tokenizer=tokenizer, 304 productions={'IDENT': ident, 'CHAR': char}) 305 valid = new['valid'] 306 307 # wellformed set by parse 308 # post conditions 309 if new['char']: 310 valid = wellformed = False 311 self._log.error(u'Could not use unexpected CHAR %r' % new['char']) 312 313 if wellformed: 314 self.seq = newseq 315 316 cssText = property(_getCssText, _setCssText, 317 doc="(DOM) A parsable textual representation of the declaration\ 318 block excluding the surrounding curly braces.") 319
320 - def getCssText(self, separator=None):
321 """ 322 returns serialized property cssText, each property separated by 323 given ``separator`` which may e.g. be u'' to be able to use 324 cssText directly in an HTML style attribute. ";" is always part of 325 each property (except the last one) and can **not** be set with 326 separator! 327 """ 328 return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
329
330 - def _getParentRule(self):
331 return self._parentRule
332
333 - def _setParentRule(self, parentRule):
334 self._parentRule = parentRule
335 336 parentRule = property(_getParentRule, _setParentRule, 337 doc="(DOM) The CSS rule that contains this declaration block or\ 338 None if this CSSStyleDeclaration is not attached to a CSSRule.") 339
340 - def getProperties(self, name=None, all=False):
341 """ 342 Returns a list of Property objects set in this declaration. 343 344 name 345 optional name of properties which are requested (a filter). 346 Only properties with this **always normalized** name are returned. 347 all=False 348 if False (DEFAULT) only the effective properties (the ones set 349 last) are returned. If name is given a list with only one property 350 is returned. 351 352 if True all properties including properties set multiple times with 353 different values or priorities for different UAs are returned. 354 The order of the properties is fully kept as in the original 355 stylesheet. 356 """ 357 if name and not all: 358 # single prop but list 359 p = self.getProperty(name) 360 if p: 361 return [p] 362 else: 363 return [] 364 elif not all: 365 # effective Properties in name order 366 return [self.getProperty(name)for name in self.__nnames()] 367 else: 368 # all properties or all with this name 369 nname = self._normalize(name) 370 properties = [] 371 for x in self.seq: 372 if isinstance(x, Property) and ( 373 (bool(nname) == False) or (x.name == nname)): 374 properties.append(x) 375 return properties
376
377 - def getProperty(self, name, normalize=True):
378 """ 379 Returns the effective Property object. 380 381 name 382 of the CSS property, always lowercase (even if not normalized) 383 normalize 384 if True (DEFAULT) name will be normalized (lowercase, no simple 385 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 386 387 If False may return **NOT** the effective value but the effective 388 for the unnormalized name. 389 """ 390 nname = self._normalize(name) 391 found = None 392 for x in reversed(self.seq): 393 if isinstance(x, Property): 394 if (normalize and nname == x.name) or name == x.literalname: 395 if x.priority: 396 return x 397 elif not found: 398 found = x 399 return found
400
401 - def getPropertyCSSValue(self, name, normalize=True):
402 """ 403 Returns CSSValue, the value of the effective property if it has been 404 explicitly set for this declaration block. 405 406 name 407 of the CSS property, always lowercase (even if not normalized) 408 normalize 409 if True (DEFAULT) name will be normalized (lowercase, no simple 410 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 411 412 If False may return **NOT** the effective value but the effective 413 for the unnormalized name. 414 415 (DOM) 416 Used to retrieve the object representation of the value of a CSS 417 property if it has been explicitly set within this declaration 418 block. Returns None if the property has not been set. 419 420 (This method returns None if the property is a shorthand 421 property. Shorthand property values can only be accessed and 422 modified as strings, using the getPropertyValue and setProperty 423 methods.) 424 425 **cssutils currently always returns a CSSValue if the property is 426 set.** 427 428 for more on shorthand properties see 429 http://www.dustindiaz.com/css-shorthand/ 430 """ 431 nname = self._normalize(name) 432 if nname in self._SHORTHANDPROPERTIES: 433 self._log.info( 434 u'CSSValue for shorthand property "%s" should be None, this may be implemented later.' % 435 nname, neverraise=True) 436 437 p = self.getProperty(name, normalize) 438 if p: 439 return p.cssValue 440 else: 441 return None
442
443 - def getPropertyValue(self, name, normalize=True):
444 """ 445 Returns the value of the effective property if it has been explicitly 446 set for this declaration block. Returns the empty string if the 447 property has not been set. 448 449 name 450 of the CSS property, always lowercase (even if not normalized) 451 normalize 452 if True (DEFAULT) name will be normalized (lowercase, no simple 453 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 454 455 If False may return **NOT** the effective value but the effective 456 for the unnormalized name. 457 """ 458 p = self.getProperty(name, normalize) 459 if p: 460 return p.value 461 else: 462 return u''
463
464 - def getPropertyPriority(self, name, normalize=True):
465 """ 466 Returns the priority of the effective CSS property (e.g. the 467 "important" qualifier) if the property has been explicitly set in 468 this declaration block. The empty string if none exists. 469 470 name 471 of the CSS property, always lowercase (even if not normalized) 472 normalize 473 if True (DEFAULT) name will be normalized (lowercase, no simple 474 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 475 476 If False may return **NOT** the effective value but the effective 477 for the unnormalized name. 478 """ 479 p = self.getProperty(name, normalize) 480 if p: 481 return p.priority 482 else: 483 return u''
484
485 - def removeProperty(self, name, normalize=True):
486 """ 487 (DOM) 488 Used to remove a CSS property if it has been explicitly set within 489 this declaration block. 490 491 Returns the value of the property if it has been explicitly set for 492 this declaration block. Returns the empty string if the property 493 has not been set or the property name does not correspond to a 494 known CSS property 495 496 name 497 of the CSS property 498 normalize 499 if True (DEFAULT) name will be normalized (lowercase, no simple 500 escapes) so "color", "COLOR" or "C\olor" will all be equivalent. 501 The effective Property value is returned and *all* Properties 502 with ``Property.name == name`` are removed. 503 504 If False may return **NOT** the effective value but the effective 505 for the unnormalized ``name`` only. Also only the Properties with 506 the literal name ``name`` are removed. 507 508 raises DOMException 509 510 - NO_MODIFICATION_ALLOWED_ERR: (self) 511 Raised if this declaration is readonly or the property is 512 readonly. 513 """ 514 self._checkReadonly() 515 r = self.getPropertyValue(name, normalize=normalize) 516 if normalize: 517 # remove all properties with name == nname 518 nname = self._normalize(name) 519 newseq = [x for x in self.seq 520 if not(isinstance(x, Property) and x.name == nname)] 521 else: 522 # remove all properties with literalname == name 523 newseq = [x for x in self.seq 524 if not(isinstance(x, Property) and x.literalname == name)] 525 self.seq = newseq 526 return r
527
528 - def setProperty(self, name, value=None, priority=u'', normalize=True):
529 """ 530 (DOM) 531 Used to set a property value and priority within this declaration 532 block. 533 534 name 535 of the CSS property to set (in W3C DOM the parameter is called 536 "propertyName"), always lowercase (even if not normalized) 537 538 If a property with this name is present it will be reset 539 540 cssutils also allowed name to be a Property object, all other 541 parameter are ignored in this case 542 543 value 544 the new value of the property, omit if name is already a Property 545 priority 546 the optional priority of the property (e.g. "important") 547 normalize 548 if True (DEFAULT) name will be normalized (lowercase, no simple 549 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 550 551 DOMException on setting 552 553 - SYNTAX_ERR: (self) 554 Raised if the specified value has a syntax error and is 555 unparsable. 556 - NO_MODIFICATION_ALLOWED_ERR: (self) 557 Raised if this declaration is readonly or the property is 558 readonly. 559 """ 560 self._checkReadonly() 561 562 if isinstance(name, Property): 563 newp = name 564 name = newp.literalname 565 else: 566 newp = Property(name, value, priority) 567 if not newp.wellformed: 568 self._log.warn(u'Invalid Property: %s: %s %s' 569 % (name, value, priority)) 570 else: 571 nname = self._normalize(name) 572 properties = self.getProperties(name, all=(not normalize)) 573 for property in reversed(properties): 574 if normalize and property.name == nname: 575 property.cssValue = newp.cssValue.cssText 576 property.priority = newp.priority 577 break 578 elif property.literalname == name: 579 property.cssValue = newp.cssValue.cssText 580 property.priority = newp.priority 581 break 582 else: 583 self.seq.append(newp)
584
585 - def item(self, index):
586 """ 587 (DOM) 588 Used to retrieve the properties that have been explicitly set in 589 this declaration block. The order of the properties retrieved using 590 this method does not have to be the order in which they were set. 591 This method can be used to iterate over all properties in this 592 declaration block. 593 594 index 595 of the property to retrieve, negative values behave like 596 negative indexes on Python lists, so -1 is the last element 597 598 returns the name of the property at this ordinal position. The 599 empty string if no property exists at this position. 600 601 ATTENTION: 602 Only properties with a different name are counted. If two 603 properties with the same name are present in this declaration 604 only the effective one is included. 605 606 ``item()`` and ``length`` work on the same set here. 607 """ 608 names = list(self.__nnames()) 609 try: 610 return names[index] 611 except IndexError: 612 return u''
613 614 length = property(lambda self: len(self.__nnames()), 615 doc="(DOM) The number of distinct properties that have been explicitly\ 616 in this declaration block. The range of valid indices is 0 to\ 617 length-1 inclusive. These are properties with a different ``name``\ 618 only. ``item()`` and ``length`` work on the same set here.") 619
620 - def __repr__(self):
621 return "cssutils.css.%s(cssText=%r)" % ( 622 self.__class__.__name__, self.getCssText(separator=u' '))
623
624 - def __str__(self):
625 return "<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % ( 626 self.__class__.__name__, self.length, 627 len(self.getProperties(all=True)), id(self))
628