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