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