1. cthedot
  2. cssutils

Commits

cthedot  committed 95d288c

initial version of MarginRule

  • Participants
  • Parent commits 7ae0a3d
  • Branches default

Comments (0)

Files changed (14)

File src/cssutils/css/__init__.py

View file
     'CSSMediaRule',
     'CSSNamespaceRule',
     'CSSPageRule',
+    'MarginRule',
     'CSSStyleRule',
     'CSSUnknownRule',
     'CSSVariablesRule',
 from cssmediarule import *
 from cssnamespacerule import *
 from csspagerule import *
+from marginrule import *
 from cssstylerule import *
 from cssvariablesrule import *
 from cssunknownrule import *

File src/cssutils/css/csspagerule.py

View file
 __docformat__ = 'restructuredtext'
 __version__ = '$Id$'
 
+from itertools import chain
 from cssstyledeclaration import CSSStyleDeclaration
+from marginrule import MarginRule
 import cssrule
 import cssutils
 import xml.dom
             self.style = style
         else:
             self.style = CSSStyleDeclaration()
+
+        self.cssRules = cssutils.css.CSSRuleList()
         
         tempseq.append(self.style, 'style')
 
                 self.style.cssText,
                 id(self))
 
+    # TODO: refactor with @media!
+    def _setCssRules(self, cssRules):
+        "Set new cssRules and update contained rules refs."
+        cssRules.append = self.insertRule
+        cssRules.extend = self.insertRule
+        cssRules.__delitem__ == self.deleteRule
+        
+        for rule in cssRules:
+            rule._parentStyleSheet = None #self.parentStyleSheet?
+            rule._parentRule = self
+            
+        self._cssRules = cssRules
+
+    cssRules = property(lambda self: self._cssRules, _setCssRules,
+            "All Rules in this style sheet, a "
+            ":class:`~cssutils.css.CSSRuleList`.")
+
     def __parseSelectorText(self, selectorText):
         """
         Parse `selectorText` which may also be a list of tokens
             
         return wellformed, newseq
 
+
+    def __parseMarginAndStyle(self, tokens):
+        "tokens is a list, no generator (yet)"        
+        g = (t for t in tokens)
+        styletokens = []
+        
+        # new rules until parse done
+        cssRules = []
+        
+        for token in g:
+            if (token[0] == 'ATKEYWORD'):                
+                rule = MarginRule(chain([token], g))
+                cssRules.append(rule)
+                continue
+            
+            styletokens.append(token)
+
+        return cssRules, styletokens
+        
+
     def _getCssText(self):
         """Return serialized property cssText."""
         return cssutils.ser.do_CSSPageRule(self)
                 if 'EOF' == type_:
                     # add again as style needs it
                     styletokens.append(braceorEOFtoken)
+                    
+                # filter pagemargin rules out first
+                cssRules, styletokens = self.__parseMarginAndStyle(styletokens)
+                    
                 # SET, may raise:
                 newStyle.cssText = styletokens
 
             if ok:
                 self._selectorText = newselectorseq
                 self.style = newStyle 
+                self.cssRules = cssutils.css.CSSRuleList()
+                for r in cssRules:
+                    self.cssRules.append(r)
                 
     cssText = property(_getCssText, _setCssText,
         doc=u"(DOM) The parsable textual representation of this rule.")
 
+
     def _getSelectorText(self):
         """Wrapper for cssutils Selector object."""
         return cssutils.ser.do_CSSPageRuleSelector(self._selectorText)
     
     # constant but needed:
     wellformed = property(lambda self: True)
+
+
+    # TODO: refactor with @media
+    def deleteRule(self, index):
+        self._checkReadonly()
+        
+        del self._cssRules[index]
+
+    def insertRule(self, rule, index=None):
+        self._checkReadonly()
+        
+        if index is None:
+            index = len(self._cssRules)
+        
+        self._cssRules.insert(index, rule)
+        rule._parentRule = self
+        rule._parentStyleSheet = self.parentStyleSheet
+        return index

File src/cssutils/css/cssrule.py

View file
     """:class:`cssutils.css.CSSVariablesRule` - experimental rule
     not in the offical spec"""
 
+    MARGIN_RULE = 1006 
+    """:class:`cssutils.css.MarginRule` - experimental rule
+    not in the offical spec"""
+
     _typestrings = {UNKNOWN_RULE: u'UNKNOWN_RULE', 
                     STYLE_RULE: u'STYLE_RULE',
                     CHARSET_RULE: u'CHARSET_RULE', 
                     PAGE_RULE: u'PAGE_RULE',                     
                     NAMESPACE_RULE: u'NAMESPACE_RULE',
                     COMMENT: u'COMMENT',
-                    VARIABLES_RULE: u'VARIABLES_RULE'
+                    VARIABLES_RULE: u'VARIABLES_RULE',
+                    MARGIN_RULE: u'MARGIN_RULE'
                     }
 
     def __init__(self, parentRule=None, parentStyleSheet=None, readonly=False):
         self._parentRule = parentRule
         self._parentStyleSheet = parentStyleSheet
         self._setSeq(self._tempSeq())
+        #self._atkeyword = None
         # must be set after initialization of #inheriting rule is done
         self._readonly = False
 
-    def _setAtkeyword(self, akw):
+    def _setAtkeyword(self, keyword):
         """Check if new keyword fits the rule it is used for."""
-        if not self.atkeyword or (self._normalize(akw) ==
-                                  self._normalize(self.atkeyword)):
-            self._atkeyword = akw
+        atkeyword = self._normalize(keyword)
+        if not self.atkeyword or (self.atkeyword == atkeyword):
+            self._atkeyword = atkeyword
+            self._keyword = keyword
         else:
             self._log.error(u'%s: Invalid atkeyword for this rule: %r' %
-                            (self._normalize(self.atkeyword), akw),
+                            (self.atkeyword, keyword),
                             error=xml.dom.InvalidModificationErr)
 
     atkeyword = property(lambda self: self._atkeyword, _setAtkeyword,
-                         doc=u"Literal keyword of an @rule (e.g. ``@IMport``).")
+                         doc=u"Normalized  keyword of an @rule (e.g. ``@import``).")
 
     def _setCssText(self, cssText):
         """

