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