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

# Source Code for Module cssutils.css.cssmediarule

      1  """CSSMediaRule implements DOM Level 2 CSS CSSMediaRule.
      2  """
      3  __all__ = ['CSSMediaRule']
      4  __docformat__ = 'restructuredtext'
      5  __version__ = '$Id: cssmediarule.py 1370 2008-07-14 20:15:03Z cthedot $'
      6
      7  import xml.dom
      8  import cssrule
      9  import cssutils
     10
     11 -class CSSMediaRule(cssrule.CSSRule):
     12      """
     13      Objects implementing the CSSMediaRule interface can be identified by the
     14      MEDIA_RULE constant. On these objects the type attribute must return the
     15      value of that constant.
     16
     17      Properties
     18      ==========
     19      atkeyword: (cssutils only)
     20          the literal keyword used
     21      cssRules: A css::CSSRuleList of all CSS rules contained within the
     22          media block.
     23      cssText: of type DOMString
     24          The parsable textual representation of this rule
     25      media: of type stylesheets::MediaList, (DOM readonly)
     26          A list of media types for this rule of type MediaList.
     27      name:
     28          An optional name used for cascading
     29
     30      Format
     31      ======
     32      media
     33        : MEDIA_SYM S* medium [ COMMA S* medium ]*
     34
     35            STRING? # the name
     36
     37        LBRACE S* ruleset* '}' S*;
     38      """
     39      # CONSTANT
     40      type = property(lambda self: cssrule.CSSRule.MEDIA_RULE)
     41
     42 -    def __init__(self, mediaText='all', name=None,
     43                   parentRule=None, parentStyleSheet=None, readonly=False):
     44          """constructor"""
     45          super(CSSMediaRule, self).__init__(parentRule=parentRule,
     46                                             parentStyleSheet=parentStyleSheet)
     47          self._atkeyword = u'@media'
     48          self._media = cssutils.stylesheets.MediaList(mediaText,
     49                                                       readonly=readonly)
     50          self.name = name
     51          self.cssRules = cssutils.css.cssrulelist.CSSRuleList()
     52          self.cssRules.append = self.insertRule
     53          self.cssRules.extend = self.insertRule
     54          self.cssRules.__delitem__ == self.deleteRule
     55
     56          self._readonly = readonly
     57
     58 -    def __iter__(self):
     59          """generator which iterates over cssRules."""
     60          for rule in self.cssRules:
     61              yield rule
     62
     63 -    def _getCssText(self):
     64          """return serialized property cssText"""
     65          return cssutils.ser.do_CSSMediaRule(self)
     66
     67 -    def _setCssText(self, cssText):
     68          """
     69          :param cssText:
     70              a parseable string or a tuple of (cssText, dict-of-namespaces)
     71          :Exceptions:
     72              - `NAMESPACE_ERR`: (Selector)
     73                Raised if a specified selector uses an unknown namespace
     74                prefix.
     75              - `SYNTAX_ERR`: (self, StyleDeclaration, etc)
     76                Raised if the specified CSS string value has a syntax error and
     77                is unparsable.
     78              - `INVALID_MODIFICATION_ERR`: (self)
     79                Raised if the specified CSS string value represents a different
     80                type of rule than the current one.
     81              - `HIERARCHY_REQUEST_ERR`: (CSSStylesheet)
     82                Raised if the rule cannot be inserted at this point in the
     83                style sheet.
     84              - `NO_MODIFICATION_ALLOWED_ERR`: (CSSRule)
     85                Raised if the rule is readonly.
     86          """
     87          super(CSSMediaRule, self)._setCssText(cssText)
     88
     89          # might be (cssText, namespaces)
     90          cssText, namespaces = self._splitNamespacesOff(cssText)
     91          try:
     92              # use parent style sheet ones if available
     93              namespaces = self.parentStyleSheet.namespaces
     94          except AttributeError:
     95              pass
     96
     97          tokenizer = self._tokenize2(cssText)
     98          attoken = self._nexttoken(tokenizer, None)
     99          if self._type(attoken) != self._prods.MEDIA_SYM:
    100              self._log.error(u'CSSMediaRule: No CSSMediaRule found: %s' %
    101                  self._valuestr(cssText),
    102                  error=xml.dom.InvalidModificationErr)
    103          else:
    104              # media "name"? { cssRules }
    105
    106              # media
    107              wellformed = True
    108              mediatokens, end = self._tokensupto2(tokenizer,
    109                                              mediaqueryendonly=True,
    110                                              separateEnd=True)
    111              if u'{' == self._tokenvalue(end) or self._prods.STRING == self._type(end):
    112                  newmedia = cssutils.stylesheets.MediaList()
    113                  newmedia.mediaText = mediatokens
    114
    115              # name (optional)
    116              name = None
    117              nameseq = self._tempSeq()
    118              if self._prods.STRING == self._type(end):
    119                  name = self._stringtokenvalue(end)
    120                  # TODO: for now comments are lost after name
    121                  nametokens, end = self._tokensupto2(tokenizer,
    122                                                  blockstartonly=True,
    123                                                  separateEnd=True)
    124                  wellformed, expected = self._parse(None, nameseq, nametokens, {})
    125                  if not wellformed:
    126                      self._log.error(u'CSSMediaRule: Syntax Error: %s' %
    127                                      self._valuestr(cssText))
    128
    129
    130              # check for {
    131              if u'{' != self._tokenvalue(end):
    132                  self._log.error(u'CSSMediaRule: No "{" found: %s' %
    133                                  self._valuestr(cssText))
    134                  return
    135
    136              # cssRules
    137              cssrulestokens, braceOrEOF = self._tokensupto2(tokenizer,
    138                                                 mediaendonly=True,
    139                                                 separateEnd=True)
    140              nonetoken = self._nexttoken(tokenizer, None)
    141              if (u'}' != self._tokenvalue(braceOrEOF) and
    142                 'EOF' != self._type(braceOrEOF)):
    143                  self._log.error(u'CSSMediaRule: No "}" found.',
    144                                  token=braceOrEOF)
    145              elif nonetoken:
    146                  self._log.error(u'CSSMediaRule: Trailing content found.',
    147                                  token=nonetoken)
    148              else:
    149                  # for closures: must be a mutable
    150                  newcssrules = [] #cssutils.css.CSSRuleList()
    151                  new = {'wellformed': True }
    152
    153                  def ruleset(expected, seq, token, tokenizer):
    154                      rule = cssutils.css.CSSStyleRule(parentRule=self)
    155                      rule.cssText = (self._tokensupto2(tokenizer, token),
    156                                      namespaces)
    157                      if rule.wellformed:
    158                          rule._parentStyleSheet=self.parentStyleSheet
    159                          seq.append(rule)
    160                      return expected
    161
    162                  def atrule(expected, seq, token, tokenizer):
    163                      # TODO: get complete rule!
    164                      tokens = self._tokensupto2(tokenizer, token)
    165                      atval = self._tokenvalue(token)
    166                      if atval in ('@charset ', '@font-face', '@import', '@namespace',
    167                                   '@page', '@media'):
    168                          self._log.error(
    169                              u'CSSMediaRule: This rule is not allowed in CSSMediaRule - ignored: %s.'
    170                                  % self._valuestr(tokens),
    171                                  token = token,
    172                                  error=xml.dom.HierarchyRequestErr)
    173                      else:
    174                          rule = cssutils.css.CSSUnknownRule(parentRule=self,
    175                                             parentStyleSheet=self.parentStyleSheet)
    176                          rule.cssText = tokens
    177                          if rule.wellformed:
    178                              seq.append(rule)
    179                      return expected
    180
    181                  def COMMENT(expected, seq, token, tokenizer=None):
    182                      seq.append(cssutils.css.CSSComment([token]))
    183                      return expected
    184
    185                  tokenizer = (t for t in cssrulestokens) # TODO: not elegant!
    186                  wellformed, expected = self._parse(braceOrEOF,
    187                                                     newcssrules,
    188                                                     tokenizer, {
    189                                                       'COMMENT': COMMENT,
    190                                                       'CHARSET_SYM': atrule,
    191                                                       'FONT_FACE_SYM': atrule,
    192                                                       'IMPORT_SYM': atrule,
    193                                                       'NAMESPACE_SYM': atrule,
    194                                                       'PAGE_SYM': atrule,
    195                                                       'MEDIA_SYM': atrule,
    196                                                       'ATKEYWORD': atrule
    197                                                     },
    198                                                     default=ruleset,
    199                                                     new=new)
    200
    201                  # no post condition
    202                  if newmedia.wellformed and wellformed:
    203                      # keep reference
    204                      self._media.mediaText = newmedia.mediaText
    205                      self.name = name
    206                      self._setSeq(nameseq)
    207                      del self.cssRules[:]
    208                      for r in newcssrules:
    209                          self.cssRules.append(r)
    210
    211      cssText = property(_getCssText, _setCssText,
    212          doc="(DOM attribute) The parsable textual representation.")
    213
    214 -    def _setName(self, name):
    215          if isinstance(name, basestring) or name is None:
    216              # "" or ''
    217              if not name:
    218                  name = None
    219
    220              self._name = name
    221          else:
    222              self._log.error(u'CSSImportRule: Not a valid name: %s' % name)
    223
    224
    225      name = property(lambda self: self._name, _setName,
    226                      doc=u"An optional name for the media rules")
    227
    228      media = property(lambda self: self._media,
    229          doc=u"(DOM readonly) A list of media types for this rule of type\
    230              MediaList")
    231
    232      wellformed = property(lambda self: self.media.wellformed)
    233
    234 -    def deleteRule(self, index):
    235          """
    236          index
    237              within the media block's rule collection of the rule to remove.
    238
    239          Used to delete a rule from the media block.
    240
    241          DOMExceptions
    242
    243          - INDEX_SIZE_ERR: (self)
    244            Raised if the specified index does not correspond to a rule in
    245            the media rule list.
    246          - NO_MODIFICATION_ALLOWED_ERR: (self)
    247            Raised if this media rule is readonly.
    248          """
    249          self._checkReadonly()
    250
    251          try:
    252              self.cssRules[index]._parentRule = None # detach
    253              del self.cssRules[index] # remove from @media
    254          except IndexError:
    255              raise xml.dom.IndexSizeErr(
    256                  u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % (
    257                  index, self.cssRules.length))
    258
    259 -    def add(self, rule):
    260          """Add rule to end of this mediarule. Same as ``.insertRule(rule)``."""
    261          self.insertRule(rule, index=None)
    262
    263 -    def insertRule(self, rule, index=None):
    264          """
    265          rule
    266              The parsable text representing the rule. For rule sets this
    267              contains both the selector and the style declaration. For
    268              at-rules, this specifies both the at-identifier and the rule
    269              content.
    270
    271              cssutils also allows rule to be a valid **CSSRule** object
    272
    273          index
    274              within the media block's rule collection of the rule before
    275              which to insert the specified rule. If the specified index is
    276              equal to the length of the media blocks's rule collection, the
    277              rule will be added to the end of the media block.
    278              If index is not given or None rule will be appended to rule
    279              list.
    280
    281          Used to insert a new rule into the media block.
    282
    283          DOMException on setting
    284
    285          - HIERARCHY_REQUEST_ERR:
    286            (no use case yet as no @charset or @import allowed))
    287            Raised if the rule cannot be inserted at the specified index,
    288            e.g., if an @import rule is inserted after a standard rule set
    289            or other at-rule.
    290          - INDEX_SIZE_ERR: (self)
    291            Raised if the specified index is not a valid insertion point.
    292          - NO_MODIFICATION_ALLOWED_ERR: (self)
    293            Raised if this media rule is readonly.
    294          - SYNTAX_ERR: (CSSStyleRule)
    295            Raised if the specified rule has a syntax error and is
    296            unparsable.
    297
    298          returns the index within the media block's rule collection of the
    299          newly inserted rule.
    300
    301          """
    302          self._checkReadonly()
    303
    304          # check position
    305          if index is None:
    306              index = len(self.cssRules)
    307          elif index < 0 or index > self.cssRules.length:
    308              raise xml.dom.IndexSizeErr(
    309                  u'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % (
    310                      index, self.cssRules.length))
    311
    312          # parse
    313          if isinstance(rule, basestring):
    314              tempsheet = cssutils.css.CSSStyleSheet()
    315              tempsheet.cssText = rule
    316              if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and
    317               not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)):
    318                  self._log.error(u'CSSMediaRule: Invalid Rule: %s' % rule)
    319                  return
    320              rule = tempsheet.cssRules[0]
    321          elif not isinstance(rule, cssutils.css.CSSRule):
    322              self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule)
    323              return
    324
    325          # CHECK HIERARCHY
    326          # @charset @import @page @namespace @media
    327          if isinstance(rule, cssutils.css.CSSCharsetRule) or \
    328             isinstance(rule, cssutils.css.CSSFontFaceRule) or \
    329             isinstance(rule, cssutils.css.CSSImportRule) or \
    330             isinstance(rule, cssutils.css.CSSNamespaceRule) or \
    331             isinstance(rule, cssutils.css.CSSPageRule) or \
    332             isinstance(rule, CSSMediaRule):
    333              self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' %
    334                        rule.cssText,
    335                        error=xml.dom.HierarchyRequestErr)
    336              return
    337
    338          self.cssRules.insert(index, rule)
    339          rule._parentRule = self
    340          rule._parentStyleSheet = self.parentStyleSheet
    341          return index
    342
    343 -    def __repr__(self):
    344          return "cssutils.css.%s(mediaText=%r)" % (
    345                  self.__class__.__name__, self.media.mediaText)
    346
    347 -    def __str__(self):
    348          return "<cssutils.css.%s object mediaText=%r at 0x%x>" % (
    349                  self.__class__.__name__, self.media.mediaText, id(self))
    350