File src/cssutils/css/cssstyledeclaration.py

View file
         newseq = self._tempSeq()
         wellformed, expected = self._parse(expected=None,
             seq=newseq, tokenizer=tokenizer,
-            productions={'IDENT': ident},#, 'CHAR': char},
+            productions={'IDENT': ident},
             default=unexpected)
         # wellformed set by parse
 

File src/cssutils/prodparser.py

View file
     def __init__(self, name, match, optional=False,
                  toSeq=None, toStore=None,
                  stop=False, stopAndKeep=False, 
-                 nextSor=False, mayEnd=False):
+                 nextSor=False, mayEnd=False,
+                 storeToken=None):
         """
         name
             name used for error reporting
         mayEnd = False
             no token must follow even defined by Sequence.
             Used for operator ',/ ' currently only
+            
+        storeToken = None
+            if True toStore saves simple token tuple and not and Item object
+            to store. Old style processing, TODO: resolve
         """
         self._name = name
         self.match = match
         self.stopAndKeep = stopAndKeep
         self.nextSor = nextSor
         self.mayEnd = mayEnd
+        self.storeToken = storeToken
 
         def makeToStore(key):
             "Return a function used by toStore."
             def toStore(store, item):
                 "Set or append store item."
                 if key in store:
+                    _v = store[key]
+                    if not isinstance(_v, list):
+                        store[key] = [_v]
                     store[key].append(item)
                 else:
                     store[key] = item
                         if val is not None:
                             seq.append(val, type_, line, col)
                             if prod.toStore:
-                                prod.toStore(store, seq[-1])
+                                if not prod.storeToken:
+                                    prod.toStore(store, seq[-1])
+                                else:                                   
+                                    # workaround for now for old style token
+                                    # parsing!
+                                    # TODO: remove when all new style
+                                    prod.toStore(store, token)
                                 
                     if prod.stop: # EOF?
                         # stop here and ignore following tokens

File src/cssutils/serialize.py

View file
                 val = helper.uri(val)
             elif 'HASH' == typ:
                 val = self.ser._hash(val)
-            elif val in u'+>~,:{;)]/=':
+            elif val in u'+>~,:{;)]/=}':
                 self._remove_last_if_S()
 
             # APPEND
-            if indent:
+            
+            if indent or (val == u'}' and self.ser.prefs.indentClosingBrace):
                 self.out.append(self.ser._indentblock(val, self.ser._level+1))
             else:
                 if val.endswith(u' '):
                 self.out.append(self.ser.prefs.lineSeparator)
             elif u';' == val: # end or prop or block
                 self.out.append(self.ser.prefs.lineSeparator)
