Package cssutils :: Package css :: Module cssstylesheet |
[hide private]
[frames] | no frames]

# Source Code for Module cssutils.css.cssstylesheet

      1  """
      2  CSSStyleSheet implements DOM Level 2 CSS CSSStyleSheet.
      3
      4  Partly also:
      5      - http://dev.w3.org/csswg/cssom/#the-cssstylesheet
      6      - http://www.w3.org/TR/2006/WD-css3-namespace-20060828/
      7
      8  TODO:
      9      - ownerRule and ownerNode
     10  """
     11  __all__ = ['CSSStyleSheet']
     12  __docformat__ = 'restructuredtext'
     13  __version__ = '$Id: cssstylesheet.py 1395 2008-07-27 13:30:13Z cthedot $'
     14
     15  import xml.dom
     16  import cssutils.stylesheets
     17  from cssutils.util import _Namespaces, _SimpleNamespaces, _readUrl
     18  from cssutils.helper import Deprecated
     19
     20 -class CSSStyleSheet(cssutils.stylesheets.StyleSheet):
     21      """
     22      The CSSStyleSheet interface represents a CSS style sheet.
     23
     24      Properties
     25      ==========
     26      CSSOM
     27      -----
     28      cssRules
     29          of type CSSRuleList, (DOM readonly)
     30      encoding
     31          reflects the encoding of an @charset rule or 'utf-8' (default)
     32          if set to ``None``
     33      ownerRule
     34          of type CSSRule, readonly. If this sheet is imported this is a ref
     35          to the @import rule that imports it.
     36
     37      Inherits properties from stylesheet.StyleSheet
     38
     39      cssutils
     40      --------
     41      cssText: string
     42          a textual representation of the stylesheet
     43      namespaces
     44          reflects set @namespace rules of this rule.
     45          A dict of {prefix: namespaceURI} mapping.
     46
     47      Format
     48      ======
     49      stylesheet
     50        : [ CHARSET_SYM S* STRING S* ';' ]?
     51          [S|CDO|CDC]* [ import [S|CDO|CDC]* ]*
     52          [ namespace [S|CDO|CDC]* ]* # according to @namespace WD
     53          [ [ ruleset | media | page ] [S|CDO|CDC]* ]*
     54      """
     55 -    def __init__(self, href=None, media=None, title=u'', disabled=None,
     56                   ownerNode=None, parentStyleSheet=None, readonly=False,
     57                   ownerRule=None):
     58          """
     59          init parameters are the same as for stylesheets.StyleSheet
     60          """
     61          super(CSSStyleSheet, self).__init__(
     62                  'text/css', href, media, title, disabled,
     63                  ownerNode, parentStyleSheet)
     64
     65          self._ownerRule = ownerRule
     66          self.cssRules = cssutils.css.CSSRuleList()
     67          self.cssRules.append = self.insertRule
     68          self.cssRules.extend = self.insertRule
     69          self._namespaces = _Namespaces(parentStyleSheet=self)
     70          self._readonly = readonly
     71
     72          # used only during setting cssText by parse*()
     73          self.__encodingOverride = None
     74          self._fetcher = None
     75
     76 -    def __iter__(self):
     77          "generator which iterates over cssRules."
     78          for rule in self.cssRules:
     79              yield rule
     80
     81 -    def _cleanNamespaces(self):
     82          "removes all namespace rules with same namespaceURI but last one set"
     83          rules = self.cssRules
     84          namespaceitems = self.namespaces.items()
     85          i = 0
     86          while i < len(rules):
     87              rule = rules[i]
     88              if rule.type == rule.NAMESPACE_RULE and \
     89                 (rule.prefix, rule.namespaceURI) not in namespaceitems:
     90                  self.deleteRule(i)
     91              else:
     92                  i += 1
     93
     94 -    def _getUsedURIs(self):
     95          "returns set of URIs used in the sheet"
     96          useduris = set()
     97          for r1 in self:
     98              if r1.STYLE_RULE == r1.type:
     99                  useduris.update(r1.selectorList._getUsedUris())
    100              elif r1.MEDIA_RULE == r1.type:
    101                  for r2 in r1:
    102                      if r2.type == r2.STYLE_RULE:
    103                          useduris.update(r2.selectorList._getUsedUris())
    104          return useduris
    105
    106 -    def _getCssText(self):
    107          return cssutils.ser.do_CSSStyleSheet(self)
    108
    109 -    def _setCssText(self, cssText):
    110          """
    111          (cssutils)
    112          Parses ``cssText`` and overwrites the whole stylesheet.
    113
    114          :param cssText:
    115              a parseable string or a tuple of (cssText, dict-of-namespaces)
    116          :Exceptions:
    117              - `NAMESPACE_ERR`:
    118                If a namespace prefix is found which is not declared.
    119              - `NO_MODIFICATION_ALLOWED_ERR`: (self)
    120                Raised if the rule is readonly.
    121              - `SYNTAX_ERR`:
    122                Raised if the specified CSS string value has a syntax error and
    123                is unparsable.
    124          """
    125          self._checkReadonly()
    126
    127          cssText, namespaces = self._splitNamespacesOff(cssText)
    128          if not namespaces:
    129              namespaces = _SimpleNamespaces()
    130
    131          tokenizer = self._tokenize2(cssText)
    132          newseq = [] #cssutils.css.CSSRuleList()
    133
    134          # for closures: must be a mutable
    135          new = {'encoding': None, # needed for setting encoding of @import rules
    136                 'namespaces': namespaces}
    137          def S(expected, seq, token, tokenizer=None):
    138              # @charset must be at absolute beginning of style sheet
    139              if expected == 0:
    140                  return 1
    141              else:
    142                  return expected
    143
    144          def COMMENT(expected, seq, token, tokenizer=None):
    145              "special: sets parent*"
    146              comment = cssutils.css.CSSComment([token],
    147                                  parentStyleSheet=self.parentStyleSheet)
    148              seq.append(comment)
    149              return expected
    150
    151          def charsetrule(expected, seq, token, tokenizer):
    152              rule = cssutils.css.CSSCharsetRule(parentStyleSheet=self)
    153              rule.cssText = self._tokensupto2(tokenizer, token)
    154              if expected > 0 or len(seq) > 0:
    155                  self._log.error(
    156                      u'CSSStylesheet: CSSCharsetRule only allowed at beginning of stylesheet.',
    157                      token, xml.dom.HierarchyRequestErr)
    158              else:
    159                  if rule.wellformed:
    160                      seq.append(rule)
    161                      new['encoding'] = rule.encoding
    162              return 1
    163
    164          def importrule(expected, seq, token, tokenizer):
    165              if new['encoding']:
    166                  # set temporarily as used by _resolveImport
    167                  # save newEncoding which have been set by resolveImport
    168                  self.__newEncoding = new['encoding']
    169
    170              rule = cssutils.css.CSSImportRule(parentStyleSheet=self)
    171              rule.cssText = self._tokensupto2(tokenizer, token)
    172              if expected > 1:
    173                  self._log.error(
    174                      u'CSSStylesheet: CSSImportRule not allowed here.',
    175                      token, xml.dom.HierarchyRequestErr)
    176              else:
    177                  if rule.wellformed:
    178                      #del rule._parentEncoding # remove as later it is read from this sheet!
    179                      seq.append(rule)
    180
    181              try:
    182                  # remove as only used temporarily but may not be set at all
    183                  del self.__newEncoding
    184              except AttributeError, e:
    185                  pass
    186
    187              return 1
    188
    189          def namespacerule(expected, seq, token, tokenizer):
    190              rule = cssutils.css.CSSNamespaceRule(
    191                                  cssText=self._tokensupto2(tokenizer, token),
    192                                  parentStyleSheet=self)
    193              if expected > 2:
    194                  self._log.error(
    195                      u'CSSStylesheet: CSSNamespaceRule not allowed here.',
    196                      token, xml.dom.HierarchyRequestErr)
    197              else:
    198                  if rule.wellformed:
    199                      seq.append(rule)
    200                      # temporary namespaces given to CSSStyleRule and @media
    201                      new['namespaces'][rule.prefix] = rule.namespaceURI
    202              return 2
    203
    204          def fontfacerule(expected, seq, token, tokenizer):
    205              rule = cssutils.css.CSSFontFaceRule(parentStyleSheet=self)
    206              rule.cssText = self._tokensupto2(tokenizer, token)
    207              if rule.wellformed:
    208                  seq.append(rule)
    209              return 3
    210
    211          def mediarule(expected, seq, token, tokenizer):
    212              rule = cssutils.css.CSSMediaRule()
    213              rule.cssText = (self._tokensupto2(tokenizer, token),
    214                              new['namespaces'])
    215              if rule.wellformed:
    216                  rule._parentStyleSheet=self
    217                  for r in rule:
    218                      r._parentStyleSheet=self
    219                  seq.append(rule)
    220              return 3
    221
    222          def pagerule(expected, seq, token, tokenizer):
    223              rule = cssutils.css.CSSPageRule(parentStyleSheet=self)
    224              rule.cssText = self._tokensupto2(tokenizer, token)
    225              if rule.wellformed:
    226                  seq.append(rule)
    227              return 3
    228
    229          def unknownrule(expected, seq, token, tokenizer):
    230              self._log.warn(
    231                      u'CSSStylesheet: Unknown @rule found.',
    232                      token, neverraise=True)
    233              rule = cssutils.css.CSSUnknownRule(parentStyleSheet=self)
    234              rule.cssText = self._tokensupto2(tokenizer, token)
    235              if rule.wellformed:
    236                  seq.append(rule)
    237              return expected
    238
    239          def ruleset(expected, seq, token, tokenizer):
    240              rule = cssutils.css.CSSStyleRule()
    241              rule.cssText = (self._tokensupto2(tokenizer, token),
    242                              new['namespaces'])
    243              if rule.wellformed:
    244                  rule._parentStyleSheet=self
    245                  seq.append(rule)
    246              return 3
    247
    248          # expected:
    249          # ['CHARSET', 'IMPORT', 'NAMESPACE', ('PAGE', 'MEDIA', ruleset)]
    250          wellformed, expected = self._parse(0, newseq, tokenizer,
    251              {'S': S,
    252               'COMMENT': COMMENT,
    253               'CDO': lambda *ignored: None,
    254               'CDC': lambda *ignored: None,
    255               'CHARSET_SYM': charsetrule,
    256               'FONT_FACE_SYM': fontfacerule,
    257               'IMPORT_SYM': importrule,
    258               'NAMESPACE_SYM': namespacerule,
    259               'PAGE_SYM': pagerule,
    260               'MEDIA_SYM': mediarule,
    261               'ATKEYWORD': unknownrule
    262               },
    263               default=ruleset)
    264
    265          if wellformed:
    266              del self.cssRules[:]
    267              for rule in newseq:
    268                  self.insertRule(rule, _clean=False)
    269              self._cleanNamespaces()
    270
    271      cssText = property(_getCssText, _setCssText,
    272              "(cssutils) a textual representation of the stylesheet")
    273
    274 -    def _resolveImport(self, url):
    275          """Read (encoding, enctype, decodedContent) from ``url`` for @import
    276          sheets."""
    277          try:
    278              # only available during parse of a complete sheet
    279              selfAsParentEncoding = self.__newEncoding
    280          except AttributeError:
    281              try:
    282                  # explicit @charset
    283                  selfAsParentEncoding = self.cssRules[0].encoding
    284              except (IndexError, AttributeError):
    285                  # default not UTF-8 but None!
    286                  selfAsParentEncoding = None
    287
    288          return _readUrl(url, fetcher=self._fetcher,
    289                          overrideEncoding=self.__encodingOverride,
    290                          parentEncoding=selfAsParentEncoding)
    291
    292 -    def _setCssTextWithEncodingOverride(self, cssText, encodingOverride=None,
    293                                          encoding=None):
    294          """Set cssText but use ``encodingOverride`` to overwrite detected
    295          encoding. This is used by parse and @import during setting of cssText.
    296
    297          If ``encoding`` is given use this but do not save it as encodingOverride"""
    298          if encodingOverride:
    299              # encoding during resolving of @import
    300              self.__encodingOverride = encodingOverride
    301
    302          self.__newEncoding = encoding # save for nested @import
    303          self.cssText = cssText
    304
    305          if encodingOverride:
    306              # set encodingOverride explicit again!
    307              self.encoding = self.__encodingOverride
    308              # remove?
    309              self.__encodingOverride = None
    310          elif encoding:
    311              # may e.g. be httpEncoding
    312              self.encoding = encoding
    313
    314 -    def _setFetcher(self, fetcher=None):
    315          """sets @import URL loader, if None the default is used"""
    316          self._fetcher = fetcher
    317
    318 -    def _setEncoding(self, encoding):
    319          """
    320          sets encoding of charset rule if present or inserts new charsetrule
    321          with given encoding. If encoding if None removes charsetrule if
    322          present.
    323          """
    324          try:
    325              rule = self.cssRules[0]
    326          except IndexError:
    327              rule = None
    328          if rule and rule.CHARSET_RULE == rule.type:
    329              if encoding:
    330                  rule.encoding = encoding
    331              else:
    332                  self.deleteRule(0)
    333          elif encoding:
    334              self.insertRule(cssutils.css.CSSCharsetRule(encoding=encoding), 0)
    335
    336 -    def _getEncoding(self):
    337          "return encoding if @charset rule if given or default of 'utf-8'"
    338          try:
    339              return self.cssRules[0].encoding
    340          except (IndexError, AttributeError):
    341              return 'utf-8'
    342
    343      encoding = property(_getEncoding, _setEncoding,
    344              "(cssutils) reflects the encoding of an @charset rule or 'UTF-8' (default) if set to ``None``")
    345
    346      namespaces = property(lambda self: self._namespaces,
    347                            doc="Namespaces used in this CSSStyleSheet.")
    348
    349 -    def add(self, rule):
    350          """
    351          Adds rule to stylesheet at appropriate position.
    352          Same as ``sheet.insertRule(rule, inOrder=True)``.
    353          """
    354          return self.insertRule(rule, index=None, inOrder=True)
    355
    356 -    def deleteRule(self, index):
    357          """
    358          Used to delete a rule from the style sheet.
    359
    360          :param index:
    361              of the rule to remove in the StyleSheet's rule list. For an
    362              index < 0 **no** INDEX_SIZE_ERR is raised but rules for
    363              normal Python lists are used. E.g. ``deleteRule(-1)`` removes
    364              the last rule in cssRules.
    365          :Exceptions:
    366              - `INDEX_SIZE_ERR`: (self)
    367                Raised if the specified index does not correspond to a rule in
    368                the style sheet's rule list.
    369              - `NAMESPACE_ERR`: (self)
    370                Raised if removing this rule would result in an invalid StyleSheet
    371              - `NO_MODIFICATION_ALLOWED_ERR`: (self)
    372                Raised if this style sheet is readonly.
    373          """
    374          self._checkReadonly()
    375
    376          try:
    377              rule = self.cssRules[index]
    378          except IndexError:
    379              raise xml.dom.IndexSizeErr(
    380                  u'CSSStyleSheet: %s is not a valid index in the rulelist of length %i' % (
    381                  index, self.cssRules.length))
    382          else:
    383              if rule.type == rule.NAMESPACE_RULE:
    384                  # check all namespacerules if used
    385                  uris = [r.namespaceURI for r in self if r.type == r.NAMESPACE_RULE]
    386                  useduris = self._getUsedURIs()
    387                  if rule.namespaceURI in useduris and\
    388                     uris.count(rule.namespaceURI) == 1:
    389                      raise xml.dom.NoModificationAllowedErr(
    390                          u'CSSStyleSheet: NamespaceURI defined in this rule is used, cannot remove.')
    391                      return
    392
    393              rule._parentStyleSheet = None # detach
    394              del self.cssRules[index] # delete from StyleSheet
    395
    396 -    def insertRule(self, rule, index=None, inOrder=False, _clean=True):
    397          """
    398          Used to insert a new rule into the style sheet. The new rule now
    399          becomes part of the cascade.
    400
    401          :Parameters:
    402              rule
    403                  a parsable DOMString, in cssutils also a CSSRule or a
    404                  CSSRuleList
    405              index
    406                  of the rule before the new rule will be inserted.
    407                  If the specified index is equal to the length of the
    408                  StyleSheet's rule collection, the rule will be added to the end
    409                  of the style sheet.
    410                  If index is not given or None rule will be appended to rule
    411                  list.
    412              inOrder
    413                  if True the rule will be put to a proper location while
    414                  ignoring index but without raising HIERARCHY_REQUEST_ERR.
    415                  The resulting index is returned nevertheless
    416          :returns: the index within the stylesheet's rule collection
    417          :Exceptions:
    418              - `HIERARCHY_REQUEST_ERR`: (self)
    419                Raised if the rule cannot be inserted at the specified index
    420                e.g. if an @import rule is inserted after a standard rule set
    421                or other at-rule.
    422              - `INDEX_SIZE_ERR`: (self)
    423                Raised if the specified index is not a valid insertion point.
    424              - `NO_MODIFICATION_ALLOWED_ERR`: (self)
    425                Raised if this style sheet is readonly.
    426              - `SYNTAX_ERR`: (rule)
    427                Raised if the specified rule has a syntax error and is
    428                unparsable.
    429          """
    430          self._checkReadonly()
    431
    432          # check position
    433          if index is None:
    434              index = len(self.cssRules)
    435          elif index < 0 or index > self.cssRules.length:
    436              raise xml.dom.IndexSizeErr(
    437                  u'CSSStyleSheet: Invalid index %s for CSSRuleList with a length of %s.' % (
    438                      index, self.cssRules.length))
    439              return
    440
    441          if isinstance(rule, basestring):
    442              # init a temp sheet which has the same properties as self
    443              tempsheet = CSSStyleSheet(href=self.href,
    444                                        media=self.media,
    445                                        title=self.title,
    446                                        parentStyleSheet=self.parentStyleSheet,
    447                                        ownerRule=self.ownerRule)
    448              tempsheet._ownerNode = self.ownerNode
    449              tempsheet._fetcher = self._fetcher
    450
    451              # prepend encoding if in this sheet to be able to use it in
    452              # @import rules encoding resolution
    453              # do not add if new rule startswith "@charset" (which is exact!)
    454              if not rule.startswith(u'@charset') and (self.cssRules and
    455                  self.cssRules[0].type == self.cssRules[0].CHARSET_RULE):
    456                  # rule 0 is @charset!
    457                  newrulescount, newruleindex = 2, 1
    458                  rule = self.cssRules[0].cssText + rule
    459              else:
    460                  newrulescount, newruleindex = 1, 0
    461
    462              # parse the new rule(s)
    463              tempsheet.cssText = (rule, self._namespaces)
    464
    465              if len(tempsheet.cssRules) != newrulescount or (not isinstance(
    466                 tempsheet.cssRules[newruleindex], cssutils.css.CSSRule)):
    467                  self._log.error(u'CSSStyleSheet: Not a CSSRule: %s' % rule)
    468                  return
    469              rule = tempsheet.cssRules[newruleindex]
    470              rule._parentStyleSheet = None # done later?
    471
    472              # TODO:
    473              #tempsheet._namespaces = self._namespaces
    474
    475          elif isinstance(rule, cssutils.css.CSSRuleList):
    476              # insert all rules
    477              for i, r in enumerate(rule):
    478                  self.insertRule(r, index + i)
    479              return index
    480
    481          if not rule.wellformed:
    482              self._log.error(u'CSSStyleSheet: Invalid rules cannot be added.')
    483              return
    484
    485          # CHECK HIERARCHY
    486          # @charset
    487          if rule.type == rule.CHARSET_RULE:
    488              if inOrder:
    489                  index = 0
    490                  # always first and only
    491                  if (self.cssRules and self.cssRules[0].type == rule.CHARSET_RULE):
    492                      self.cssRules[0].encoding = rule.encoding
    493                  else:
    494                      self.cssRules.insert(0, rule)
    495              elif index != 0 or (self.cssRules and
    496                                self.cssRules[0].type == rule.CHARSET_RULE):
    497                  self._log.error(
    498                      u'CSSStylesheet: @charset only allowed once at the beginning of a stylesheet.',
    499                      error=xml.dom.HierarchyRequestErr)
    500                  return
    501              else:
    502                  self.cssRules.insert(index, rule)
    503
    504          # @unknown or comment
    505          elif rule.type in (rule.UNKNOWN_RULE, rule.COMMENT) and not inOrder:
    506              if index == 0 and self.cssRules and\
    507                 self.cssRules[0].type == rule.CHARSET_RULE:
    508                  self._log.error(
    509                      u'CSSStylesheet: @charset must be the first rule.',
    510                      error=xml.dom.HierarchyRequestErr)
    511                  return
    512              else:
    513                  self.cssRules.insert(index, rule)
    514
    515          # @import
    516          elif rule.type == rule.IMPORT_RULE:
    517              if inOrder:
    518                  # automatic order
    519                  if rule.type in (r.type for r in self):
    520                      # find last of this type
    521                      for i, r in enumerate(reversed(self.cssRules)):
    522                          if r.type == rule.type:
    523                              index = len(self.cssRules) - i
    524                              break
    525                  else:
    526                      # find first point to insert
    527                      if self.cssRules and self.cssRules[0].type in (rule.CHARSET_RULE,
    528                                                                     rule.COMMENT):
    529                          index = 1
    530                      else:
    531                          index = 0
    532              else:
    533                  # after @charset
    534                  if index == 0 and self.cssRules and\
    535                     self.cssRules[0].type == rule.CHARSET_RULE:
    536                      self._log.error(
    537                          u'CSSStylesheet: Found @charset at index 0.',
    538                          error=xml.dom.HierarchyRequestErr)
    539                      return
    540                  # before @namespace, @page, @font-face, @media and stylerule
    541                  for r in self.cssRules[:index]:
    542                      if r.type in (r.NAMESPACE_RULE, r.MEDIA_RULE, r.PAGE_RULE,
    543                                    r.STYLE_RULE, r.FONT_FACE_RULE):
    544                          self._log.error(
    545                              u'CSSStylesheet: Cannot insert @import here, found @namespace, @media, @page or CSSStyleRule before index %s.' %
    546                              index,
    547                              error=xml.dom.HierarchyRequestErr)
    548                          return
    549              self.cssRules.insert(index, rule)
    550
    551          # @namespace
    552          elif rule.type == rule.NAMESPACE_RULE:
    553              if inOrder:
    554                  if rule.type in (r.type for r in self):
    555                      # find last of this type
    556                      for i, r in enumerate(reversed(self.cssRules)):
    557                          if r.type == rule.type:
    558                              index = len(self.cssRules) - i
    559                              break
    560                  else:
    561                      # find first point to insert
    562                      for i, r in enumerate(self.cssRules):
    563                          if r.type in (r.MEDIA_RULE, r.PAGE_RULE, r.STYLE_RULE,
    564                                        r.FONT_FACE_RULE, r.UNKNOWN_RULE, r.COMMENT):
    565                              index = i # before these
    566                              break
    567              else:
    568                  # after @charset and @import
    569                  for r in self.cssRules[index:]:
    570                      if r.type in (r.CHARSET_RULE, r.IMPORT_RULE):
    571                          self._log.error(
    572                              u'CSSStylesheet: Cannot insert @namespace here, found @charset or @import after index %s.' %
    573                              index,
    574                              error=xml.dom.HierarchyRequestErr)
    575                          return
    576                  # before @media and stylerule
    577                  for r in self.cssRules[:index]:
    578                      if r.type in (r.MEDIA_RULE, r.PAGE_RULE, r.STYLE_RULE,
    579                                    r.FONT_FACE_RULE):
    580                          self._log.error(
    581                              u'CSSStylesheet: Cannot insert @namespace here, found @media, @page or CSSStyleRule before index %s.' %
    582                              index,
    583                              error=xml.dom.HierarchyRequestErr)
    584                          return
    585
    586              if not (rule.prefix in self.namespaces and
    587                 self.namespaces[rule.prefix] == rule.namespaceURI):
    588                  # no doublettes
    589                  self.cssRules.insert(index, rule)
    590                  if _clean:
    591                      self._cleanNamespaces()
    592
    593          # all other where order is not important
    594          else:
    595              if inOrder:
    596                  # simply add to end as no specific order
    597                  self.cssRules.append(rule)
    598                  index = len(self.cssRules) - 1
    599              else:
    600                  for r in self.cssRules[index:]:
    601                      if r.type in (r.CHARSET_RULE, r.IMPORT_RULE, r.NAMESPACE_RULE):
    602                          self._log.error(
    603                              u'CSSStylesheet: Cannot insert rule here, found @charset, @import or @namespace before index %s.' %
    604                              index,
    605                              error=xml.dom.HierarchyRequestErr)
    606                          return
    607                  self.cssRules.insert(index, rule)
    608
    609          # post settings, TODO: for other rules which contain @rules
    610          rule._parentStyleSheet = self
    611          if rule.MEDIA_RULE == rule.type:
    612              for r in rule:
    613                  r._parentStyleSheet = self
    614          # ?
    615          elif rule.IMPORT_RULE == rule.type:
    616              rule.href = rule.href # try to reload stylesheet
    617
    618          return index
    619
    620      ownerRule = property(lambda self: self._ownerRule,
    621                           doc="(DOM attribute) NOT IMPLEMENTED YET")
    622
    623      @Deprecated('Use cssutils.replaceUrls(sheet, replacer) instead.')
    624 -    def replaceUrls(self, replacer):
    625          """
    626          **EXPERIMENTAL**
    627
    628          Utility method to replace all ``url(urlstring)`` values in
    629          ``CSSImportRules`` and ``CSSStyleDeclaration`` objects (properties).
    630
    631          ``replacer`` must be a function which is called with a single
    632          argument ``urlstring`` which is the current value of url()
    633          excluding ``url(`` and ``)``. It still may have surrounding
    634          single or double quotes though.
    635          """
    636          cssutils.replaceUrls(self, replacer)
    637
    638 -    def setSerializer(self, cssserializer):
    639          """
    640          Sets the global Serializer used for output of all stylesheet
    641          output.
    642          """
    643          if isinstance(cssserializer, cssutils.CSSSerializer):
    644              cssutils.ser = cssserializer
    645          else:
    646              raise ValueError(u'Serializer must be an instance of cssutils.CSSSerializer.')
    647
    648 -    def setSerializerPref(self, pref, value):
    649          """
    650          Sets Preference of CSSSerializer used for output of this
    651          stylesheet. See cssutils.serialize.Preferences for possible
    652          preferences to be set.
    653          """
    654          cssutils.ser.prefs.__setattr__(pref, value)
    655
    656 -    def __repr__(self):
    657          if self.media:
    658              mediaText = self.media.mediaText
    659          else:
    660              mediaText = None
    661          return "cssutils.css.%s(href=%r, media=%r, title=%r)" % (
    662                  self.__class__.__name__,
    663                  self.href, mediaText, self.title)
    664
    665 -    def __str__(self):
    666          if self.media:
    667              mediaText = self.media.mediaText
    668          else:
    669              mediaText = None
    670          return "<cssutils.css.%s object encoding=%r href=%r "\
    671                 "media=%r title=%r namespaces=%r at 0x%x>" % (
    672                  self.__class__.__name__, self.encoding, self.href,
    673                  mediaText, self.title, self.namespaces.namespaces,
    674                  id(self))
    675