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