From 6f4a3efd3cca44630513bf757e6202220952cf49 Mon Sep 17 00:00:00 2001 From: Sascha Peilicke Date: Thu, 23 Jan 2014 12:20:44 +0100 Subject: [PATCH] Support & nesting and (double-)nested media queries Example: .visible { &.visible-sm { @media screen { color: blue; } } } Not the most beautiful code on the planet but does the trick. --- lesscpy/plib/block.py | 50 +++++++++++++++++++++------------- lesscpy/test/css/media.css | 15 ++++++++++ lesscpy/test/css/media.min.css | 3 ++ lesscpy/test/less/media.less | 17 ++++++++++++ 4 files changed, 66 insertions(+), 19 deletions(-) diff --git a/lesscpy/plib/block.py b/lesscpy/plib/block.py index bcbe90d..ccf75cd 100644 --- a/lesscpy/plib/block.py +++ b/lesscpy/plib/block.py @@ -49,7 +49,9 @@ class Block(Node): for p in inner: if p is not None: if isinstance(p, Block): - if p.name.tokens[0] == '@media' and not self.name.raw().startswith("@media"): + if (len(scope) == 2 and p.tokens[1] is not None): + p_is_ampersand = '&' in p.name.tokens[0] + p_is_mediaquery = p.name.tokens[0] == '@media' # Inner block @media ... { ... } is a nested media # query. But double-nested media queries have to be # removed and marked as well. While parsing ".foo", @@ -76,39 +78,49 @@ class Block(Node): reparse_p = False for child in p.tokens[1]: if isinstance(child, Block) and child.name.raw().startswith("@media"): - # Double-nested media query found. We remove it from 'p' and add - # it to this block with a new 'name'. - part_a = p.name.tokens[2:][0][0][0] - part_b = child.name.tokens[2:][0][0] - new_ident_tokens = ['@media', ' ', [part_a, (' ', 'and', ' '), part_b]] # Remove child from the nested media query, it will be re-added to # the parent with 'merged' media query (see above example). p.tokens[1].remove(child) - reparse_p = True - # Parse child again with new @media $BLA {} part - child.tokens[0] = Identifier(new_ident_tokens) - child.parsed = None - child = child.parse(scope) + if p_is_mediaquery: # Media query inside a & block + # Double-nested media query found. We remove it from 'p' and add + # it to this block with a new 'name'. + reparse_p = True + part_a = p.name.tokens[2:][0][0][0] + part_b = child.name.tokens[2:][0][0] + new_ident_tokens = ['@media', ' ', [part_a, (' ', 'and', ' '), part_b]] + # Parse child again with new @media $BLA {} part + child.tokens[0] = Identifier(new_ident_tokens) + child.parsed = None + child = child.parse(scope) + else: + child.block_name = p.name append_list.append(child) - if reparse_p: - p.parsed = None - p = p.parse(scope) - append_list.insert(0, p) # This media query should occur before it's children - for media_query in append_list: - self.inner_media_queries.append(media_query) + if reparse_p: + p.parsed = None + p = p.parse(scope) + if not p_is_mediaquery and not append_list: + self.inner.append(p) + else: + append_list.insert(0, p) # This media query should occur before it's children + for media_query in append_list: + self.inner_media_queries.append(media_query) # NOTE(saschpe): The code is not recursive but we hope that people # wont use triple-nested media queries. else: self.inner.append(p) else: self.parsed.append(p) - if len(self.inner_media_queries) > 0: + if self.inner_media_queries: # Nested media queries, we have to remove self from scope and # push all nested @media ... {} blocks. scope.remove_block(self, index=-2) for mb in self.inner_media_queries: # New inner block with current name and media block contents - cb = Block([self.tokens[0], mb.tokens[1]]).parse(scope) + if hasattr(mb, 'block_name'): + cb_name = mb.block_name + else: + cb_name = self.tokens[0] + cb = Block([cb_name, mb.tokens[1]]).parse(scope) # Replace inner block contents with new block new_mb = Block([mb.tokens[0], [cb]]).parse(scope) self.inner.append(new_mb) diff --git a/lesscpy/test/css/media.css b/lesscpy/test/css/media.css index 7691c87..0c78f71 100644 --- a/lesscpy/test/css/media.css +++ b/lesscpy/test/css/media.css @@ -80,3 +80,18 @@ color: blue; } } +@media print { + .visible { + color: green; + } +} +@media screen { + .visible.visible-sm { + color: green; + } +} +@media (max-width:10px) { + .navbar .form { + margin-bottom: 5px; + } +} diff --git a/lesscpy/test/css/media.min.css b/lesscpy/test/css/media.min.css index 4f01cbb..aa94486 100644 --- a/lesscpy/test/css/media.min.css +++ b/lesscpy/test/css/media.min.css @@ -16,3 +16,6 @@ body{max-width:35em;margin:0 auto;}} .two{width:100px;} @media (width:400px){.two{font-size:1.2em;}} @media print and (color){.two{color:blue;}} +@media print{.visible{color:green;}} +@media screen{.visible.visible-sm{color:green;}} +@media (max-width:10px){.navbar .form{margin-bottom:5px;}} diff --git a/lesscpy/test/less/media.less b/lesscpy/test/less/media.less index 35a7711..3e4c846 100644 --- a/lesscpy/test/less/media.less +++ b/lesscpy/test/less/media.less @@ -95,3 +95,20 @@ } width: 100px; } +.visible { + @media print { + color: green; + } + &.visible-sm { + @media screen { + color: green; + } + } +} +.navbar { + .form { + @media (max-width: 10px) { + margin-bottom: 5px; + } + } +}