-            elif val not in u'}[]()/=' and typ != 'FUNCTION' and space:
+            elif val not in u'}[]()/=' and space and typ != 'FUNCTION':
                 self.out.append(self.ser.prefs.spacer)
                 if typ != 'STRING' and not self.ser.prefs.spacer and \
                    self.out and not self.out[-1].endswith(u' '):
         self._selectors = [] # holds SelectorList
         self._selectorlevel = 0 # current specificity nesting level
 
-    def _atkeyword(self, rule, default):
+    def _atkeyword(self, rule):
         "returns default or source atkeyword depending on prefs"
         if self.prefs.defaultAtKeyword:
-            return default
+            return rule.atkeyword # default
         else:
-            return rule.atkeyword
+            return rule._keyword
 
     def _indentblock(self, text, level):
         """
 
         if variablesText and rule.wellformed and not self.prefs.resolveVariables:
             out = Out(self)
-            out.append(self._atkeyword(rule, u'@variables'))
+            out.append(self._atkeyword(rule))
             for item in rule.seq:
                 # assume comments {
                 out.append(item.value, item.type)
 
         if styleText and rule.wellformed:
             out = Out(self)
-            out.append(self._atkeyword(rule, u'@font-face'))
+            out.append(self._atkeyword(rule))
             for item in rule.seq:
                 # assume comments {
                 out.append(item.value, item.type)
         """
         if rule.wellformed:
             out = Out(self)
-            out.append(self._atkeyword(rule, u'@import'))
+            out.append(self._atkeyword(rule))
 
             for item in rule.seq:
                 typ, val = item.type, item.value
         """
         if rule.wellformed:
             out = Out(self)
-            out.append(self._atkeyword(rule, u'@namespace'))
+            out.append(self._atkeyword(rule))
             for item in rule.seq:
                 typ, val = item.type, item.value
                 if 'namespaceURI' == typ:
             return u''
 
         # @media
-        out = [self._atkeyword(rule, u'@media')]
+        out = [self._atkeyword(rule)]
         if not len(self.prefs.spacer):
             # for now always with space as only webkit supports @mediaall?
             out.append(u' ')
             string
         style
             CSSStyleDeclaration
+        cssRules
+            CSSRuleList of MarginRule objects
 
         + CSSComments
         """
-        styleText = self.do_css_CSSStyleDeclaration(rule.style)
-        if styleText and rule.wellformed:
+        
+        # rules
+        rules = u''
+        rulesout = []
+        for r in rule.cssRules:
+            rtext = r.cssText
+            if rtext:
+                # indent each line of cssText
+                rulesout.append(rtext)
+                rulesout.append(self.prefs.lineSeparator)
+        
+        rulesText = u''.join(rulesout).strip()
+
+        # omit semicolon only if no MarginRules
+        styleText = self.do_css_CSSStyleDeclaration(rule.style,
+                                                    omit=not rulesText)
+        
+        if (styleText or rulesText) and rule.wellformed:
             out = Out(self)
-            out.append(self._atkeyword(rule, u'@page'))
+            out.append(self._atkeyword(rule))
             out.append(rule.selectorText)
             out.append(u'{')
-            out.append(u'%s%s}' % (styleText, self.prefs.lineSeparator),
-                       indent=1)
+            
+            if styleText:
+                if not rulesText:
+                    out.append(u'%s%s' % (styleText,
+                                          self.prefs.lineSeparator
+                                          ), indent=1)
+                else:
+                    out.append(styleText, typ='x', indent=1, space=False)
+                
+            if rulesText:            
+                out.append(u'%s%s%s' % (self.prefs.lineSeparator,
+                                      rulesText,
+                                      self.prefs.lineSeparator
+                                      ), indent=1, space=False)
+            #?
+            self._level -= 1 
+            out.append(u'}')
+            self._level += 1 
+            
             return out.value()
         else:
             return u''
                 out.append(item.value, item.type)
         return out.value()
 
