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-15 21:56:10 +0100 (Di, 15 Jan 2008) $' 
 56  __version__ = '$LastChangedRevision: 859 $' 
 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, parentRule=None, cssText=u'', readonly=False):
122 """ 123 parentRule 124 The CSS rule that contains this declaration block or 125 None if this CSSStyleDeclaration is not attached to a CSSRule. 126 cssText 127 Shortcut, sets CSSStyleDeclaration.cssText 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 **cssutils returns "!important" if present.** 481 482 name 483 of the CSS property, always lowercase (even if not normalized) 484 normalize 485 if True (DEFAULT) name will be normalized (lowercase, no simple 486 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 487 488 If False may return **NOT** the effective value but the effective 489 for the unnormalized name. 490 """ 491 p = self.getProperty(name, normalize) 492 if p: 493 return p.priority 494 else: 495 return u''
496
497 - def removeProperty(self, name, normalize=True):
498 """ 499 (DOM) 500 Used to remove a CSS property if it has been explicitly set within 501 this declaration block. 502 503 Returns the value of the property if it has been explicitly set for 504 this declaration block. Returns the empty string if the property 505 has not been set or the property name does not correspond to a 506 known CSS property 507 508 name 509 of the CSS property 510 normalize 511 if True (DEFAULT) name will be normalized (lowercase, no simple 512 escapes) so "color", "COLOR" or "C\olor" will all be equivalent. 513 The effective Property value is returned and *all* Properties 514 with ``Property.name == name`` are removed. 515 516 If False may return **NOT** the effective value but the effective 517 for the unnormalized ``name`` only. Also only the Properties with 518 the literal name ``name`` are removed. 519 520 raises DOMException 521 522 - NO_MODIFICATION_ALLOWED_ERR: (self) 523 Raised if this declaration is readonly or the property is 524 readonly. 525 """ 526 self._checkReadonly() 527 r = self.getPropertyValue(name, normalize=normalize) 528 if normalize: 529 # remove all properties with name == nname 530 nname = self._normalize(name) 531 newseq = [x for x in self.seq 532 if not(isinstance(x, Property) and x.name == nname)] 533 else: 534 # remove all properties with literalname == name 535 newseq = [x for x in self.seq 536 if not(isinstance(x, Property) and x.literalname == name)] 537 self.seq = newseq 538 return r
539
540 - def setProperty(self, name, value=None, priority=u'', normalize=True):
541 """ 542 (DOM) 543 Used to set a property value and priority within this declaration 544 block. 545 546 name 547 of the CSS property to set (in W3C DOM the parameter is called 548 "propertyName"), always lowercase (even if not normalized) 549 550 If a property with this name is present it will be reset 551 552 cssutils also allowed name to be a Property object, all other 553 parameter are ignored in this case 554 555 value 556 the new value of the property, omit if name is already a Property 557 priority 558 the optional priority of the property (e.g. "important") 559 normalize 560 if True (DEFAULT) name will be normalized (lowercase, no simple 561 escapes) so "color", "COLOR" or "C\olor" will all be equivalent 562 563 DOMException on setting 564 565 - SYNTAX_ERR: (self) 566 Raised if the specified value has a syntax error and is 567 unparsable. 568 - NO_MODIFICATION_ALLOWED_ERR: (self) 569 Raised if this declaration is readonly or the property is 570 readonly. 571 """ 572 self._checkReadonly() 573 574 if isinstance(name, Property): 575 newp = name 576 name = newp.literalname 577 else: 578 newp = Property(name, value, priority) 579 if not newp.wellformed: 580 self._log.warn(u'Invalid Property: %s: %s %s' 581 % (name, value, priority)) 582 else: 583 nname = self._normalize(name) 584 properties = self.getProperties(name, all=(not normalize)) 585 for property in reversed(properties): 586 if normalize and property.name == nname: 587 property.cssValue = newp.cssValue.cssText 588 property.priority = newp.priority 589 break 590 elif property.literalname == name: 591 property.cssValue = newp.cssValue.cssText 592 property.priority = newp.priority 593 break 594 else: 595 self.seq.append(newp)
596
597 - def item(self, index):
598 """ 599 (DOM) 600 Used to retrieve the properties that have been explicitly set in 601 this declaration block. The order of the properties retrieved using 602 this method does not have to be the order in which they were set. 603 This method can be used to iterate over all properties in this 604 declaration block. 605 606 index 607 of the property to retrieve, negative values behave like 608 negative indexes on Python lists, so -1 is the last element 609 610 returns the name of the property at this ordinal position. The 611 empty string if no property exists at this position. 612 613 ATTENTION: 614 Only properties with a different name are counted. If two 615 properties with the same name are present in this declaration 616 only the effective one is included. 617 618 ``item()`` and ``length`` work on the same set here. 619 """ 620 names = list(self.__nnames()) 621 try: 622 return names[index] 623 except IndexError: 624 return u''
625 626 length = property(lambda self: len(self.__nnames()), 627 doc="(DOM) The number of distinct properties that have been explicitly\ 628 in this declaration block. The range of valid indices is 0 to\ 629 length-1 inclusive. These are properties with a different ``name``\ 630 only. ``item()`` and ``length`` work on the same set here.") 631
632 - def __repr__(self):
633 return "cssutils.css.%s(cssText=%r)" % ( 634 self.__class__.__name__, self.getCssText(separator=u' '))
635
636 - def __str__(self):
637 return "<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % ( 638 self.__class__.__name__, self.length, 639 len(self.getProperties(all=True)), id(self))
640