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', 'SameNamePropertyList'] 
 52  __docformat__ = 'restructuredtext' 
 53  __author__ = '$LastChangedBy: cthedot $' 
 54  __date__ = '$LastChangedDate: 2007-08-29 13:51:12 +0200 (Mi, 29 Aug 2007) $' 
 55  __version__ = '$LastChangedRevision: 296 $' 
 56   
 57  import xml.dom 
 58   
 59  import cssutils 
 60  from cssproperties import CSS2Properties 
 61  from property import _Property as 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: of type DOMString 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;]* Property: Value? 116 """
117 - def __init__(self, parentRule=None, cssText=u'', readonly=False):
118 """ 119 parentRule 120 The CSS rule that contains this declaration block or 121 None if this CSSStyleDeclaration is not attached to a CSSRule. 122 readonly 123 defaults to False 124 """ 125 super(CSSStyleDeclaration, self).__init__() 126 127 self.valid = True 128 129 self.seq = [] 130 self.parentRule = parentRule 131 self.cssText = cssText 132 self._readonly = readonly
133 134
135 - def __setattr__(self, n, v):
136 """ 137 Prevent setting of unknown properties on CSSStyleDeclaration 138 which would not work anyway. For these 139 ``CSSStyleDeclaration.setProperty`` MUST be called explicitly! 140 141 TODO: 142 implementation of known is not really nice, any alternative? 143 """ 144 known = ['_tokenizer', '_log', '_ttypes', 145 'valid', 'seq', 'parentRule', '_parentRule', 'cssText', 146 '_readonly'] 147 known.extend(CSS2Properties._properties) 148 if n in known: 149 super(CSSStyleDeclaration, self).__setattr__(n, v) 150 else: 151 raise AttributeError( 152 'Unknown CSS Property, ``CSSStyleDeclaration.setProperty("%s")`` MUST be used.' 153 % n)
154
155 - def __iter__(self):
156 "CSSStyleDeclaration is iterable" 157 return CSSStyleDeclaration.__items(self)
158
159 - def __items(self):
160 """ 161 the iterator 162 163 returns in contrast to calling item(index) property objects 164 """ 165 props = [] 166 for x in self.seq: 167 if isinstance(x, SameNamePropertyList): 168 for y in x: 169 props.append(y) 170 le = len(props) 171 for i in range (0, le): 172 yield props[i]
173 174 # overwritten accessor functions for CSS2Properties' properties
175 - def _getP(self, CSSName):
176 """ 177 (DOM CSS2Properties) 178 Overwritten here and effectively the same as 179 ``self.getPropertyValue(CSSname)``. 180 181 Parameter is in CSSname format ('font-style'), see CSS2Properties. 182 183 Example:: 184 185 >>> style = CSSStyleDeclaration(cssText='font-style:italic;') 186 >>> print style.fontStyle 187 italic 188 189 """ 190 return self.getPropertyValue(CSSName)
191
192 - def _setP(self, CSSName, value):
193 """ 194 (DOM CSS2Properties) 195 Overwritten here and effectively the same as 196 ``self.setProperty(CSSname, value)``. 197 198 Only known CSS2Properties may be set this way, otherwise an 199 AttributeError is raised. 200 For these unknown properties ``setPropertyValue(CSSname, value)`` 201 has to be called explicitly. 202 Also setting the priority of properties needs to be done with a 203 call like ``setPropertyValue(CSSname, value, priority)``. 204 205 Example:: 206 207 >>> style = CSSStyleDeclaration() 208 >>> style.fontStyle = 'italic' 209 >>> # or 210 >>> style.setProperty('font-style', 'italic', '!important') 211 212 """ 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
237 - def _getCssText(self):
238 """ 239 returns serialized property cssText 240 """ 241 return cssutils.ser.do_css_CSSStyleDeclaration(self)
242
243 - def _setCssText(self, cssText):
244 """ 245 Setting this attribute will result in the parsing of the new value 246 and resetting of all the properties in the declaration block 247 including the removal or addition of properties. 248 249 DOMException on setting 250 251 - NO_MODIFICATION_ALLOWED_ERR: (self) 252 Raised if this declaration is readonly or a property is readonly. 253 - SYNTAX_ERR: (self) 254 Raised if the specified CSS string value has a syntax error and 255 is unparsable. 256 """ 257 def ignoreuptopropend(i, tokens): 258 "returns position of ending ;" 259 ignoredtokens, endi = self._tokensupto( 260 tokens[i:], propertypriorityendonly=True) 261 if ignoredtokens: 262 ignored = ''.join([x.value for x in ignoredtokens]) 263 self._log.error(u'CSSStyleDeclaration: Ignored: "%s".' % 264 self._valuestr(ignoredtokens), t) 265 return endi + 1
266 267 self._checkReadonly() 268 tokens = self._tokenize(cssText) 269 270 newseq = [] 271 i, imax = 0, len(tokens) 272 while i < imax: 273 t = tokens[i] 274 if self._ttypes.S == t.type: # ignore 275 pass 276 elif self._ttypes.COMMENT == t.type: # just add 277 newseq.append(cssutils.css.CSSComment(t)) 278 else: 279 # name upto ":" (or ; -> error) 280 nametokens, endi = self._tokensupto( 281 tokens[i:], propertynameendonly=True) 282 i += endi 283 284 shouldbecolon = nametokens.pop() 285 if shouldbecolon.value == u':': # OK: exclude ending : 286 i += 1 287 elif shouldbecolon.value == u';': # ERROR: premature ; 288 self._log.error( 289 u'CSSStyleDeclaration: Incomplete Property starting here: %s.' % 290 self._valuestr(tokens[i-1:]), t) 291 i += 1 # exclude ending : 292 continue 293 else: # ERROR: no : 294 self._log.error( 295 u'CSSStyleDeclaration: No Propertyname and/or ":" found: %s.' % 296 self._valuestr(tokens[i:]), t) 297 i += ignoreuptopropend(i, tokens) 298 continue 299 300 for x in nametokens: 301 if x.type == self._ttypes.IDENT: 302 break 303 else: # ERROR: no name 304 self._log.error( 305 u'CSSStyleDeclaration: No Propertyname found: %s.' 306 % self._valuestr(tokens[i-1:]), t) 307 i += ignoreuptopropend(i, tokens) 308 continue 309 310 # value upto ";" or "!important" or end 311 valuetokens, endi = self._tokensupto( 312 tokens[i:], propertyvalueendonly=True) 313 i += endi 314 if valuetokens and \ 315 valuetokens[-1].type == self._ttypes.SEMICOLON: 316 del valuetokens[-1] # exclude ending ; 317 prioritytokens = None 318 elif valuetokens and \ 319 valuetokens[-1].type == self._ttypes.IMPORTANT_SYM: 320 del valuetokens[-1] # exclude !important 321 322 # priority upto ; or end 323 prioritytokens, endi = self._tokensupto( 324 tokens[i:], propertypriorityendonly=True) 325 i += endi 326 327 if prioritytokens and prioritytokens[-1].type == \ 328 self._ttypes.SEMICOLON: 329 del prioritytokens[-1] # exclude ending ; 330 elif not valuetokens: 331 self._log.error(u'CSSStyleDeclaration: No property value: %s' 332 % self._valuestr(cssText)) 333 i += ignoreuptopropend(i, tokens) 334 continue 335 else: 336 prioritytokens = None 337 338 self.setProperty(nametokens, valuetokens, prioritytokens, 339 overwrite=False, _seq=newseq) 340 i += 1 341 342 self.seq = newseq
343 344 cssText = property(_getCssText, _setCssText, 345 doc="(DOM) The parsable textual representation of the declaration\ 346 block excluding the surrounding curly braces.") 347
348 - def getCssText(self, separator=None):
349 """ 350 returns serialized property cssText, each property separated by 351 given ``separator`` which may e.g. be u'' to be able to use 352 cssText directly in an HTML style attribute. ";" is always part of 353 each property (except the last one) and can **not** be set with 354 separator! 355 """ 356 return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
357
358 - def _getLength(self):
359 return len([x for x in self.seq if isinstance(x, SameNamePropertyList)])
360 361 length = property(_getLength, 362 doc="(DOM) the number of properties that have been explicitly set\ 363 in this declaration block. The range of valid indices is 0 to\ 364 length-1 inclusive.") 365
366 - def _getParentRule(self):
367 return self._parentRule
368
369 - def _setParentRule(self, parentRule):
370 self._parentRule = parentRule
371 372 parentRule = property(_getParentRule, _setParentRule, 373 doc="(DOM) The CSS rule that contains this declaration block or\ 374 None if this CSSStyleDeclaration is not attached to a CSSRule.") 375
376 - def getPropertyCSSValue(self, name):
377 """ 378 (DOM) 379 Used to retrieve the object representation of the value of a CSS 380 property if it has been explicitly set within this declaration 381 block. This method returns None if the property is a shorthand 382 property. Shorthand property values can only be accessed and 383 modified as strings, using the getPropertyValue and setProperty 384 methods. 385 386 name 387 of the CSS property 388 389 The name will be normalized (lowercase, no simple escapes) so 390 "color", "COLOR" or "C\olor" are all equivalent 391 392 returns CSSValue, the value of the property if it has been 393 explicitly set for this declaration block. Returns None if the 394 property has not been set. 395 396 for more on shorthand properties see 397 http://www.dustindiaz.com/css-shorthand/ 398 """ 399 SHORTHAND = [ 400 u'background', 401 u'border', 402 u'border-left', u'border-right', 403 u'border-top', u'border-bottom', 404 u'border-color', u'border-style', u'border-width', 405 u'cue', 406 u'font', 407 u'list-style', 408 u'margin', 409 u'outline', 410 u'padding', 411 u'pause'] 412 413 normalname = self._normalize(name) 414 415 if normalname in SHORTHAND: 416 self._log.debug( 417 u'CSSValue for shorthand property "%s" should be None, this may be implemented later.' % 418 normalname, neverraise=True) 419 420 for pl in self.seq: 421 if isinstance(pl, SameNamePropertyList) and \ 422 pl.name == normalname: 423 return pl[pl._currentIndex()].cssValue
424
425 - def getPropertyValue(self, name):
426 """ 427 (DOM) 428 Used to retrieve the value of a CSS property if it has been 429 explicitly set within this declaration block. 430 431 name 432 of the CSS property 433 434 The name will be normalized (lowercase, no simple escapes) so 435 "color", "COLOR" or "C\olor" are all equivalent 436 437 returns the value of the property if it has been explicitly set 438 for this declaration block. Returns the empty string if the 439 property has not been set. 440 """ 441 normalname = self._normalize(name) 442 443 for pl in self.seq: 444 if isinstance(pl, SameNamePropertyList) and \ 445 pl.name == normalname: 446 return pl[pl._currentIndex()].cssValue._value 447 return u''
448
449 - def getPropertyPriority(self, name):
450 """ 451 (DOM) 452 Used to retrieve the priority of a CSS property (e.g. the 453 "important" qualifier) if the property has been explicitly set in 454 this declaration block. 455 456 name 457 of the CSS property 458 459 The name will be normalized (lowercase, no simple escapes) so 460 "color", "COLOR" or "C\olor" are all equivalent 461 462 returns a string representing the priority (e.g. "important") if 463 one exists. The empty string if none exists. 464 """ 465 normalname = self._normalize(name) 466 467 for pl in self.seq: 468 if isinstance(pl, SameNamePropertyList) and \ 469 pl.name == normalname: 470 return pl[pl._currentIndex()].priority 471 return u''
472
473 - def getSameNamePropertyList(self, name):
474 """ 475 (cssutils) EXPERIMENTAL 476 Used to retrieve all properties set with this name. For cases where 477 a property is set multiple times with different values or 478 priorities for different UAs:: 479 480 background: url(1.gif) fixed; 481 background: url(2.gif) scroll; 482 483 name 484 of the CSS property 485 486 The name will be normalized (lowercase, no simple escapes) so 487 "color", "COLOR" or "C\olor" are all equivalent 488 489 Returns the SameNamePropertyList object if available for the given 490 property name, else returns ``None``. 491 """ 492 normalname = self._normalize(name) 493 494 for pl in self.seq: 495 if isinstance(pl, SameNamePropertyList) and \ 496 pl.name == normalname: 497 return pl
498
499 - def item(self, index):
500 """ 501 (DOM) 502 Used to retrieve the properties that have been explicitly set in 503 this declaration block. The order of the properties retrieved using 504 this method does not have to be the order in which they were set. 505 This method can be used to iterate over all properties in this 506 declaration block. 507 508 index 509 of the property to retrieve, negative values behave like 510 negative indexes on Python lists, so -1 is the last element 511 512 returns the name of the property at this ordinal position. The 513 empty string if no property exists at this position. 514 """ 515 properties = [x.name for x in self.seq 516 if isinstance(x, SameNamePropertyList)] 517 try: 518 return properties[index] 519 except IndexError: 520 return u''
521
522 - def removeProperty(self, name):
523 """ 524 (DOM) 525 Used to remove a CSS property if it has been explicitly set within 526 this declaration block. 527 528 name 529 of the CSS property to remove 530 531 The name will be normalized (lowercase, no simple escapes) so 532 "color", "COLOR" or "C\olor" are all equivalent 533 534 returns the value of the property if it has been explicitly set for 535 this declaration block. Returns the empty string if the property 536 has not been set or the property name does not correspond to a 537 known CSS property 538 539 raises DOMException 540 541 - NO_MODIFICATION_ALLOWED_ERR: (self) 542 Raised if this declaration is readonly or the property is 543 readonly. 544 """ 545 self._checkReadonly() 546 547 normalname = self._normalize(name) 548 549 r = u'' 550 for i, pl in enumerate(self.seq): 551 if isinstance(pl, SameNamePropertyList) and \ 552 pl.name == normalname: 553 r = pl[pl._currentIndex()].cssValue._value 554 del self.seq[i] 555 break 556 557 return r
558
559 - def setProperty(self, name, value, priority=None, overwrite=True, 560 _seq=None):
561 """ 562 (DOM) 563 Used to set a property value and priority within this declaration 564 block. 565 566 name 567 of the CSS property to set 568 (in W3C DOM the parameter is called "propertyName") 569 will be normalized in the Property 570 571 value 572 the new value of the property 573 priority 574 the optional priority of the property (e.g. "important") 575 _seq 576 used by self._setCssText only as in temp seq 577 578 DOMException on setting 579 580 - SYNTAX_ERR: (self) 581 Raised if the specified value has a syntax error and is 582 unparsable. 583 - NO_MODIFICATION_ALLOWED_ERR: (self) 584 Raised if this declaration is readonly or the property is 585 readonly. 586 """ 587 self._checkReadonly() 588 589 if _seq is None: # seq to update 590 _seq = self.seq 591 592 newp = Property(name, value, priority) 593 if newp.name and newp.cssValue: 594 newnormalname = newp.normalname 595 596 # index of pl to exchange or append to, maybe 597 index = -1 598 for i, pl in enumerate(_seq): 599 if isinstance(pl, SameNamePropertyList) and \ 600 pl.name == newnormalname: 601 index = i 602 break 603 604 if index == -1: 605 # not yet in _seq -> append 606 newpl = SameNamePropertyList(newnormalname) 607 newpl.append(newp) 608 _seq.append(newpl) 609 else: 610 if overwrite: 611 # empty proplist and add new property 612 del _seq[index][:] 613 _seq[index].append(newp) 614 else: 615 # append to pl 616 _seq[index].append(newp)
617
618 - def __repr__(self):
619 return "cssutils.css.%s()" % ( 620 self.__class__.__name__)
621
622 - def __str__(self):
623 return "<cssutils.css.%s object length=%r at 0x%x>" % ( 624 self.__class__.__name__, self.length, id(self))
625 626
627 -class SameNamePropertyList(list):
628 """ 629 (cssutils) EXPERIMENTAL 630 631 A list of properties with the same normalname. Used for equivelant 632 properties like color, c\olor, co\lor. 633 To get the actual Property (latest with the highest priority) use 634 SameNamePropertyList[SameNamePropertyList._currentIndex()]. 635 636 Properties 637 ========== 638 name 639 normalized Property name (e.g. ``color``) 640 641 see ``token.Token`` for details on "normalized" 642 643 Useful only if all similar named properties should be kept after 644 parsing. See ``Serializer.prefs.keepAllProperties`` 645 """
646 - def __init__(self, name):
648
649 - def _currentIndex(self):
650 """ 651 returns index of current value of this property 652 used by serializer and getValue and getPriority 653 """ 654 importants = [i for (i, p) in enumerate(self) 655 if p.priority] 656 if importants: 657 return importants[-1] 658 else: 659 normals = [i for (i, p) in enumerate(self) 660 if not p.priority] 661 if normals: 662 return normals[-1]
663
664 - def __repr__(self):
665 return "cssutils.css.%s(name=%r)" % ( 666 self.__class__.__name__, self.name)
667
668 - def __str__(self):
669 return "<cssutils.css.%s object name=%r at 0x%x>" % ( 670 self.__class__.__name__, self.name, id(self))
671 672 673 if __name__ == '__main__': 674 pass 675