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