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