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 1370 2008-07-14 20:15:03Z 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          # if self._media is used (simply empty)
     81          self._usemedia = False
     82          self._media = cssutils.stylesheets.MediaList()
     83          if mediaText:
     84              self._usemedia = True
     85              self._media.mediaText = mediaText
     86
     87          self._name = name
     88
     89          seq = self._tempSeq()
     90          seq.append(self.href, 'href')
     91          seq.append(self.media, 'media')
     92          seq.append(self.name, 'name')
     93          self._setSeq(seq)
     94
     95          self._readonly = readonly
     96
     97 -    def _getCssText(self):
     98          """
     99          returns serialized property cssText
    100          """
    101          return cssutils.ser.do_CSSImportRule(self)
    102
    103 -    def _setCssText(self, cssText):
    104          """
    105          DOMException on setting
    106
    107          - HIERARCHY_REQUEST_ERR: (CSSStylesheet)
    108            Raised if the rule cannot be inserted at this point in the
    109            style sheet.
    110          - INVALID_MODIFICATION_ERR: (self)
    111            Raised if the specified CSS string value represents a different
    112            type of rule than the current one.
    113          - NO_MODIFICATION_ALLOWED_ERR: (CSSRule)
    114            Raised if the rule is readonly.
    115          - SYNTAX_ERR: (self)
    116            Raised if the specified CSS string value has a syntax error and
    117            is unparsable.
    118          """
    119          super(CSSImportRule, self)._setCssText(cssText)
    120          tokenizer = self._tokenize2(cssText)
    121          attoken = self._nexttoken(tokenizer, None)
    122          if self._type(attoken) != self._prods.IMPORT_SYM:
    123              self._log.error(u'CSSImportRule: No CSSImportRule found: %s' %
    124                  self._valuestr(cssText),
    125                  error=xml.dom.InvalidModificationErr)
    126          else:
    127              # for closures: must be a mutable
    128              new = {'keyword': self._tokenvalue(attoken),
    129                     'href': None,
    130                     'hreftype': None,
    131                     'media': None,
    132                     'name': None,
    133                     'wellformed': True
    134                     }
    135
    136              def __doname(seq, token):
    137                  # called by _string or _ident
    138                  new['name'] = self._stringtokenvalue(token)
    139                  seq.append(new['name'], 'name')
    140                  return ';'
    141
    142              def _string(expected, seq, token, tokenizer=None):
    143                  if 'href' == expected:
    144                      # href
    145                      new['href'] = self._stringtokenvalue(token)
    146                      new['hreftype'] = 'string'
    147                      seq.append(new['href'], 'href')
    148                      return 'media name ;'
    149                  elif 'name' in expected:
    150                      # name
    151                      return __doname(seq, token)
    152                  else:
    153                      new['wellformed'] = False
    154                      self._log.error(
    155                          u'CSSImportRule: Unexpected string.', token)
    156                      return expected
    157
    158              def _uri(expected, seq, token, tokenizer=None):
    159                  # href
    160                  if 'href' == expected:
    161                      uri = self._uritokenvalue(token)
    162                      new['hreftype'] = 'uri'
    163                      new['href'] = uri
    164                      seq.append(new['href'], 'href')
    165                      return 'media name ;'
    166                  else:
    167                      new['wellformed'] = False
    168                      self._log.error(
    169                          u'CSSImportRule: Unexpected URI.', token)
    170                      return expected
    171
    172              def _ident(expected, seq, token, tokenizer=None):
    173                  # medialist ending with ; which is checked upon too
    174                  if expected.startswith('media'):
    175                      mediatokens = self._tokensupto2(
    176                          tokenizer, importmediaqueryendonly=True)
    177                      mediatokens.insert(0, token) # push found token
    178
    179                      last = mediatokens.pop() # retrieve ;
    180                      lastval, lasttyp = self._tokenvalue(last), self._type(last)
    181                      if lastval != u';' and lasttyp not in ('EOF', self._prods.STRING):
    182                          new['wellformed'] = False
    183                          self._log.error(u'CSSImportRule: No ";" found: %s' %
    184                                          self._valuestr(cssText), token=token)
    185
    186                      media = cssutils.stylesheets.MediaList()
    187                      media.mediaText = mediatokens
    188                      if media.wellformed:
    189                          new['media'] = media
    190                          seq.append(media, 'media')
    191                      else:
    192                          new['wellformed'] = False
    193                          self._log.error(u'CSSImportRule: Invalid MediaList: %s' %
    194                                          self._valuestr(cssText), token=token)
    195
    196                      if lasttyp == self._prods.STRING:
    197                          # name
    198                          return __doname(seq, last)
    199                      else:
    200                          return 'EOF' # ';' is token "last"
    201                  else:
    202                      new['wellformed'] = False
    203                      self._log.error(
    204                          u'CSSImportRule: Unexpected ident.', token)
    205                      return expected
    206
    207              def _char(expected, seq, token, tokenizer=None):
    208                  # final ;
    209                  val = self._tokenvalue(token)
    210                  if expected.endswith(';') and u';' == val:
    211                      return 'EOF'
    212                  else:
    213                      new['wellformed'] = False
    214                      self._log.error(
    215                          u'CSSImportRule: Unexpected char.', token)
    216                      return expected
    217
    218              # import : IMPORT_SYM S* [STRING|URI]
    219              #            S* [ medium [ ',' S* medium]* ]? ';' S*
    220              #         STRING? # see http://www.w3.org/TR/css3-cascade/#cascading
    221              #        ;
    222              newseq = self._tempSeq()
    223              wellformed, expected = self._parse(expected='href',
    224                  seq=newseq, tokenizer=tokenizer,
    225                  productions={'STRING': _string,
    226                               'URI': _uri,
    227                               'IDENT': _ident,
    228                               'CHAR': _char},
    229                  new=new)
    230
    231              # wellformed set by parse
    232              wellformed = wellformed and new['wellformed']
    233
    234              # post conditions
    235              if not new['href']:
    236                  wellformed = False
    237                  self._log.error(u'CSSImportRule: No href found: %s' %
    238                      self._valuestr(cssText))
    239
    240              if expected != 'EOF':
    241                  wellformed = False
    242                  self._log.error(u'CSSImportRule: No ";" found: %s' %
    243                      self._valuestr(cssText))
    244
    245              # set all
    246              if wellformed:
    247                  self.atkeyword = new['keyword']
    248                  self.hreftype = new['hreftype']
    249                  if new['media']:
    250                      # use same object
    251                      self._usemedia = True
    252                      self._media.mediaText = new['media'].mediaText
    253                  else:
    254                      self._usemedia = False
    255                  self.name = new['name']
    256                  self._setSeq(newseq)
    257                  self.href = new['href']
    258
    259                  if self.styleSheet:
    260                      # title is set by href
    261                      #self.styleSheet._href = self.href
    262                      self.styleSheet._parentStyleSheet = self.parentStyleSheet
    263
    264      cssText = property(fget=_getCssText, fset=_setCssText,
    265          doc="(DOM attribute) The parsable textual representation.")
    266
    267 -    def _setHref(self, href):
    268          # update seq
    269          for i, item in enumerate(self.seq):
    270              val, typ = item.value, item.type
    271              if 'href' == typ:
    272                  self._seq[i] = (href, typ, item.line, item.col)
    273                  break
    274          else:
    275              seq = self._tempSeq()
    276              seq.append(self.href, 'href')
    277              self._setSeq(seq)
    278          # set new href
    279          self._href = href
    280          if not self.styleSheet:
    281              # set only if not set before
    282              self.__setStyleSheet()
    283
    284      href = property(lambda self: self._href, _setHref,
    285                      doc="Location of the style sheet to be imported.")
    286
    287      media = property(lambda self: self._media,
    288                       doc=u"(DOM readonly) A list of media types for this rule"
    289                       " of type MediaList")
    290
    291 -    def _setName(self, name):
    292          """raises xml.dom.SyntaxErr if name is not a string"""
    293          if isinstance(name, basestring) or name is None:
    294              # "" or ''
    295              if not name:
    296                  name = None
    297              # update seq
    298              for i, item in enumerate(self.seq):
    299                  val, typ = item.value, item.type
    300                  if 'name' == typ:
    301                      self._seq[i] = (name, typ, item.line, item.col)
    302                      break
    303              else:
    304                  # append
    305                  seq = self._tempSeq()
    306                  for item in self.seq:
    307                      # copy current seq
    308                      seq.append(item.value, item.type, item.line, item.col)
    309                  seq.append(name, 'name')
    310                  self._setSeq(seq)
    311              self._name = name
    312              # set title of referred sheet
    313              if self.styleSheet:
    314                  self.styleSheet.title = name
    315          else:
    316              self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
    317
    318      name = property(lambda self: self._name, _setName,
    319                      doc=u"An optional name for the imported sheet")
    320
    321 -    def __setStyleSheet(self):
    322          """Read new CSSStyleSheet cssText from href using parentStyleSheet.href
    323
    324          indirectly called if setting ``href``
    325          """
    326          # should simply fail so all errors are catched!
    327          if self.parentStyleSheet and self.href:
    328              # relative href
    329              parentHref = self.parentStyleSheet.href
    330              if parentHref is None:
    331                  # use cwd instead
    332                  parentHref = u'file:' + urllib.pathname2url(os.getcwd()) + '/'
    333              href = urlparse.urljoin(parentHref, self.href)
    334
    335              # all possible exceptions are ignored and styleSheet is None
    336              try:
    337                  overrideEncoding, cssText = self.parentStyleSheet._resolveImport(href)
    338                  if cssText is None:
    339                      raise IOError('Cannot read Stylesheet or it is empty.')
    340
    341                  refsheet = cssutils.css.CSSStyleSheet(href=href,
    342                                                        media=self.media,
    343                                                        ownerRule=self,
    344                                                        title=self.name)
    345                  # inherit fetcher for @imports in refsheet
    346                  refsheet._setFetcher(self.parentStyleSheet._fetcher)
    347                  # overrideEncoding with parentStyleSheet.overrideEncoding,
    348                  # HTTP or parent
    349                  refsheet._setCssTextWithEncodingOverride(cssText,
    350                                                           overrideEncoding)
    351
    352              except (OSError, IOError, ValueError), e:
    353                  self._log.warn(u'CSSImportRule: While processing imported style sheet href=%r: %r'
    354                                 % (self.href, e), neverraise=True)
    355              else:
    356                  self._styleSheet = refsheet
    357
    358      styleSheet = property(lambda self: self._styleSheet,
    359                            doc="(readonly) The style sheet referred to by this rule.")
    360
    361 -    def _getWellformed(self):
    362          "depending if media is used at all"
    363          if self._usemedia:
    364              return bool(self.href and self.media.wellformed)
    365          else:
    366              return bool(self.href)
    367
    368      wellformed = property(_getWellformed)
    369
    370 -    def __repr__(self):
    371          if self._usemedia:
    372              mediaText = self.media.mediaText
    373          else:
    374              mediaText = None
    375          return "cssutils.css.%s(href=%r, mediaText=%r, name=%r)" % (
    376                  self.__class__.__name__,
    377                  self.href, self.media.mediaText, self.name)
    378
    379 -    def __str__(self):
    380          if self._usemedia:
    381              mediaText = self.media.mediaText
    382          else:
    383              mediaText = None
    384          return "<cssutils.css.%s object href=%r mediaText=%r name=%r at 0x%x>" % (
    385                  self.__class__.__name__, self.href, mediaText, self.name, id(self))
    386