+    def do_MarginRule(self, rule):
+        """
+        serializes MarginRule
+
+        atkeyword
+            string
+        style
+            CSSStyleDeclaration
+
+        + CSSComments
+        """
+        # might not be set at all?!
+        if rule.atkeyword:
+            styleText = self.do_css_CSSStyleDeclaration(rule.style)
+                    
+            if styleText and rule.wellformed:
+                out = Out(self)
+                out.append(self._atkeyword(rule), typ='ATKEYWORD')
+                out.append(u'{')
+                out.append(u'%s%s' % (self._indentblock(styleText, self._level+1),
+                                       self.prefs.lineSeparator))
+                out.append(u'}')
+                return out.value()
+
+        return u''
+
     def do_CSSUnknownRule(self, rule):
         """
         serializes CSSUnknownRule
 
         selectorText = self.do_css_SelectorList(rule.selectorList)
         if not selectorText or not rule.wellformed:
-            return u''
+            return u''        
         self._level += 1
         styleText = u''
         try:
         else:
             return u''
 
-    def do_css_CSSStyleDeclaration(self, style, separator=None):
+    def do_css_CSSStyleDeclaration(self, style, separator=None, omit=True):
         """
         Style declaration of CSSStyleRule
         """
                          or not isinstance(item.value, cssutils.css.Property)]
 
             out = []
+            omitLastSemicolon = omit and self.prefs.omitLastSemicolon
+            
             for i, item in enumerate(seq):
                 typ, val = item.type, item.value
                 if isinstance(val, cssutils.css.CSSComment):
                     # PropertySimilarNameList
                     if val.cssText:
                         out.append(val.cssText)
-                        if not (self.prefs.omitLastSemicolon and i==len(seq)-1):
+                        if not (omitLastSemicolon and i==len(seq)-1):
                             out.append(u';')
                         out.append(separator)
                 elif isinstance(val, cssutils.css.CSSUnknownRule):

File src/tests/test_csspagerule.py

View file
 
     def setUp(self):
         super(CSSPageRuleTestCase, self).setUp()
+        
+        cssutils.ser.prefs.useDefaults()
         self.r = cssutils.css.CSSPageRule()
         self.rRO = cssutils.css.CSSPageRule(readonly=True)
         self.r_type = cssutils.css.CSSPageRule.PAGE_RULE#
         self.r_typeString = 'PAGE_RULE'
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+            
     def test_init(self):
         "CSSPageRule.__init__()"
         super(CSSPageRuleTestCase, self).test_init()

File src/tests/test_cssrule.py

View file
         relf.r_type the type as defined in CSSRule
         """
         super(CSSRuleTestCase, self).setUp()
+        
         self.sheet = cssutils.css.CSSStyleSheet()
         self.r = cssutils.css.CSSRule()
         self.rRO = cssutils.css.CSSRule()
         self.r_type = cssutils.css.CSSRule.UNKNOWN_RULE
         self.r_typeString = 'UNKNOWN_RULE'
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+        
     def test_init(self):
         "CSSRule.type and init"
         self.assertEqual(self.r_type, self.r.type)

File src/tests/test_cssstylesheet.py

View file
         self.s = self.r # used here
         self.rule = cssutils.css.CSSStyleRule()
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+        
     def test_init(self):
         "CSSStyleSheet.__init__()"
         self.assertEqual('text/css', self.s.type)

File src/tests/test_cssunknownrule.py

View file
         self.r_type = cssutils.css.CSSUnknownRule.UNKNOWN_RULE
         self.r_typeString = 'UNKNOWN_RULE'
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+        
     def test_init(self):
         "CSSUnknownRule.type and init"
         super(CSSUnknownRuleTestCase, self).test_init()
             # not normal rules!
             u'@font-facex{}': u'@font-facex {\n    }',
             u'@importurl(x.css);': u'@importurl (x . css);',
-            u'@mediaAll{}': u'@mediaAll {\n    }',
+            u'@mediaAll{}': u'@mediaall {\n    }',
             u'@namespacep"x";': u'@namespacep "x";',
-            u'@pageX{}': u'@pageX {\n    }',
-            u'@bottom { content: counter(page) }': u'@bottom {\n    content: counter(page)\n    }', 
-            u'@bottom { content: "x" counter(page) "y"}': u'@bottom {\n    content: "x" counter(page) "y"\n    }' 
+            u'@pageX{}': u'@pagex {\n    }',
+            u'@xbottom { content: counter(page) }': u'@xbottom {\n    content: counter(page)\n    }', 
+            u'@xbottom { content: "x" counter(page) "y"}': u'@xbottom {\n    content: "x" counter(page) "y"\n    }' 
             }
         self.do_equal_p(tests)
         

File src/tests/test_cssutils.py

View file
 
 class CSSutilsTestCase(basetest.BaseTestCase):
 
