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