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 1116 2008-03-05 13:52:23Z 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 = 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          self._name = name
    225
    226      name = property(lambda self: self._name, _setName,
    227                      doc=u"An optional name for the media rules")
    228
    229      media = property(lambda self: self._media,
    230          doc=u"(DOM readonly) A list of media types for this rule of type\
    231              MediaList")
    232
    233      wellformed = property(lambda self: self.media.wellformed)
    234
    235 -    def deleteRule(self, index):
    236          """
    237          index
    238              within the media block's rule collection of the rule to remove.
    239
    240          Used to delete a rule from the media block.
    241
    242          DOMExceptions
    243
    244          - INDEX_SIZE_ERR: (self)
    245            Raised if the specified index does not correspond to a rule in
    246            the media rule list.
    247          - NO_MODIFICATION_ALLOWED_ERR: (self)
    248            Raised if this media rule is readonly.
    249          """
    250          self._checkReadonly()
    251
    252          try:
    253              self.cssRules[index]._parentRule = None # detach
    254              del self.cssRules[index] # remove from @media
    255          except IndexError:
    256              raise xml.dom.IndexSizeErr(
    257                  u'CSSMediaRule: %s is not a valid index in the rulelist of length %i' % (
    258                  index, self.cssRules.length))
    259
    260 -    def add(self, rule):
    261          """
    262          Adds rule to end of this mediarule. Same as ``.insertRule(rule)``.
    263          """
    264          self.insertRule(rule, index=None)
    265
    266 -    def insertRule(self, rule, index=None):
    267          """
    268          rule
    269              The parsable text representing the rule. For rule sets this
    270              contains both the selector and the style declaration. For
    271              at-rules, this specifies both the at-identifier and the rule
    272              content.
    273
    274              cssutils also allows rule to be a valid **CSSRule** object
    275
    276          index
    277              within the media block's rule collection of the rule before
    278              which to insert the specified rule. If the specified index is
    279              equal to the length of the media blocks's rule collection, the
    280              rule will be added to the end of the media block.
    281              If index is not given or None rule will be appended to rule
    282              list.
    283
    284          Used to insert a new rule into the media block.
    285
    286          DOMException on setting
    287
    288          - HIERARCHY_REQUEST_ERR:
    289            (no use case yet as no @charset or @import allowed))
    290            Raised if the rule cannot be inserted at the specified index,
    291            e.g., if an @import rule is inserted after a standard rule set
    292            or other at-rule.
    293          - INDEX_SIZE_ERR: (self)
    294            Raised if the specified index is not a valid insertion point.
    295          - NO_MODIFICATION_ALLOWED_ERR: (self)
    296            Raised if this media rule is readonly.
    297          - SYNTAX_ERR: (CSSStyleRule)
    298            Raised if the specified rule has a syntax error and is
    299            unparsable.
    300
    301          returns the index within the media block's rule collection of the
    302          newly inserted rule.
    303
    304          """
    305          self._checkReadonly()
    306
    307          # check position
    308          if index is None:
    309              index = len(self.cssRules)
    310          elif index < 0 or index > self.cssRules.length:
    311              raise xml.dom.IndexSizeErr(
    312                  u'CSSMediaRule: Invalid index %s for CSSRuleList with a length of %s.' % (
    313                      index, self.cssRules.length))
    314
    315          # parse
    316          if isinstance(rule, basestring):
    317              tempsheet = cssutils.css.CSSStyleSheet()
    318              tempsheet.cssText = rule
    319              if len(tempsheet.cssRules) != 1 or (tempsheet.cssRules and
    320               not isinstance(tempsheet.cssRules[0], cssutils.css.CSSRule)):
    321                  self._log.error(u'CSSMediaRule: Invalid Rule: %s' % rule)
    322                  return
    323              rule = tempsheet.cssRules[0]
    324          elif not isinstance(rule, cssutils.css.CSSRule):
    325              self._log.error(u'CSSMediaRule: Not a CSSRule: %s' % rule)
    326              return
    327
    328          # CHECK HIERARCHY
    329          # @charset @import @page @namespace @media
    330          if isinstance(rule, cssutils.css.CSSCharsetRule) or \
    331             isinstance(rule, cssutils.css.CSSFontFaceRule) or \
    332             isinstance(rule, cssutils.css.CSSImportRule) or \
    333             isinstance(rule, cssutils.css.CSSNamespaceRule) or \
    334             isinstance(rule, cssutils.css.CSSPageRule) or \
    335             isinstance(rule, CSSMediaRule):
    336              self._log.error(u'CSSMediaRule: This type of rule is not allowed here: %s' %
    337                        rule.cssText,
    338                        error=xml.dom.HierarchyRequestErr)
    339              return
    340
    341          self.cssRules.insert(index, rule)
    342          rule._parentRule = self
    343          rule._parentStyleSheet = self.parentStyleSheet
    344          return index
    345
    346 -    def __repr__(self):
    347          return "cssutils.css.%s(mediaText=%r)" % (
    348                  self.__class__.__name__, self.media.mediaText)
    349
    350 -    def __str__(self):
    351          return "<cssutils.css.%s object mediaText=%r at 0x%x>" % (
    352                  self.__class__.__name__, self.media.mediaText, id(self))
    353