Package cssutils :: Module serialize |
[hide private]
[frames] | no frames]

# Source Code for Module cssutils.serialize

      1  #!/usr/bin/env python
      2  # -*- coding: utf-8 -*-
      3  """serializer classes for CSS classes
      4
      5  """
      6  __all__ = ['CSSSerializer', 'Preferences']
      7  __docformat__ = 'restructuredtext'
      8  __version__ = '$Id: serialize.py 1329 2008-07-09 12:28:02Z cthedot $'
      9  import codecs
     10  import re
     11  import cssutils
     12  import util
     13
     14 -def _escapecss(e):
     15      """
     16      Escapes characters not allowed in the current encoding the CSS way
     17      with a backslash followed by a uppercase hex code point
     18
     19      E.g. the german umlaut 'ä' is escaped as \E4
     20      """
     21      s = e.object[e.start:e.end]
     22      return u''.join([ur'\%s ' % str(hex(ord(x)))[2:] # remove 0x from hex
     23                       .upper() for x in s]), e.end
     24
     25  codecs.register_error('escapecss', _escapecss)
     26
     27
     28 -class Preferences(object):
     29      """
     30      controls output of CSSSerializer
     31
     32      defaultAtKeyword = True
     33          Should the literal @keyword from src CSS be used or the default
     34          form, e.g. if ``True``: ``@import`` else: ``@i\mport``
     35      defaultPropertyName = True
     36          Should the normalized propertyname be used or the one given in
     37          the src file, e.g. if ``True``: ``color`` else: ``c\olor``
     38
     39          Only used if ``keepAllProperties==False``.
     40
     41      defaultPropertyPriority = True
     42          Should the normalized or literal priority be used, e.g. '!important'
     43          or u'!Im\portant'
     44
     45      importHrefFormat = None
     46          Uses hreftype if ``None`` or explicit ``'string'`` or ``'uri'``
     47      indent = 4 * ' '
     48          Indentation of e.g Properties inside a CSSStyleDeclaration
     49      indentSpecificities = False
     50          Indent rules with subset of Selectors and higher Specitivity
     51
     52      keepAllProperties = True
     53          If ``True`` all properties set in the original CSSStylesheet
     54          are kept meaning even properties set twice with the exact same
     55          same name are kept!
     56      keepComments = True
     57          If ``False`` removes all CSSComments
     58      keepEmptyRules = False
     59          defines if empty rules like e.g. ``a {}`` are kept in the resulting
     60          serialized sheet
     61      keepUsedNamespaceRulesOnly = False
     62          if True only namespace rules which are actually used are kept
     63
     64      lineNumbers = False
     65          Only used if a complete CSSStyleSheet is serialized.
     66      lineSeparator = u'\\n'
     67          How to end a line. This may be set to e.g. u'' for serializing of
     68          CSSStyleDeclarations usable in HTML style attribute.
     69      listItemSpacer = u' '
     70          string which is used in ``css.SelectorList``, ``css.CSSValue`` and
     71          ``stylesheets.MediaList`` after the comma
     72      omitLastSemicolon = True
     73          If ``True`` omits ; after last property of CSSStyleDeclaration
     74      paranthesisSpacer = u' '
     75          string which is used before an opening paranthesis like in a
     76          ``css.CSSMediaRule`` or ``css.CSSStyleRule``
     77      propertyNameSpacer = u' '
     78          string which is used after a Property name colon
     79      selectorCombinatorSpacer = u' '
     80          string which is used before and after a Selector combinator like +, > or ~.
     81          CSSOM defines a single space for this which is also the default in cssutils.
     82      spacer = u' '
     83          general spacer, used e.g. by CSSUnknownRule
     84
     85      validOnly = False **DO NOT CHANGE YET**
     86          if True only valid (currently Properties) are kept
     87
     88          A Property is valid if it is a known Property with a valid value.
     89          Currently CSS 2.1 values as defined in cssproperties.py would be
     90          valid.
     91
     92      """
     93 -    def __init__(self, **initials):
     94          """
     95          Always use named instead of positional parameters
     96          """
     97          self.useDefaults()
     98
     99          for key, value in initials.items():
    100              if value:
    101                  self.__setattr__(key, value)
    102
    103 -    def useDefaults(self):
    104          "reset all preference options to the default value"
    105          self.defaultAtKeyword = True
    106          self.defaultPropertyName = True
    107          self.defaultPropertyPriority = True
    108          self.importHrefFormat = None
    109          self.indent = 4 * u' '
    110          self.indentSpecificities = False
    111          self.keepAllProperties = True
    112          self.keepComments = True
    113          self.keepEmptyRules = False
    114          self.keepUsedNamespaceRulesOnly = False
    115          self.lineNumbers = False
    116          self.lineSeparator = u'\n'
    117          self.listItemSpacer = u' '
    118          self.omitLastSemicolon = True
    119          self.paranthesisSpacer = u' '
    120          self.propertyNameSpacer = u' '
    121          self.selectorCombinatorSpacer = u' '
    122          self.spacer = u' '
    123          self.validOnly = False # should not be changed currently!!!
    124
    125 -    def useMinified(self):
    126          """
    127          sets options to achive a minified stylesheet
    128
    129          you may want to set preferences with this convenience method
    130          and set settings you want adjusted afterwards
    131          """
    132          self.importHrefFormat = 'string'
    133          self.indent = u''
    134          self.keepComments = False
    135          self.keepEmptyRules = False
    136          self.keepUsedNamespaceRulesOnly = True
    137          self.lineNumbers = False
    138          self.lineSeparator = u''
    139          self.listItemSpacer = u''
    140          self.omitLastSemicolon = True
    141          self.paranthesisSpacer = u''
    142          self.propertyNameSpacer = u''
    143          self.selectorCombinatorSpacer = u''
    144          self.spacer = u''
    145          self.validOnly = False
    146
    147 -    def __repr__(self):
    148          return u"cssutils.css.%s(%s)" % (self.__class__.__name__,
    149              u', '.join(['\n    %s=%r' % (p, self.__getattribute__(p)) for p in self.__dict__]
    150                  ))
    151
    152 -    def __str__(self):
    153          return u"<cssutils.css.%s object %s at 0x%x" % (self.__class__.__name__,
    154              u' '.join(['%s=%r' % (p, self.__getattribute__(p)) for p in self.__dict__]
    155                  ),
    156                  id(self))
    157
    158
    159 -class Out(object):
    160      """
    161      a simple class which makes appended items available as a combined string
    162      """
    163 -    def __init__(self, ser):
    164          self.ser = ser
    165          self.out = []
    166
    167 -    def _remove_last_if_S(self):
    168          if self.out and not self.out[-1].strip():
    169              # remove trailing S
    170              del self.out[-1]
    171
    172 -    def append(self, val, typ=None, space=True, keepS=False, indent=False,
    173                 lineSeparator=False):
    174          """Appends val. Adds a single S after each token except as follows:
    175
    176          - typ COMMENT
    177              uses cssText depending on self.ser.prefs.keepComments
    178          - typ "Property", cssutils.css.CSSRule.UNKNOWN_RULE
    179              uses cssText
    180          - typ STRING
    181              escapes ser._string
    182          - typ S
    183              ignored except ``keepS=True``
    184          - typ URI
    185              calls ser_uri
    186          - val ``{``
    187              adds LF after
    188          - val ``;``
    189              removes S before and adds LF after
    190          - val ``, :``
    191              removes S before
    192          - val ``+ > ~``
    193              encloses in prefs.selectorCombinatorSpacer
    194          - some other vals
    195              add ``*spacer`` except ``space=False``
    196          """
    197          if val or 'STRING' == typ:
    198              # PRE
    199              if 'COMMENT' == typ:
    200                  if self.ser.prefs.keepComments:
    201                      val = val.cssText
    202                  else:
    203                      return
    204              elif typ in ('Property', cssutils.css.CSSRule.UNKNOWN_RULE):
    205                  val = val.cssText
    206              elif 'S' == typ and not keepS:
    207                  return
    208              elif 'STRING' == typ:
    209                  # may be empty but MUST not be None
    210                  if val is None:
    211                      return
    212                  val = self.ser._string(val)
    213              elif 'URI' == typ:
    214                  val = self.ser._uri(val)
    215              elif val in u'+>~,:{;)]':
    216                  self._remove_last_if_S()
    217
    218              # APPEND
    219              if indent:
    220                  self.out.append(self.ser._indentblock(val, self.ser._level+1))
    221              else:
    222                  self.out.append(val)
    223              # POST
    224              if lineSeparator:
    225                  # Property , ...
    226                  pass
    227              elif val in u'+>~': # enclose selector combinator
    228                  self.out.insert(-1, self.ser.prefs.selectorCombinatorSpacer)
    229                  self.out.append(self.ser.prefs.selectorCombinatorSpacer)
    230              elif u',' == val: # list
    231                  self.out.append(self.ser.prefs.listItemSpacer)
    232              elif u':' == val: # prop
    233                  self.out.append(self.ser.prefs.propertyNameSpacer)
    234              elif u'{' == val: # block start
    235                  self.out.insert(-1, self.ser.prefs.paranthesisSpacer)
    236                  self.out.append(self.ser.prefs.lineSeparator)
    237              elif u';' == val: # end or prop or block
    238                  self.out.append(self.ser.prefs.lineSeparator)
    239              elif val not in u'}[]()' and space:
    240                  self.out.append(self.ser.prefs.spacer)
    241
    242 -    def value(self, delim=u'', end=None):
    243          "returns all items joined by delim"
    244          self._remove_last_if_S()
    245          if end:
    246              self.out.append(end)
    247          return delim.join(self.out)
    248
    249
    250 -class CSSSerializer(object):
    251      """
    252      Methods to serialize a CSSStylesheet and its parts
    253
    254      To use your own serializing method the easiest is to subclass CSS
    255      Serializer and overwrite the methods you like to customize.
    256      """
    257      # chars not in URI without quotes around
    258      __forbidden_in_uri_matcher = re.compile(ur'''.*?[\)\s\;]''', re.U).match
    259
    260 -    def __init__(self, prefs=None):
    261          """
    262          prefs
    263              instance of Preferences
    264          """
    265          if not prefs:
    266              prefs = Preferences()
    267          self.prefs = prefs
    268          self._level = 0 # current nesting level
    269
    270          # TODO:
    271          self._selectors = [] # holds SelectorList
    272          self._selectorlevel = 0 # current specificity nesting level
    273
    274 -    def _atkeyword(self, rule, default):
    275          "returns default or source atkeyword depending on prefs"
    276          if self.prefs.defaultAtKeyword:
    277              return default
    278          else:
    279              return rule.atkeyword
    280
    281 -    def _indentblock(self, text, level):
    282          """
    283          indent a block like a CSSStyleDeclaration to the given level
    284          which may be higher than self._level (e.g. for CSSStyleDeclaration)
    285          """
    286          if not self.prefs.lineSeparator:
    287              return text
    288          return self.prefs.lineSeparator.join(
    289              [u'%s%s' % (level * self.prefs.indent, line)
    290                  for line in text.split(self.prefs.lineSeparator)]
    291          )
    292
    293 -    def _propertyname(self, property, actual):
    294          """
    295          used by all styledeclarations to get the propertyname used
    296          dependent on prefs setting defaultPropertyName and
    297          keepAllProperties
    298          """
    299          if self.prefs.defaultPropertyName and not self.prefs.keepAllProperties:
    300              return property.name
    301          else:
    302              return actual
    303
    304 -    def _linenumnbers(self, text):
    305          if self.prefs.lineNumbers:
    306              pad = len(str(text.count(self.prefs.lineSeparator)+1))
    307              out = []
    308              for i, line in enumerate(text.split(self.prefs.lineSeparator)):
    309                  out.append((u'%*i: %s') % (pad, i+1, line))
    310              text = self.prefs.lineSeparator.join(out)
    311          return text
    312
    313 -    def _string(self, s):
    314          """
    315          returns s encloded between "..." and escaped delim charater ",
    316          escape line breaks \\n \\r and \\f
    317          """
    318          # \n = 0xa, \r = 0xd, \f = 0xc
    319          s = s.replace('\n', '\\a ').replace(
    320                        '\r', '\\d ').replace(
    321                        '\f', '\\c ')
    322          return u'"%s"' % s.replace('"', u'\\"')
    323
    324 -    def _uri(self, uri):
    325          """returns uri enclosed in url() and "..." if necessary"""
    326          if CSSSerializer.__forbidden_in_uri_matcher(uri):
    327              return 'url(%s)' % self._string(uri)
    328          else:
    329              return 'url(%s)' % uri
    330
    331 -    def _valid(self, x):
    332          "checks items valid property and prefs.validOnly"
    333          return not self.prefs.validOnly or (self.prefs.validOnly and
    334                                              x.valid)
    335
    336 -    def do_CSSStyleSheet(self, stylesheet):
    337          """serializes a complete CSSStyleSheet"""
    338          useduris = stylesheet._getUsedURIs()
    339          out = []
    340          for rule in stylesheet.cssRules:
    341              if self.prefs.keepUsedNamespaceRulesOnly and\
    342                 rule.NAMESPACE_RULE == rule.type and\
    343                 rule.namespaceURI not in useduris and (
    344                      rule.prefix or None not in useduris):
    345                  continue
    346
    347              cssText = rule.cssText
    348              if cssText:
    349                  out.append(cssText)
    350          text = self._linenumnbers(self.prefs.lineSeparator.join(out))
    351
    352          # get encoding of sheet, defaults to UTF-8
    353          try:
    354              encoding = stylesheet.cssRules[0].encoding
    355          except (IndexError, AttributeError):
    356              encoding = 'UTF-8'
    357
    358          return text.encode(encoding, 'escapecss')
    359
    360 -    def do_CSSComment(self, rule):
    361          """
    362          serializes CSSComment which consists only of commentText
    363          """
    364          if rule._cssText and self.prefs.keepComments:
    365              return rule._cssText
    366          else:
    367              return u''
    368
    369 -    def do_CSSCharsetRule(self, rule):
    370          """
    371          serializes CSSCharsetRule
    372          encoding: string
    373
    374          always @charset "encoding";
    375          no comments or other things allowed!
    376          """
    377          if rule.wellformed:
    378              return u'@charset %s;' % self._string(rule.encoding)
    379          else:
    380              return u''
    381
    382 -    def do_CSSFontFaceRule(self, rule):
    383          """
    384          serializes CSSFontFaceRule
    385
    386          style
    387              CSSStyleDeclaration
    388
    389          + CSSComments
    390          """
    391          styleText = self.do_css_CSSStyleDeclaration(rule.style)
    392
    393          if styleText and rule.wellformed:
    394              out = Out(self)
    395              out.append(self._atkeyword(rule, u'@font-face'))
    396              for item in rule.seq:
    397                  # assume comments {
    398                  out.append(item.value, item.type)
    399              out.append(u'{')
    400              out.append(u'%s%s}' % (styleText, self.prefs.lineSeparator),
    401                         indent=1)
    402              return out.value()
    403          else:
    404              return u''
    405
    406 -    def do_CSSImportRule(self, rule):
    407          """
    408          serializes CSSImportRule
    409
    410          href
    411              string
    412          media
    413              optional cssutils.stylesheets.medialist.MediaList
    414          name
    415              optional string
    416
    417          + CSSComments
    418          """
    419          if rule.wellformed:
    420              out = Out(self)
    421              out.append(self._atkeyword(rule, u'@import'))
    422
    423              for item in rule.seq:
    424                  typ, val = item.type, item.value
    425                  if 'href' == typ:
    426                      # "href" or url(href)
    427                      if self.prefs.importHrefFormat == 'string' or (
    428                               self.prefs.importHrefFormat != 'uri' and
    429                               rule.hreftype == 'string'):
    430                          out.append(val, 'STRING')
    431                      else:
    432                          if not len(self.prefs.spacer):
    433                              out.append(u' ')
    434                          out.append(val, 'URI')
    435                  elif 'media' == typ:
    436                      # media
    437                      mediaText = self.do_stylesheets_medialist(val)
    438                      if mediaText and mediaText != u'all':
    439                          out.append(mediaText)
    440                  elif 'name' == typ:
    441                      out.append(val, 'STRING')
    442                  else:
    443                      out.append(val, typ)
    444
    445              return out.value(end=u';')
    446          else:
    447              return u''
    448
    449 -    def do_CSSNamespaceRule(self, rule):
    450          """
    451          serializes CSSNamespaceRule
    452
    453          uri
    454              string
    455          prefix
    456              string
    457
    458          + CSSComments
    459          """
    460          if rule.wellformed:
    461              out = Out(self)
    462              out.append(self._atkeyword(rule, u'@namespace'))
    463              if not len(self.prefs.spacer):
    464                  out.append(u' ')
    465
    466              for item in rule.seq:
    467                  typ, val = item.type, item.value
    468                  if 'namespaceURI' == typ:
    469                      out.append(val, 'STRING')
    470                  else:
    471                      out.append(val, typ)
    472
    473              return out.value(end=u';')
    474          else:
    475              return u''
    476
    477 -    def do_CSSMediaRule(self, rule):
    478          """
    479          serializes CSSMediaRule
    480
    481          + CSSComments
    482          """
    483          # TODO: use Out()?
    484
    485          # mediaquery
    486          if not rule.media.wellformed:
    487              return u''
    488
    489          # @media
    490          out = [self._atkeyword(rule, u'@media')]
    491          if not len(self.prefs.spacer):
    492              # for now always with space as only webkit supports @mediaall?
    493              out.append(u' ')
    494          else:
    495              out.append(self.prefs.spacer) # might be empty
    496
    497          out.append(self.do_stylesheets_medialist(rule.media))
    498
    499          # name, seq contains content after name only (Comments)
    500          if rule.name:
    501              out.append(self.prefs.spacer)
    502              nameout = Out(self)
    503              nameout.append(self._string(rule.name))
    504              for item in rule.seq:
    505                  nameout.append(item.value, item.type)
    506              out.append(nameout.value())
    507
    508          #  {
    509          out.append(self.prefs.paranthesisSpacer)
    510          out.append(u'{')
    511          out.append(self.prefs.lineSeparator)
    512
    513          # rules
    514          rulesout = []
    515          for r in rule.cssRules:
    516              rtext = r.cssText
    517              if rtext:
    518                  # indent each line of cssText
    519                  rulesout.append(self._indentblock(rtext, self._level + 1))
    520                  rulesout.append(self.prefs.lineSeparator)
    521          if not self.prefs.keepEmptyRules and not u''.join(rulesout).strip():
    522              return u''
    523          out.extend(rulesout)
    524
    525          #     }
    526          out.append(u'%s}' % ((self._level + 1) * self.prefs.indent))
    527
    528          return u''.join(out)
    529
    530 -    def do_CSSPageRule(self, rule):
    531          """
    532          serializes CSSPageRule
    533
    534          selectorText
    535              string
    536          style
    537              CSSStyleDeclaration
    538
    539          + CSSComments
    540          """
    541          styleText = self.do_css_CSSStyleDeclaration(rule.style)
    542
    543          if styleText and rule.wellformed:
    544              out = Out(self)
    545              out.append(self._atkeyword(rule, u'@page'))
    546              if not len(self.prefs.spacer):
    547                  out.append(u' ')
    548
    549              for item in rule.seq:
    550                  out.append(item.value, item.type)
    551
    552              out.append(u'{')
    553              out.append(u'%s%s}' % (styleText, self.prefs.lineSeparator),
    554                         indent=1)
    555              return out.value()
    556          else:
    557              return u''
    558
    559 -    def do_CSSUnknownRule(self, rule):
    560          """
    561          serializes CSSUnknownRule
    562          anything until ";" or "{...}"
    563          + CSSComments
    564          """
    565          if rule.wellformed:
    566              out = Out(self)
    567              out.append(rule.atkeyword)
    568              if not len(self.prefs.spacer):
    569                  out.append(u' ')
    570
    571              stacks = []
    572              for item in rule.seq:
    573                  typ, val = item.type, item.value
    574                  # PRE
    575                  if u'}' == val:
    576                      # close last open item on stack
    577                      stackblock = stacks.pop().value()
    578                      if stackblock:
    579                          val = self._indentblock(
    580                                 stackblock + self.prefs.lineSeparator + val,
    581                                 min(1, len(stacks)+1))
    582                      else:
    583                          val = self._indentblock(val, min(1, len(stacks)+1))
    584                  # APPEND
    585                  if stacks:
    586                      stacks[-1].append(val, typ)
    587                  else:
    588                      out.append(val, typ)
    589
    590                  # POST
    591                  if u'{' == val:
    592                      # new stack level
    593                      stacks.append(Out(self))
    594
    595              return out.value()
    596          else:
    597              return u''
    598
    599 -    def do_CSSStyleRule(self, rule):
    600          """
    601          serializes CSSStyleRule
    602
    603          selectorList
    604          style
    605
    606          + CSSComments
    607          """
    608          # TODO: use Out()
    609
    610          # prepare for element nested rules
    611          # TODO: sort selectors!
    612          if self.prefs.indentSpecificities:
    613              # subselectorlist?
    614              elements = set([s.element for s in rule.selectorList])
    615              specitivities = [s.specificity for s in rule.selectorList]
    616              for selector in self._selectors:
    617                  lastelements = set([s.element for s in selector])
    618                  if elements.issubset(lastelements):
    619                      # higher specificity?
    620                      lastspecitivities = [s.specificity for s in selector]
    621                      if specitivities > lastspecitivities:
    622                          self._selectorlevel += 1
    623                          break
    624                  elif self._selectorlevel > 0:
    625                      self._selectorlevel -= 1
    626              else:
    627                  # save new reference
    628                  self._selectors.append(rule.selectorList)
    629                  self._selectorlevel = 0
    630
    631          # TODO ^ RESOLVE!!!!
    632
    633          selectorText = self.do_css_SelectorList(rule.selectorList)
    634          if not selectorText or not rule.wellformed:
    635              return u''
    636          self._level += 1
    637          styleText = u''
    638          try:
    639              styleText = self.do_css_CSSStyleDeclaration(rule.style)
    640          finally:
    641              self._level -= 1
    642          if not styleText:
    643                  if self.prefs.keepEmptyRules:
    644                      return u'%s%s{}' % (selectorText,
    645                                          self.prefs.paranthesisSpacer)
    646          else:
    647              return self._indentblock(
    648                  u'%s%s{%s%s%s%s}' % (
    649                      selectorText,
    650                      self.prefs.paranthesisSpacer,
    651                      self.prefs.lineSeparator,
    652                      self._indentblock(styleText, self._level + 1),
    653                      self.prefs.lineSeparator,
    654                      (self._level + 1) * self.prefs.indent),
    655                  self._selectorlevel)
    656
    657 -    def do_css_SelectorList(self, selectorlist):
    658          "comma-separated list of Selectors"
    659          # does not need Out() as it is too simple
    660          if selectorlist.wellformed:
    661              out = []
    662              for part in selectorlist.seq:
    663                  if isinstance(part, cssutils.css.Selector):
    664                      out.append(part.selectorText)
    665                  else:
    666                      out.append(part) # should not happen
    667              sep = u',%s' % self.prefs.listItemSpacer
    668              return sep.join(out)
    669          else:
    670              return u''
    671
    672 -    def do_css_Selector(self, selector):
    673          """
    674          a single Selector including comments
    675
    676          an element has syntax (namespaceURI, name) where namespaceURI may be:
    677
    678          - cssutils._ANYNS => ``*|name``
    679          - None => ``name``
    680          - u'' => ``|name``
    681          - any other value: => ``prefix|name``
    682          """
    683          if selector.wellformed:
    684              out = Out(self)
    685
    686              DEFAULTURI = selector._namespaces.get('', None)
    687              for item in selector.seq:
    688                  typ, val = item.type, item.value
    689                  if type(val) == tuple:
    690                      # namespaceURI|name (element or attribute)
    691                      namespaceURI, name = val
    692                      if DEFAULTURI == namespaceURI or (not DEFAULTURI and
    693                                                        namespaceURI is None):
    694                          out.append(name, typ, space=False)
    695                      else:
    696                          if namespaceURI == cssutils._ANYNS:
    697                              prefix = u'*'
    698                          else:
    699                              try:
    700                                  prefix = selector._namespaces.prefixForNamespaceURI(
    701                                                      namespaceURI)
    702                              except IndexError:
    703                                  prefix = u''
    704
    705                          out.append(u'%s|%s' % (prefix, name), typ, space=False)
    706                  else:
    707                      out.append(val, typ, space=False, keepS=True)
    708
    709              return out.value()
    710          else:
    711              return u''
    712
    713 -    def do_css_CSSStyleDeclaration(self, style, separator=None):
    714          """
    715          Style declaration of CSSStyleRule
    716          """
    717  #        # TODO: use Out()
    718
    719          # may be comments only
    720          if len(style.seq) > 0:
    721              if separator is None:
    722                  separator = self.prefs.lineSeparator
    723
    724              if self.prefs.keepAllProperties:
    725                  # all
    726                  seq = style.seq
    727              else:
    728                  # only effective ones
    729                  _effective = style.getProperties()
    730                  seq = [item for item in style.seq
    731                           if (isinstance(item.value, cssutils.css.Property)
    732                               and item.value in _effective)
    733                           or not isinstance(item.value, cssutils.css.Property)]
    734
    735              out = []
    736              for i, item in enumerate(seq):
    737                  typ, val = item.type, item.value
    738                  if isinstance(val, cssutils.css.CSSComment):
    739                      # CSSComment
    740                      if self.prefs.keepComments:
    741                          out.append(val.cssText)
    742                          out.append(separator)
    743                  elif isinstance(val, cssutils.css.Property):
    744                      # PropertySimilarNameList
    745                      out.append(self.do_Property(val))
    746                      if not (self.prefs.omitLastSemicolon and i==len(seq)-1):
    747                          out.append(u';')
    748                      out.append(separator)
    749                  elif isinstance(val, cssutils.css.CSSUnknownRule):
    750                      # @rule
    751                      out.append(val.cssText)
    752                      out.append(separator)
    753                  else:
    754                      # ?
    755                      out.append(val)
    756                      out.append(separator)
    757
    758              if out and out[-1] == separator:
    759                  del out[-1]
    760
    761              return u''.join(out)
    762
    763          else:
    764              return u''
    765
    766 -    def do_Property(self, property):
    767          """
    768          Style declaration of CSSStyleRule
    769
    770          Property has a seqs attribute which contains seq lists for
    771          name, a CSSvalue and a seq list for priority
    772          """
    773          # TODO: use Out()
    774
    775          out = []
    776          if property.seqs[0] and property.wellformed and self._valid(property):
    777              nameseq, cssvalue, priorityseq = property.seqs
    778
    779              #name
    780              for part in nameseq:
    781                  if hasattr(part, 'cssText'):
    782                      out.append(part.cssText)
    783                  elif property.literalname == part:
    784                      out.append(self._propertyname(property, part))
    785                  else:
    786                      out.append(part)
    787
    788              if out and (not property._mediaQuery or
    789                          property._mediaQuery and cssvalue.cssText):
    790                  # MediaQuery may consist of name only
    791                  out.append(u':')
    792                  out.append(self.prefs.propertyNameSpacer)
    793
    794              # value
    795              out.append(cssvalue.cssText)
    796
    797              # priority
    798              if out and priorityseq:
    799                  out.append(u' ')
    800                  for part in priorityseq:
    801                      if hasattr(part, 'cssText'): # comments
    802                          out.append(part.cssText)
    803                      else:
    804                          if part == property.literalpriority and\
    805                             self.prefs.defaultPropertyPriority:
    806                              out.append(property.priority)
    807                          else:
    808                              out.append(part)
    809
    810          return u''.join(out)
    811
    812 -    def do_Property_priority(self, priorityseq):
    813          """
    814          a Properties priority "!" S* "important"
    815          """
    816          # TODO: use Out()
    817
    818          out = []
    819          for part in priorityseq:
    820              if hasattr(part, 'cssText'): # comments
    821                  out.append(u' ')
    822                  out.append(part.cssText)
    823                  out.append(u' ')
    824              else:
    825                  out.append(part)
    826          return u''.join(out).strip()
    827
    828 -    def do_css_CSSValue(self, cssvalue):
    829          """
    830          serializes a CSSValue
    831          """
    832          # TODO: use Out()
    833          # TODO: use self._valid(cssvalue)?
    834
    835          if not cssvalue:
    836              return u''
    837          else:
    838              sep = u',%s' % self.prefs.listItemSpacer
    839              out = []
    840              for part in cssvalue.seq:
    841                  if hasattr(part, 'cssText'):
    842                      # comments or CSSValue if a CSSValueList
    843                      out.append(part.cssText)
    844                  elif isinstance(part, basestring) and part == u',':
    845                      out.append(sep)
    846                  else:
    847                      # TODO: escape func parameter if STRING!
    848                      if part and part[0] == part[-1] and part[0] in '\'"':
    849                          # string has " " around it in CSSValue!
    850                          part = self._string(part[1:-1])
    851                      out.append(part)
    852              return (u''.join(out)).strip()
    853
    854 -    def do_stylesheets_medialist(self, medialist):
    855          """
    856          comma-separated list of media, default is 'all'
    857
    858          If "all" is in the list, every other media *except* "handheld" will
    859          be stripped. This is because how Opera handles CSS for PDAs.
    860          """
    861          if len(medialist) == 0:
    862              return u'all'
    863          else:
    864              sep = u',%s' % self.prefs.listItemSpacer
    865              return sep.join((mq.mediaText for mq in medialist))
    866
    867 -    def do_stylesheets_mediaquery(self, mediaquery):
    868          """
    869          a single media used in medialist
    870          """
    871          if mediaquery.wellformed:
    872              out = []
    873              for part in mediaquery.seq:
    874                  if isinstance(part, cssutils.css.Property): # Property
    875                      out.append(u'(%s)' % part.cssText)
    876                  elif hasattr(part, 'cssText'): # comments
    877                      out.append(part.cssText)
    878                  else:
    879                      # TODO: media queries!
    880                      out.append(part)
    881              return u' '.join(out)
    882          else:
    883              return u''
    884