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