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

# Source Code for Module cssutils.css.cssimportrule

      1  """CSSImportRule implements DOM Level 2 CSS CSSImportRule.
      2
      3  plus:
      4
      5  ``name`` property
      6      http://www.w3.org/TR/css3-cascade/#cascading
      7
      8  """
      9  __all__ = ['CSSImportRule']
     10  __docformat__ = 'restructuredtext'
     11  __version__ = '$Id: cssimportrule.py 1401 2008-07-29 21:07:54Z cthedot $'
     12
     13  import os
     14  import urllib
     15  import urlparse
     16  import xml.dom
     17  import cssrule
     18  import cssutils
     19
     20 -class CSSImportRule(cssrule.CSSRule):
     21      """
     22      Represents an @import rule within a CSS style sheet.  The @import rule
     23      is used to import style rules from other style sheets.
     24
     25      Properties
     26      ==========
     27      atkeyword: (cssutils only)
     28          the literal keyword used
     29      cssText: of type DOMString
     30          The parsable textual representation of this rule
     31      href: of type DOMString, (DOM readonly, cssutils also writable)
     32          The location of the style sheet to be imported. The attribute will
     33          not contain the url(...) specifier around the URI.
     34      hreftype: 'uri' (serializer default) or 'string' (cssutils only)
     35          The original type of href, not really relevant as it may be
     36          reconfigured in the serializer but it is kept anyway
     37      media: of type stylesheets::MediaList (DOM readonly)
     38          A list of media types for this rule of type MediaList.
     39      name:
     40          An optional name used for cascading
     41      styleSheet: of type CSSStyleSheet (DOM readonly)
     42          The style sheet referred to by this rule. The value of this
     43          attribute is None if the style sheet has not yet been loaded or if
     44          it will not be loaded (e.g. if the stylesheet is for a media type
     45          not supported by the user agent).
     46
     47      Inherits properties from CSSRule
     48
     49      Format
     50      ======
     51      import
     52        : IMPORT_SYM S*
     53        [STRING|URI] S* [ medium [ COMMA S* medium]* ]? S* STRING? S* ';' S*
     54        ;
     55      """
     56      type = property(lambda self: cssrule.CSSRule.IMPORT_RULE)
     57
     58 -    def __init__(self, href=None, mediaText=u'all', name=None,
     59                   parentRule=None, parentStyleSheet=None, readonly=False):
     60          """
     61          if readonly allows setting of properties in constructor only
     62
     63          Do not use as positional but as keyword attributes only!
     64
     65          href
     66              location of the style sheet to be imported.
     67          mediaText
     68              A list of media types for which this style sheet may be used
     69              as a string
     70          """
     71          super(CSSImportRule, self).__init__(parentRule=parentRule,
     72                                              parentStyleSheet=parentStyleSheet)
     73          self._atkeyword = u'@import'
     74          self.hreftype = None
     75          self._styleSheet = None
     76
     77          self._href = None
     78          self.href = href
     79
     80          self._media = cssutils.stylesheets.MediaList()
     81          if mediaText:
     82              self._media.mediaText = mediaText
     83
     84          self._name = name
     85
     86          seq = self._tempSeq()
     87          seq.append(self.href, 'href')
     88          seq.append(self.media, 'media')
     89          seq.append(self.name, 'name')
     90          self._setSeq(seq)
     91          self._readonly = readonly
     92
     93      _usemedia = property(lambda self: self.media.mediaText not in (u'', u'all'),
     94                           doc="if self._media is used (or simply empty)")
     95
     96 -    def _getCssText(self):
     97          """
     98          returns serialized property cssText
     99          """
    100          return cssutils.ser.do_CSSImportRule(self)
    101
    102 -    def _setCssText(self, cssText):
    103          """
    104          DOMException on setting
    105
    106          - HIERARCHY_REQUEST_ERR: (CSSStylesheet)
    107            Raised if the rule cannot be inserted at this point in the
    108            style sheet.
    109          - INVALID_MODIFICATION_ERR: (self)
    110            Raised if the specified CSS string value represents a different
    111            type of rule than the current one.
    112          - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
    113            Raised if the rule is readonly.
    114          - SYNTAX_ERR: (self)
    115            Raised if the specified CSS string value has a syntax error and
    116            is unparsable.
    117          """
    118          super(CSSImportRule, self)._setCssText(cssText)
    119          tokenizer = self._tokenize2(cssText)
    120          attoken = self._nexttoken(tokenizer, None)
    121          if self._type(attoken) != self._prods.IMPORT_SYM:
    122              self._log.error(u'CSSImportRule: No CSSImportRule found: %s' %
    123                  self._valuestr(cssText),
    124                  error=xml.dom.InvalidModificationErr)
    125          else:
    126              # for closures: must be a mutable
    127              new = {'keyword': self._tokenvalue(attoken),
    128                     'href': None,
    129                     'hreftype': None,
    130                     'media': None,
    131                     'name': None,
    132                     'wellformed': True
    133                     }
    134
    135              def __doname(seq, token):
    136                  # called by _string or _ident
    137                  new['name'] = self._stringtokenvalue(token)
    138                  seq.append(new['name'], 'name')
    139                  return ';'
    140
    141              def _string(expected, seq, token, tokenizer=None):
    142                  if 'href' == expected:
    143                      # href
    144                      new['href'] = self._stringtokenvalue(token)
    145                      new['hreftype'] = 'string'
    146                      seq.append(new['href'], 'href')
    147                      return 'media name ;'
    148                  elif 'name' in expected:
    149                      # name
    150                      return __doname(seq, token)
    151                  else:
    152                      new['wellformed'] = False
    153                      self._log.error(
    154                          u'CSSImportRule: Unexpected string.', token)
    155                      return expected
    156
    157              def _uri(expected, seq, token, tokenizer=None):
    158                  # href
    159                  if 'href' == expected:
    160                      uri = self._uritokenvalue(token)
    161                      new['hreftype'] = 'uri'
    162                      new['href'] = uri
    163                      seq.append(new['href'], 'href')
    164                      return 'media name ;'
    165                  else:
    166                      new['wellformed'] = False
    167                      self._log.error(
    168                          u'CSSImportRule: Unexpected URI.', token)
    169                      return expected
    170
    171              def _ident(expected, seq, token, tokenizer=None):
    172                  # medialist ending with ; which is checked upon too
    173                  if expected.startswith('media'):
    174                      mediatokens = self._tokensupto2(
    175                          tokenizer, importmediaqueryendonly=True)
    176                      mediatokens.insert(0, token) # push found token
    177
    178                      last = mediatokens.pop() # retrieve ;
    179                      lastval, lasttyp = self._tokenvalue(last), self._type(last)
    180                      if lastval != u';' and lasttyp not in ('EOF', self._prods.STRING):
    181                          new['wellformed'] = False
    182                          self._log.error(u'CSSImportRule: No ";" found: %s' %
    183                                          self._valuestr(cssText), token=token)
    184
    185                      media = cssutils.stylesheets.MediaList()
    186                      media.mediaText = mediatokens
    187                      if media.wellformed:
    188                          new['media'] = media
    189                          seq.append(media, 'media')
    190                      else:
    191                          new['wellformed'] = False
    192                          self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
    193                                          self._valuestr(cssText), token=token)
    194
    195                      if lasttyp == self._prods.STRING:
    196                          # name
    197                          return __doname(seq, last)
    198                      else:
    199                          return 'EOF' # ';' is token "last"
    200                  else:
    201                      new['wellformed'] = False
    202                      self._log.error(
    203                          u'CSSImportRule: Unexpected ident.', token)
    204                      return expected
    205
    206              def _char(expected, seq, token, tokenizer=None):
    207                  # final ;
    208                  val = self._tokenvalue(token)
    209                  if expected.endswith(';') and u';' == val:
    210                      return 'EOF'
    211                  else:
    212                      new['wellformed'] = False
    213                      self._log.error(
    214                          u'CSSImportRule: Unexpected char.', token)
    215                      return expected
    216
    217              # import : IMPORT_SYM S* [STRING|URI]
    218              #            S* [ medium [ ',' S* medium]* ]? ';' S*
    219              #         STRING? # see http://www.w3.org/TR/css3-cascade/#cascading
    220              #        ;
    221              newseq = self._tempSeq()
    222              wellformed, expected = self._parse(expected='href',
    223                  seq=newseq, tokenizer=tokenizer,
    224                  productions={'STRING': _string,
    225                               'URI': _uri,
    226                               'IDENT': _ident,
    227                               'CHAR': _char},
    228                  new=new)
    229
    230              # wellformed set by parse
    231              wellformed = wellformed and new['wellformed']
    232
    233              # post conditions
    234              if not new['href']:
    235                  wellformed = False
    236                  self._log.error(u'CSSImportRule: No href found: %s' %
    237                      self._valuestr(cssText))
    238
    239              if expected != 'EOF':
    240                  wellformed = False
    241                  self._log.error(u'CSSImportRule: No ";" found: %s' %
    242                      self._valuestr(cssText))
    243
    244              # set all
    245              if wellformed:
    246                  self.atkeyword = new['keyword']
    247                  self.hreftype = new['hreftype']
    248                  if new['media']:
    249                      # use same object
    250                      self.media.mediaText = new['media'].mediaText
    251                      # put it in newseq too
    252                      for index, x in enumerate(newseq):
    253                          if x.type == 'media':
    254                              newseq.replace(index, self.media,
    255                                             x.type, x.line, x.col)
    256                              break
    257                  else:
    258                          # reset media
    259                      self.media.mediaText = u'all'
    260                      newseq.append(self.media, 'media')
    261                  self.name = new['name']
    262                  self._setSeq(newseq)
    263                  self.href = new['href']
    264
    265                  if self.styleSheet:
    266                      # title is set by href
    267                      #self.styleSheet._href = self.href
    268                      self.styleSheet._parentStyleSheet = self.parentStyleSheet
    269
    270      cssText = property(fget=_getCssText, fset=_setCssText,
    271          doc="(DOM attribute) The parsable textual representation.")
    272
    273 -    def _setHref(self, href):
    274          # update seq
    275          for i, item in enumerate(self.seq):
    276              val, typ = item.value, item.type
    277              if 'href' == typ:
    278                  self._seq[i] = (href, typ, item.line, item.col)
    279                  break
    280          else:
    281              seq = self._tempSeq()
    282              seq.append(self.href, 'href')
    283              self._setSeq(seq)
    284          # set new href
    285          self._href = href
    286          if not self.styleSheet:
    287              # set only if not set before
    288              self.__setStyleSheet()
    289
    290      href = property(lambda self: self._href, _setHref,
    291                      doc="Location of the style sheet to be imported.")
    292
    293      media = property(lambda self: self._media,
    294                       doc=u"(DOM readonly) A list of media types for this rule"
    295                       " of type MediaList")
    296
    297 -    def _setName(self, name):
    298          """raises xml.dom.SyntaxErr if name is not a string"""
    299          if isinstance(name, basestring) or name is None:
    300              # "" or ''
    301              if not name:
    302                  name = None
    303              # update seq
    304              for i, item in enumerate(self.seq):
    305                  val, typ = item.value, item.type
    306                  if 'name' == typ:
    307                      self._seq[i] = (name, typ, item.line, item.col)
    308                      break
    309              else:
    310                  # append
    311                  seq = self._tempSeq()
    312                  for item in self.seq:
    313                      # copy current seq
    314                      seq.append(item.value, item.type, item.line, item.col)
    315                  seq.append(name, 'name')
    316                  self._setSeq(seq)
    317              self._name = name
    318              # set title of referred sheet
    319              if self.styleSheet:
    320                  self.styleSheet.title = name
    321          else:
    322              self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
    323
    324      name = property(lambda self: self._name, _setName,
    325                      doc=u"An optional name for the imported sheet")
    326
    327 -    def __setStyleSheet(self):
    328          """Read new CSSStyleSheet cssText from href using parentStyleSheet.href
    329
    330          Indirectly called if setting ``href``. In case of any error styleSheet
    331          is set to ``None``.
    332          """
    333          # should simply fail so all errors are catched!
    334          if self.parentStyleSheet and self.href:
    335              # relative href
    336              parentHref = self.parentStyleSheet.href
    337              if parentHref is None:
    338                  # use cwd instead
    339                  parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/'
    340              href = urlparse.urljoin(parentHref, self.href)
    341
    342              # all possible exceptions are ignored (styleSheet is None then)
    343              try:
    344                  usedEncoding, enctype, cssText = self.parentStyleSheet._resolveImport(href)
    345                  if cssText is None:
    346                      # catched in next except below!
    347                      raise IOError('Cannot read Stylesheet.')
    348                  styleSheet = cssutils.css.CSSStyleSheet(href=href,
    349                                                        media=self.media,
    350                                                        ownerRule=self,
    351                                                        title=self.name)
    352                  # inherit fetcher for @imports in styleSheet
    353                  styleSheet._setFetcher(self.parentStyleSheet._fetcher)
    354                  # contentEncoding with parentStyleSheet.overrideEncoding,
    355                  # HTTP or parent
    356                  encodingOverride, encoding = None, None
    357                  if enctype == 0:
    358                      encodingOverride = usedEncoding
    359                  elif 5 > enctype > 0:
    360                      encoding = usedEncoding
    361
    362                  styleSheet._setCssTextWithEncodingOverride(cssText,
    363                                                           encodingOverride=encodingOverride,
    364                                                           encoding=encoding)
    365
    366              except (OSError, IOError, ValueError), e:
    367                  self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r'
    368                                 % (self.href, e), neverraise=True)
    369              else:
    370                  self._styleSheet = styleSheet
    371
    372      styleSheet = property(lambda self: self._styleSheet,
    373                            doc="(readonly) The style sheet referred to by this rule.")
    374
    375 -    def _getWellformed(self):
    376          "depending if media is used at all"
    377          if self._usemedia:
    378              return bool(self.href and self.media.wellformed)
    379          else:
    380              return bool(self.href)
    381
    382      wellformed = property(_getWellformed)
    383
    384 -    def __repr__(self):
    385          if self._usemedia:
    386              mediaText = self.media.mediaText
    387          else:
    388              mediaText = None
    389          return "cssutils.css.%s(href=%r, mediaText=%r, name=%r)" % (
    390                  self.__class__.__name__,
    391                  self.href, self.media.mediaText, self.name)
    392
    393 -    def __str__(self):
    394          if self._usemedia:
    395              mediaText = self.media.mediaText
    396          else:
    397              mediaText = None
    398          return "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % (
    399                  self.__class__.__name__, self.href, mediaText, self.name, id(self))
    400