+    def setUp(self):
+        cssutils.ser.prefs.useDefaults()
+    
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+        
     exp = u'''@import "import/import2.css";
 .import {
     /* ./import.css */
         self.assertEqual('import/import2.css', ir.href)
         irs = ir.styleSheet
         self.assert_(isinstance(irs, cssutils.css.CSSStyleSheet))
-        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(./../images/example.gif)\n    }'.encode())
+        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(http://example.com/images/example.gif);\n    background: url(//example.com/images/example.gif);\n    background: url(/images/example.gif);\n    background: url(images2/example.gif);\n    background: url(./images2/example.gif);\n    background: url(../images/example.gif);\n    background: url(./../images/example.gif)\n    }'.encode())
 
         tests = {
                  'a {color: red}': u'a {\n    color: red\n    }',
         self.assertEqual('import/import2.css', ir.href)
         irs = ir.styleSheet
         self.assert_(isinstance(irs, cssutils.css.CSSStyleSheet))
-        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(./../images/example.gif)\n    }'.encode())
+        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(http://example.com/images/example.gif);\n    background: url(//example.com/images/example.gif);\n    background: url(/images/example.gif);\n    background: url(images2/example.gif);\n    background: url(./images2/example.gif);\n    background: url(../images/example.gif);\n    background: url(./../images/example.gif)\n    }'.encode())
         
         # name is used for open and setting of href automatically
         # test needs to be relative to this test file!
         self.assertEqual('import/import2.css', ir.href)
         irs = ir.styleSheet
         self.assert_(isinstance(irs, cssutils.css.CSSStyleSheet))
-        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(./../images/example.gif)\n    }'.encode())
+        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(http://example.com/images/example.gif);\n    background: url(//example.com/images/example.gif);\n    background: url(/images/example.gif);\n    background: url(images2/example.gif);\n    background: url(./images2/example.gif);\n    background: url(../images/example.gif);\n    background: url(./../images/example.gif)\n    }'.encode())
         
         # next test
         css = u'a:after { content: "羊蹄€\u2020" }'
         ir = s.cssRules[0]
         self.assertEqual(u'import/import2.css', ir.href)
         irs = ir.styleSheet
-        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(./../images/example.gif)\n    }'.encode())
+        self.assertEqual(irs.cssText, '@import "../import3.css";\n@import "import-impossible.css" print;\n.import2 {\n    /* sheets/import2.css */\n    background: url(http://example.com/images/example.gif);\n    background: url(//example.com/images/example.gif);\n    background: url(/images/example.gif);\n    background: url(images2/example.gif);\n    background: url(./images2/example.gif);\n    background: url(../images/example.gif);\n    background: url(./../images/example.gif)\n    }'.encode())
 
         ir2 = irs.cssRules[0]
         self.assertEqual(u'../import3.css', ir2.href)
         irs2 = ir2.styleSheet
-        self.assertEqual(irs2.cssText, '/* import3 */\n.import3 {\n    /* from ./import/../import3.css */\n    background: url(import/images2/../../images/example3.gif)\n    }'.encode())
+        self.assertEqual(irs2.cssText, '/* import3 */\n.import3 {\n    /* from ./import/../import3.css */\n    background: url(images/example3.gif);\n    background: url(./images/example3.gif);\n    background: url(import/images2/example2.gif);\n    background: url(./import/images2/example2.gif);\n    background: url(import/images2/../../images/example3.gif)\n    }'.encode())
 
     def test_setCSSSerializer(self):
         "cssutils.setSerializer() and cssutils.ser"
     z: url(b/subimg/subimg.gif)
     }'''.encode(), r.cssText)
         
+            cssutils.ser.prefs.useDefaults()
         else:
             self.assertEqual(False, u'Mock needed for this test')
 
-
 if __name__ == '__main__':
     import unittest
     unittest.main()

File src/tests/test_cssvariablesdeclaration.py

View file
     def setUp(self):
         self.r = cssutils.css.CSSVariablesDeclaration()
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+
     def test_init(self):
         "CSSVariablesDeclaration.__init__()"
         v = cssutils.css.CSSVariablesDeclaration()

File src/tests/test_settings.py

View file
 import cssutils.settings
 
 class Settings(test_cssrule.CSSRuleTestCase):
-
-    def tearDown(self):
-        cssutils.ser.prefs.useDefaults()
         
     def test_set(self):
         "settings.set()"
         cssutils.settings.set('DXImageTransform.Microsoft', True)
         self.assertEqual(cssutils.parseString(text).cssText,
                          'a{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=90)}'.encode())
+
+        cssutils.ser.prefs.useDefaults()
         
 
 if __name__ == '__main__':

File src/tests/test_x.py

View file
 class XTestCase(basetest.BaseTestCase):
 
     def setUp(self):
-        pass
+        cssutils.ser.prefs.useDefaults()
 
+    def tearDown(self):
+        cssutils.ser.prefs.useDefaults()
+        
     def test_prioriy(self):
         "Property.priority"
         s = cssutils.parseString(u'a { color: red }')