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', 'Property']
     52  __docformat__ = 'restructuredtext'
     53  __author__ = '$LastChangedBy: cthedot $'
     54  __date__ = '$LastChangedDate: 2007-10-27 22:27:40 +0200 (Sa, 27 Okt 2007) $'
     55  __version__ = '$LastChangedRevision: 590 $'
     56
     57  import xml.dom
     58  import cssutils
     59  from cssproperties import CSS2Properties
     60  from property import Property
     61
     62 -class CSSStyleDeclaration(CSS2Properties, cssutils.util.Base):
     63      """
     64      The CSSStyleDeclaration class represents a single CSS declaration
     65      block. This class may be used to determine the style properties
     66      currently set in a block or to set style properties explicitly
     67      within the block.
     68
     69      While an implementation may not recognize all CSS properties within
     70      a CSS declaration block, it is expected to provide access to all
     71      specified properties in the style sheet through the
     72      CSSStyleDeclaration interface.
     73      Furthermore, implementations that support a specific level of CSS
     74      should correctly handle CSS shorthand properties for that level. For
     75      a further discussion of shorthand properties, see the CSS2Properties
     76      interface.
     77
     78      Additionally the CSS2Properties interface is implemented.
     79
     80      Properties
     81      ==========
     82      cssText: of type DOMString
     83          The parsable textual representation of the declaration block
     84          (excluding the surrounding curly braces). Setting this attribute
     85          will result in the parsing of the new value and resetting of the
     86          properties in the declaration block. It also allows the insertion
     87          of additional properties and their values into the block.
     88      length: of type unsigned long, readonly
     89          The number of properties that have been explicitly set in this
     90          declaration block. The range of valid indices is 0 to length-1
     91          inclusive.
     92      parentRule: of type CSSRule, readonly
     93          The CSS rule that contains this declaration block or None if this
     94          CSSStyleDeclaration is not attached to a CSSRule.
     95      seq: a list (cssutils)
     96          All parts of this style declaration including CSSComments
     97
     98      $css2propertyname
     99          All properties defined in the CSS2Properties class are available
    100          as direct properties of CSSStyleDeclaration with their respective
    101          DOM name, so e.g. ``fontStyle`` for property 'font-style'.
    102
    103          These may be used as::
    104
    105              >>> style = CSSStyleDeclaration(cssText='color: red')
    106              >>> style.color = 'green'
    107              >>> print style.color
    108              green
    109              >>> del style.color
    110              >>> print style.color # print empty string
    111
    112      Format
    113      ======
    114      [Property: Value;]* Property: Value?
    115      """
    116 -    def __init__(self, parentRule=None, cssText=u'', readonly=False):
    117          """
    118          parentRule
    119              The CSS rule that contains this declaration block or
    120              None if this CSSStyleDeclaration is not attached to a CSSRule.
    121          readonly
    122              defaults to False
    123          """
    124          super(CSSStyleDeclaration, self).__init__()
    125          self.valid = True
    126          self.seq = []
    127          self.parentRule = parentRule
    128          self.cssText = cssText
    129          self._readonly = readonly
    130
    131 -    def __setattr__(self, n, v):
    132          """
    133          Prevent setting of unknown properties on CSSStyleDeclaration
    134          which would not work anyway. For these
    135          ``CSSStyleDeclaration.setProperty`` MUST be called explicitly!
    136
    137          TODO:
    138              implementation of known is not really nice, any alternative?
    139          """
    140          known = ['_tokenizer', '_log', '_ttypes',
    141                   'valid', 'seq', 'parentRule', '_parentRule', 'cssText',
    142                   '_readonly']
    143          known.extend(CSS2Properties._properties)
    144          if n in known:
    145              super(CSSStyleDeclaration, self).__setattr__(n, v)
    146          else:
    147              raise AttributeError(
    148                  'Unknown CSS Property, ``CSSStyleDeclaration.setProperty("%s")`` MUST be used.'
    149                  % n)
    150
    151 -    def __iter__(self):
    152          "CSSStyleDeclaration is iterable, see __items()"
    153          return CSSStyleDeclaration.__items(self)
    154
    155 -    def __items(self):
    156          """
    157          the iterator
    158
    159          returns in contrast to calling item(index) all property objects so
    160          effectively the same as ``getProperties(all=True)``
    161          """
    162          properties = self.getProperties(all=True)
    163          for property in properties:
    164              yield property
    165
    166      # overwritten accessor functions for CSS2Properties' properties
    167 -    def _getP(self, CSSName):
    168          """
    169          (DOM CSS2Properties)
    170          Overwritten here and effectively the same as
    171          ``self.getPropertyValue(CSSname)``.
    172
    173          Parameter is in CSSname format ('font-style'), see CSS2Properties.
    174
    175          Example::
    176
    177              >>> style = CSSStyleDeclaration(cssText='font-style:italic;')
    178              >>> print style.fontStyle
    179              italic
    180
    181          """
    182          return self.getPropertyValue(CSSName)
    183
    184 -    def _setP(self, CSSName, value):
    185          """
    186          (DOM CSS2Properties)
    187          Overwritten here and effectively the same as
    188          ``self.setProperty(CSSname, value)``.
    189
    190          Only known CSS2Properties may be set this way, otherwise an
    191          AttributeError is raised.
    192          For these unknown properties ``setPropertyValue(CSSname, value)``
    193          has to be called explicitly.
    194          Also setting the priority of properties needs to be done with a
    195          call like ``setPropertyValue(CSSname, value, priority)``.
    196
    197          Example::
    198
    199              >>> style = CSSStyleDeclaration()
    200              >>> style.fontStyle = 'italic'
    201              >>> # or
    202              >>> style.setProperty('font-style', 'italic', '!important')
    203
    204          """
    205          self.setProperty(CSSName, value)
    206          # TODO:
    207  #        if 'background-image' == CSSName:
    208  #            for p in self._properties():
    209  #                if p.name == 'background':
    210  #                    print p
    211  #            self.setProperty(CSSName, value)
    212  #        else:
    213  #            self.setProperty(CSSName, value)
    214
    215 -    def _delP(self, CSSName):
    216          """
    217          (cssutils only)
    218          Overwritten here and effectively the same as
    219          ``self.removeProperty(CSSname)``.
    220
    221          Example::
    222
    223              >>> style = CSSStyleDeclaration(cssText='font-style:italic;')
    224              >>> del style.fontStyle
    225              >>> print style.fontStyle # prints u''
    226
    227          """
    228          self.removeProperty(CSSName)
    229
    230 -    def _getCssText(self):
    231          """
    232          returns serialized property cssText
    233          """
    234          return cssutils.ser.do_css_CSSStyleDeclaration(self)
    235
    236 -    def _setCssText(self, cssText):
    237          """
    238          Setting this attribute will result in the parsing of the new value
    239          and resetting of all the properties in the declaration block
    240          including the removal or addition of properties.
    241
    242          DOMException on setting
    243
    244          - NO_MODIFICATION_ALLOWED_ERR: (self)
    245            Raised if this declaration is readonly or a property is readonly.
    246          - SYNTAX_ERR: (self)
    247            Raised if the specified CSS string value has a syntax error and
    248            is unparsable.
    249          """
    250          self._checkReadonly()
    251          tokenizer = self._tokenize2(cssText)
    252
    253          def _ident(expected, seq, token, tokenizer=None):
    254              # a property
    255              tokens = self._tokensupto2(tokenizer, starttoken=token,
    256                                         semicolon=True)
    257              if self._tokenvalue(tokens[-1]) == u';':
    258                  tokens.pop()
    259              property = Property()
    260              property.cssText = tokens
    261              if property.wellformed:
    262                  seq.append(property)
    263              else:
    264                  self._log.error(u'CSSStyleDeclaration: Syntax Error in Property: %s'
    265                                  % self._valuestr(tokens))
    266              return expected
    267
    268          # [Property: Value;]* Property: Value?
    269          newseq = []
    270          valid, expected = self._parse(expected=None,
    271              seq=newseq, tokenizer=tokenizer,
    272              productions={'IDENT': _ident})
    273
    274          # valid set by parse
    275          # no post conditions
    276          if valid:
    277              self.seq = newseq
    278
    279      cssText = property(_getCssText, _setCssText,
    280          doc="(DOM) A parsable textual representation of the declaration\
    281          block excluding the surrounding curly braces.")
    282
    283 -    def getCssText(self, separator=None):
    284          """
    285          returns serialized property cssText, each property separated by
    286          given ``separator`` which may e.g. be u'' to be able to use
    287          cssText directly in an HTML style attribute. ";" is always part of
    288          each property (except the last one) and can **not** be set with
    289          separator!
    290          """
    291          return cssutils.ser.do_css_CSSStyleDeclaration(self, separator)
    292
    293 -    def _getParentRule(self):
    294          return self._parentRule
    295
    296 -    def _setParentRule(self, parentRule):
    297          self._parentRule = parentRule
    298
    299      parentRule = property(_getParentRule, _setParentRule,
    300          doc="(DOM) The CSS rule that contains this declaration block or\
    301          None if this CSSStyleDeclaration is not attached to a CSSRule.")
    302
    303 -    def getProperties(self, name=None, all=False):
    304          """
    305          returns a list of Property objects set in this declaration in order
    306          they have been set e.g. in the original stylesheet
    307
    308          name
    309              optional name of properties which are requested (a filter).
    310              Only properties with this (normalized) name are returned.
    311          all=False
    312              if False (DEFAULT) only the effective properties (the ones set
    313              last) are returned. So if a name is given only one property
    314              is returned.
    315
    316              if True all properties including properties set multiple times with
    317              different values or priorities for different UAs are returned.
    318          """
    319          nname = self._normalize(name)
    320          properties = []
    321          done = set()
    322          for x in reversed(self.seq):
    323              if isinstance(x, Property):
    324                  isname = (bool(nname) == False) or (x.normalname == nname)
    325                  stilltodo = x.normalname not in done
    326                  if isname and stilltodo:
    327                      properties.append(x)
    328                      if not all:
    329                          done.add(x.normalname)
    330
    331          properties.reverse()
    332          return properties
    333
    334 -    def getPropertyCSSValue(self, name, normalize=True):
    335          """
    336          (DOM)
    337          Used to retrieve the object representation of the value of a CSS
    338          property if it has been explicitly set within this declaration
    339          block. This method returns None if the property is a shorthand
    340          property. Shorthand property values can only be accessed and
    341          modified as strings, using the getPropertyValue and setProperty
    342          methods.
    343
    344          name
    345              of the CSS property
    346
    347              The name will be normalized (lowercase, no simple escapes) so
    348              "color", "COLOR" or "C\olor" are all equivalent
    349
    350          returns CSSValue, the value of the property if it has been
    351          explicitly set for this declaration block. Returns None if the
    352          property has not been set.
    353
    354          for more on shorthand properties see
    355              http://www.dustindiaz.com/css-shorthand/
    356          """
    357          nname = self._normalize(name)
    358          if nname in self._SHORTHANDPROPERTIES:
    359              self._log.debug(
    360                  u'CSSValue for shorthand property "%s" should be None, this may be implemented later.' %
    361                  nname, neverraise=True)
    362
    363          properties = self.getProperties(name, all=(not normalize))
    364          for property in reversed(properties):
    365              if normalize and property.normalname == nname:
    366                  return property.cssValue
    367              elif property.name == name:
    368                  return property.cssValue
    369          return None
    370
    371 -    def getPropertyValue(self, name, normalize=True):
    372          """
    373          (DOM)
    374          Used to retrieve the value of a CSS property if it has been
    375          explicitly set within this declaration block.
    376
    377          name
    378              of the CSS property
    379
    380              The name will be normalized (lowercase, no simple escapes) so
    381              "color", "COLOR" or "C\olor" are all equivalent
    382
    383          returns the value of the property if it has been explicitly set
    384          for this declaration block. Returns the empty string if the
    385          property has not been set.
    386          """
    387          nname = self._normalize(name)
    388          properties = self.getProperties(name, all=(not normalize))
    389          for property in reversed(properties):
    390              if normalize and property.normalname == nname:
    391                  return property.value
    392              elif property.name == name:
    393                  return property.value
    394          return u''
    395
    396 -    def getPropertyPriority(self, name, normalize=True):
    397          """
    398          (DOM)
    399          Used to retrieve the priority of a CSS property (e.g. the
    400          "important" qualifier) if the property has been explicitly set in
    401          this declaration block.
    402
    403          name
    404              of the CSS property
    405
    406              The name will be normalized (lowercase, no simple escapes) so
    407              "color", "COLOR" or "C\olor" are all equivalent
    408
    409          returns a string representing the priority (e.g. "important") if
    410          one exists. The empty string if none exists.
    411          """
    412          nname = self._normalize(name)
    413          properties = self.getProperties(name, all=(not normalize))
    414          for property in reversed(properties):
    415              if normalize and property.normalname == nname:
    416                  return property.priority
    417              elif property.name == name:
    418                  return property.priority
    419          return u''
    420
    421 -    def removeProperty(self, name, normalize=True):
    422          """
    423          (DOM)
    424          Used to remove a CSS property if it has been explicitly set within
    425          this declaration block.
    426
    427          name
    428              of the CSS property to remove
    429
    430              The name will be normalized (lowercase, no simple escapes) so
    431              "color", "COLOR" or "C\olor" are all equivalent
    432
    433          returns the value of the property if it has been explicitly set for
    434          this declaration block. Returns the empty string if the property
    435          has not been set or the property name does not correspond to a
    436          known CSS property
    437
    438          raises DOMException
    439
    440          - NO_MODIFICATION_ALLOWED_ERR: (self)
    441            Raised if this declaration is readonly or the property is
    442            readonly.
    443          """
    444          self._checkReadonly()
    445          nname = self._normalize(name)
    446          newseq = []
    447          r = u''
    448          isdone = False
    449          for x in reversed(self.seq):
    450              if isinstance(x, Property):
    451                  if not isdone and normalize and x.normalname == nname:
    452                      r = x.cssValue.cssText
    453                      isdone = True
    454                  elif not isdone and x.name == name:
    455                      r = x.cssValue.cssText
    456                      isdone = True
    457                  else:
    458                      newseq.append(x)
    459              else:
    460                  newseq.append(x)
    461          newseq.reverse()
    462          self.seq = newseq
    463          return r
    464
    465 -    def setProperty(self, name, value, priority=u'', normalize=True):
    466          """
    467          (DOM)
    468          Used to set a property value and priority within this declaration
    469          block.
    470
    471          name
    472              of the CSS property to set (in W3C DOM the parameter is called
    473              "propertyName")
    474
    475              If a property with this name is present it will be reset
    476
    477              The name is normalized if normalize=True
    478
    479          value
    480              the new value of the property
    481          priority
    482              the optional priority of the property (e.g. "important")
    483
    484          DOMException on setting
    485
    486          - SYNTAX_ERR: (self)
    487            Raised if the specified value has a syntax error and is
    488            unparsable.
    489          - NO_MODIFICATION_ALLOWED_ERR: (self)
    490            Raised if this declaration is readonly or the property is
    491            readonly.
    492          """
    493          self._checkReadonly()
    494
    495          newp = Property(name, value, priority)
    496          if not newp.wellformed:
    497              self._log.warn(u'Invalid Property: %s: %s %s'
    498                      % (name, value, priority))
    499          else:
    500              nname = self._normalize(name)
    501              properties = self.getProperties(name, all=(not normalize))
    502              for property in reversed(properties):
    503                  if normalize and property.normalname == nname:
    504                      property.cssValue = newp.cssValue.cssText
    505                      property.priority = newp.priority
    506                      break
    507                  elif property.name == name:
    508                      property.cssValue = newp.cssValue.cssText
    509                      property.priority = newp.priority
    510                      break
    511              else:
    512                  self.seq.append(newp)
    513
    514 -    def __nnames(self):
    515          nnames = set()
    516          for x in self.seq:
    517              if isinstance(x, Property):
    518                  nnames.add(x.normalname)
    519          return nnames
    520
    521 -    def item(self, index):
    522          """
    523          **NOTE**:
    524          Compare to ``for property in declaration`` which works on **all**
    525          properties set in this declaration and not just the effecitve ones.
    526
    527          (DOM)
    528          Used to retrieve the properties that have been explicitly set in
    529          this declaration block. The order of the properties retrieved using
    530          this method does not have to be the order in which they were set.
    531          This method can be used to iterate over all properties in this
    532          declaration block.
    533
    534          index
    535              of the property to retrieve, negative values behave like
    536              negative indexes on Python lists, so -1 is the last element
    537
    538          returns the name of the property at this ordinal position. The
    539          empty string if no property exists at this position.
    540
    541          ATTENTION:
    542          Only properties with a different normalname are counted. If two
    543          properties with the same normalname are present in this declaration
    544          only the last set (and effectively *in style*) is used.
    545
    546          ``item()`` and ``length`` work on the same set here.
    547          """
    548          nnames = self.__nnames()
    549          orderednnames = []
    550          for x in reversed(self.seq):
    551              nname = x.normalname
    552              if isinstance(x, Property) and nname in nnames:
    553                  nnames.remove(nname)
    554                  orderednnames.append(nname)
    555          orderednnames.reverse()
    556          try:
    557              return orderednnames[index]
    558          except IndexError:
    559              return u''
    560
    561 -    def _getLength(self):
    562          return len(self.__nnames())
    563
    564      length = property(_getLength,
    565          doc="(DOM) The number of distince properties that have been explicitly\
    566          in this declaration block. The range of valid indices is 0 to\
    567          length-1 inclusive. These are properties with the same ``normalname``\
    568          only. ``item()`` and ``length`` work on the same set here.")
    569
    570 -    def __repr__(self):
    571          return "cssutils.css.%s()" % (
    572                  self.__class__.__name__)
    573
    574 -    def __str__(self):
    575          return "<cssutils.css.%s object length=%r (all: %r) at 0x%x>" % (
    576                  self.__class__.__name__, self.length,
    577                  len(self.getProperties(all=True)), id(self))
    578