From 50fe469e7173c91f9d1f7ff34c8a3013b149f920 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Tue, 24 Jun 2014 00:14:12 +1200 Subject: [PATCH 1/9] Fixed @import for sprite-maps. --HG-- branch : import_sprite extra : amend_source : 827ba5ccd5408ded9faaf329c6d4c52304f96a98 --- scss/__init__.py | 23 +++---- scss/functions/compass/sprites.py | 36 +++++++++- .../compass/utilities/sprites/_base.scss | 68 +++++++++++++++++++ scss/tests/files/kronuz/sprite-import.css | 10 +++ scss/tests/files/kronuz/sprite-import.scss | 3 + scss/tests/test_files.py | 8 +++ scss/types.py | 3 + 7 files changed, 136 insertions(+), 15 deletions(-) create mode 100644 scss/tests/files/kronuz/include/compass/utilities/sprites/_base.scss create mode 100644 scss/tests/files/kronuz/sprite-import.css create mode 100644 scss/tests/files/kronuz/sprite-import.scss diff --git a/scss/__init__.py b/scss/__init__.py index 1a4c749e..16428ebd 100644 --- a/scss/__init__.py +++ b/scss/__init__.py @@ -1021,28 +1021,27 @@ def _at_magic_import(self, calculator, rule, scope, block): Implements @import for sprite-maps Imports magic sprite map directories """ + to_import = block.argument.strip('"') if callable(config.STATIC_ROOT): - files = sorted(config.STATIC_ROOT(block.argument)) + files = sorted(config.STATIC_ROOT(to_import)) else: - glob_path = os.path.join(config.STATIC_ROOT, block.argument) + glob_path = os.path.join(config.STATIC_ROOT, to_import) files = glob.glob(glob_path) files = sorted((file[len(config.STATIC_ROOT):], None) for file in files) if not files: return - # Build magic context - map_name = os.path.normpath(os.path.dirname(block.argument)).replace('\\', '_').replace('/', '_') + # Populate namespace with sprite variables + map_name = os.path.normpath(os.path.dirname(to_import)).replace(os.sep, '_') kwargs = {} def setdefault(var, val): _var = '$' + map_name + '-' + var - if _var in rule.context: - kwargs[var] = calculator.interpolate(rule.context[_var], rule, self._library) - else: - rule.context[_var] = val - kwargs[var] = calculator.interpolate(val, rule, self._library) - return rule.context[_var] + if _var not in rule.namespace.variables: + rule.namespace.set_variable(_var, val) + kwargs[var] = calculator.interpolate(_var) + return rule.namespace.variable(_var) setdefault('sprite-base-class', String('.' + map_name + '-sprite', quotes=None)) setdefault('sprite-dimensions', Boolean(False)) @@ -1054,7 +1053,7 @@ def setdefault(var, val): setdefault(n + '-position', position) setdefault(n + '-spacing', spacing) setdefault(n + '-repeat', repeat) - rule.context['$' + map_name + '-' + 'sprites'] = sprite_map(block.argument, **kwargs) + rule.namespace.set_variable('$' + map_name + '-' + 'sprites', sprite_map(block.argument, **kwargs)) ret = ''' @import "compass/utilities/sprites/base"; @@ -1079,7 +1078,7 @@ def setdefault(var, val): // It will also apply the image dimensions if $dimensions is true. @mixin %(map_name)s-sprite($name, $dimensions: $%(map_name)s-sprite-dimensions, $offset-x: 0, $offset-y: 0) { @extend #{$%(map_name)s-sprite-base-class}; - @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y); + @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y, False); } @mixin %(map_name)s-sprites($sprite-names, $dimensions: $%(map_name)s-sprite-dimensions) { diff --git a/scss/functions/compass/sprites.py b/scss/functions/compass/sprites.py index ca70ccdf..76cf5e31 100644 --- a/scss/functions/compass/sprites.py +++ b/scss/functions/compass/sprites.py @@ -122,7 +122,7 @@ def sprite_map(g, **kwargs): now_time = time.time() globs = String(g, quotes=None).value - globs = sorted(g.strip() for g in globs.split(',')) + globs = sorted(g.strip(' "') for g in globs.split(',')) _k_ = ','.join(globs) @@ -150,7 +150,7 @@ def sprite_map(g, **kwargs): if _files: files.extend(_files) rfiles.extend(_rfiles) - base_name = os.path.normpath(os.path.dirname(_glob)).replace('\\', '_').replace('/', '_') + base_name = os.path.normpath(os.path.dirname(_glob)).replace(os.sep, '_') _map_name, _, _map_type = base_name.partition('.') if _map_type: _map_type += '-' @@ -325,6 +325,7 @@ def images(f=lambda x: x): offsets_x = [] offsets_y = [] + selectors = [] for i, image in enumerate(images()): x, y, width, height, cssx, cssy, cssw, cssh = layout_positions[i] iwidth, iheight = image.size @@ -358,6 +359,13 @@ def images(f=lambda x: x): offsets_x.append(cssx) offsets_y.append(cssy) + # extracting selector for compass spriting's magic selectors + # http://compass-style.org/help/tutorials/spriting/magic-selectors/ + name = os.path.splitext(os.path.basename(image.filename))[0] + spl = name.split('_') + selector = spl[-1] if len(spl) > 1 else None + selectors.append(selector) + if useless_dst_color: log.warning("Useless use of $dst-color in sprite map for files at '%s' (never used for)" % glob_path) @@ -387,7 +395,7 @@ def images(f=lambda x: x): asset = file_asset = List([String.unquoted(url), String.unquoted(repeat)]) # Add the new object: - sprite_map = dict(zip(tnames, zip(sizes, rfiles, offsets_x, offsets_y))) + sprite_map = dict(zip(tnames, zip(sizes, rfiles, offsets_x, offsets_y, selectors))) sprite_map['*'] = now_time sprite_map['*f*'] = asset_file sprite_map['*k*'] = key @@ -398,6 +406,9 @@ def images(f=lambda x: x): cache_tmp = tempfile.NamedTemporaryFile(delete=False, dir=ASSETS_ROOT) pickle.dump((now_time, file_asset, inline_asset, sprite_map, sizes), cache_tmp) cache_tmp.close() + if os.path.exists(cache_path): + # renaming when replacing an existing file causes an error on windows + os.remove(cache_path) os.rename(cache_tmp.name, cache_path) # Use the sorted list to remove older elements (keep only 500 objects): @@ -559,3 +570,22 @@ def sprite_position(map, sprite, offset_x=None, offset_y=None): y -= Number(sprite[3], 'px') return List([x, y]) return List([Number(0), Number(0)]) + + +@register('sprite-does-not-have-parent', 2) +def sprite_does_not_have_parent(map, sprite): + map = map.render() + sprite_map = sprite_maps.get(map) + sprite_name = String.unquoted(sprite).value + sprite = sprite_map and sprite_map.get(sprite_name) + # if there is no selector, the sprite does not have any parents + return Boolean(not sprite[4]) + + +@register('sprite-has-selector', 3) +def sprite_has_selector(map, sprite, selector): + map = map.render() + sprite_map = sprite_maps.get(map) + sprite_name = String.unquoted(sprite).value + sprite = sprite_map and sprite_map.get(sprite_name + '_' + selector.value) + return Boolean(sprite) diff --git a/scss/tests/files/kronuz/include/compass/utilities/sprites/_base.scss b/scss/tests/files/kronuz/include/compass/utilities/sprites/_base.scss new file mode 100644 index 00000000..3a94e686 --- /dev/null +++ b/scss/tests/files/kronuz/include/compass/utilities/sprites/_base.scss @@ -0,0 +1,68 @@ +// This file comes from compass (compass-style.org) and is only used in sprite-import test + +// Determines those states for which you want to enable magic sprite selectors +$sprite-selectors: hover, target, active !default; + +// Set the width and height of an element to the original +// dimensions of an image before it was included in the sprite. +@mixin sprite-dimensions($map, $sprite) { + height: image-height(sprite-file($map, $sprite)); + width: image-width(sprite-file($map, $sprite)); +} + +// Set the background position of the given sprite `$map` to display the +// sprite of the given `$sprite` name. You can move the image relative to its +// natural position by passing `$offset-x` and `$offset-y`. +@mixin sprite-background-position($map, $sprite, $offset-x: 0, $offset-y: 0) { + background-position: sprite-position($map, $sprite, $offset-x, $offset-y); +} + + +// Determines if you want to include magic selectors in your sprites +$disable-magic-sprite-selectors:false !default; + +// Include the position and (optionally) dimensions of this `$sprite` +// in the given sprite `$map`. The sprite url should come from either a base +// class or you can specify the `sprite-url` explicitly like this: +// +// background: $map no-repeat; +@mixin sprite($map, $sprite, $dimensions: false, $offset-x: 0, $offset-y: 0) { + @include sprite-background-position($map, $sprite, $offset-x, $offset-y); + @if $dimensions { + @include sprite-dimensions($map, $sprite); + } + @if not $disable-magic-sprite-selectors { + @include sprite-selectors($map, $sprite, $sprite, $offset-x, $offset-y); + } +} + +// Include the selectors for the `$sprite` given the `$map` and the +// `$full-sprite-name` +// @private +@mixin sprite-selectors($map, $sprite-name, $full-sprite-name, $offset-x: 0, $offset-y: 0) { + @each $selector in $sprite-selectors { + @if sprite_has_selector($map, $sprite-name, $selector) { + &:#{$selector}, &.#{$full-sprite-name}_#{$selector}, &.#{$full-sprite-name}-#{$selector} { + @include sprite-background-position($map, "#{$sprite-name}_#{$selector}", $offset-x, $offset-y); + } + } + } +} + +// Generates a class for each space separated name in `$sprite-names`. +// The class will be of the form .-. +// +// If a base class is provided, then each class will extend it. +// +// If `$dimensions` is `true`, the sprite dimensions will specified. +@mixin sprites($map, $sprite-names, $base-class: false, $dimensions: false, $prefix: sprite-map-name($map), $offset-x: 0, $offset-y: 0) { + @each $sprite-name in $sprite-names { + @if sprite_does_not_have_parent($map, $sprite-name) { + $full-sprite-name: "#{$prefix}-#{$sprite-name}"; + .#{$full-sprite-name} { + @if $base-class { @extend #{$base-class}; } + @include sprite($map, $sprite-name, $dimensions, $offset-x, $offset-y); + } + } + } +} \ No newline at end of file diff --git a/scss/tests/files/kronuz/sprite-import.css b/scss/tests/files/kronuz/sprite-import.css new file mode 100644 index 00000000..957b6c35 --- /dev/null +++ b/scss/tests/files/kronuz/sprite-import.css @@ -0,0 +1,10 @@ +.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty { + background: url(static/assets/squares-3YSC5aV_o5mClGk1bBuyjg.png) no-repeat; +} +.squares-ten-by-ten { + background-position: 0px 0px; +} + +.squares-twenty-by-twenty { + background-position: 0px -10px; +} diff --git a/scss/tests/files/kronuz/sprite-import.scss b/scss/tests/files/kronuz/sprite-import.scss new file mode 100644 index 00000000..282026fc --- /dev/null +++ b/scss/tests/files/kronuz/sprite-import.scss @@ -0,0 +1,3 @@ +@import "squares/*.png"; + +@include all-squares-sprites; diff --git a/scss/tests/test_files.py b/scss/tests/test_files.py index 972e57cf..65135318 100644 --- a/scss/tests/test_files.py +++ b/scss/tests/test_files.py @@ -25,6 +25,14 @@ def test_pair_programmatic(scss_file_pair): scss_fn, css_fn = scss_file_pair + if scss_fn.endswith('sprite-import.scss'): + # disable cache_buster on sprite_map + sprite_map_0 = scss.sprite_map + def sprite_map_patch(g, **kwargs): + kwargs.setdefault('cache_buster', scss.types.Boolean(False)) + return sprite_map_0(g, **kwargs) + scss.sprite_map = sprite_map_patch + with open(scss_fn) as fh: source = fh.read() try: diff --git a/scss/types.py b/scss/types.py index c88dc04b..f893be84 100644 --- a/scss/types.py +++ b/scss/types.py @@ -275,6 +275,9 @@ def __repr__(self): def __hash__(self): return hash((self.value, self.unit_numer, self.unit_denom)) + def __nonzero__(self): + return bool(self.value) + def __int__(self): return int(self.value) From 43c43eac8b18b71e888ec985f957e77494537459 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Tue, 24 Jun 2014 12:24:52 +1200 Subject: [PATCH 2/9] Cleaner approach for test_pair_programmatic pre-config script --HG-- branch : import_sprite --- scss/tests/files/kronuz/sprite-import.py | 14 ++++++++++++++ scss/tests/test_files.py | 11 ++++------- 2 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 scss/tests/files/kronuz/sprite-import.py diff --git a/scss/tests/files/kronuz/sprite-import.py b/scss/tests/files/kronuz/sprite-import.py new file mode 100644 index 00000000..3667b013 --- /dev/null +++ b/scss/tests/files/kronuz/sprite-import.py @@ -0,0 +1,14 @@ +""" +Disables cache_buster on sprite_map for sprite_import test +""" + +import scss + +sprite_map_0 = scss.sprite_map + +def sprite_map_patch(g, **kwargs): + global sprite_map_0 + kwargs.setdefault('cache_buster', scss.types.Boolean(False)) + return sprite_map_0(g, **kwargs) + +scss.sprite_map = sprite_map_patch diff --git a/scss/tests/test_files.py b/scss/tests/test_files.py index 65135318..d8423e24 100644 --- a/scss/tests/test_files.py +++ b/scss/tests/test_files.py @@ -25,13 +25,10 @@ def test_pair_programmatic(scss_file_pair): scss_fn, css_fn = scss_file_pair - if scss_fn.endswith('sprite-import.scss'): - # disable cache_buster on sprite_map - sprite_map_0 = scss.sprite_map - def sprite_map_patch(g, **kwargs): - kwargs.setdefault('cache_buster', scss.types.Boolean(False)) - return sprite_map_0(g, **kwargs) - scss.sprite_map = sprite_map_patch + # look for a config script related to the pair and execute it if found + cfg_script = scss_fn.replace('.scss', '.py') + if os.path.exists(cfg_script): + execfile(cfg_script) with open(scss_fn) as fh: source = fh.read() From 083dbc98509f9ffe9ecadf4633cc3f61d146a544 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Sun, 29 Jun 2014 18:25:55 +1200 Subject: [PATCH 3/9] Switched to module import for test_pair_programmatic setup and teardown functions --HG-- branch : import_sprite --- scss/tests/files/kronuz/sprite-import.py | 8 +++++++- scss/tests/test_files.py | 12 ++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/scss/tests/files/kronuz/sprite-import.py b/scss/tests/files/kronuz/sprite-import.py index 3667b013..c4257122 100644 --- a/scss/tests/files/kronuz/sprite-import.py +++ b/scss/tests/files/kronuz/sprite-import.py @@ -11,4 +11,10 @@ def sprite_map_patch(g, **kwargs): kwargs.setdefault('cache_buster', scss.types.Boolean(False)) return sprite_map_0(g, **kwargs) -scss.sprite_map = sprite_map_patch + +def setUp(): + scss.sprite_map = sprite_map_patch + + +def tearDown(): + scss.sprite_map = sprite_map_0 diff --git a/scss/tests/test_files.py b/scss/tests/test_files.py index d8423e24..0141010f 100644 --- a/scss/tests/test_files.py +++ b/scss/tests/test_files.py @@ -12,6 +12,8 @@ import os.path import logging +import sys +from importlib import import_module import scss @@ -25,10 +27,14 @@ def test_pair_programmatic(scss_file_pair): scss_fn, css_fn = scss_file_pair - # look for a config script related to the pair and execute it if found + # look for a python module related to the pair and execute it if found + mod = None cfg_script = scss_fn.replace('.scss', '.py') if os.path.exists(cfg_script): - execfile(cfg_script) + sys.path[0:0] = [os.path.dirname(scss_fn)] + mod = import_module(os.path.splitext(os.path.split(scss_fn)[1])[0]) + getattr(mod, 'setUp', lambda: None)() + sys.path = sys.path[1:] with open(scss_fn) as fh: source = fh.read() @@ -45,6 +51,8 @@ def test_pair_programmatic(scss_file_pair): compiler = scss.Scss(scss_opts=dict(style='expanded'), search_paths=[include_dir, directory]) actual = compiler.compile(source) + getattr(mod, 'tearDown', lambda:None)() + # Normalize leading and trailing newlines actual = actual.strip('\n') expected = expected.strip('\n') From ae157ac0f026c6ec5935dae7edb962d82ef32c01 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Mon, 14 Jul 2014 21:55:12 +1200 Subject: [PATCH 4/9] Fixed args issues with magic import's mixins --HG-- branch : import_sprite --- scss/__init__.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/scss/__init__.py b/scss/__init__.py index 16428ebd..4140e940 100644 --- a/scss/__init__.py +++ b/scss/__init__.py @@ -1075,19 +1075,18 @@ def setdefault(var, val): } // Extends the sprite base class and set the background position for the desired sprite. - // It will also apply the image dimensions if $dimensions is true. - @mixin %(map_name)s-sprite($name, $dimensions: $%(map_name)s-sprite-dimensions, $offset-x: 0, $offset-y: 0) { + @mixin %(map_name)s-sprite($name, $offset-x: 0, $offset-y: 0) { @extend #{$%(map_name)s-sprite-base-class}; - @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y, False); + @include sprite($%(map_name)s-sprites, $name, $offset-x, $offset-y, False); } - @mixin %(map_name)s-sprites($sprite-names, $dimensions: $%(map_name)s-sprite-dimensions) { - @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class, $dimensions); + @mixin %(map_name)s-sprites($sprite-names) { + @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class); } // Generates a class for each sprited image. - @mixin all-%(map_name)s-sprites($dimensions: $%(map_name)s-sprite-dimensions) { - @include %(map_name)s-sprites(%(sprites)s, $dimensions); + @mixin all-%(map_name)s-sprites() { + @include %(map_name)s-sprites(%(sprites)s); } ''' % {'map_name': map_name, 'sprites': ' '.join(names)} return ret From c6f2c557f8adcd8599f922d017f6ea27e294922a Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Tue, 15 Jul 2014 21:25:58 +1200 Subject: [PATCH 5/9] Reversed previous commit 'Fixed args issue ...'. and properly fixed the issue that was introduced by commit 6c00584a0dab --HG-- branch : import_sprite --- scss/__init__.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/scss/__init__.py b/scss/__init__.py index 4140e940..7f84335f 100644 --- a/scss/__init__.py +++ b/scss/__init__.py @@ -1075,18 +1075,19 @@ def setdefault(var, val): } // Extends the sprite base class and set the background position for the desired sprite. - @mixin %(map_name)s-sprite($name, $offset-x: 0, $offset-y: 0) { + // It will also apply the image dimensions if $dimensions is true. + @mixin %(map_name)s-sprite($name, $dimensions: $%(map_name)s-sprite-dimensions, $offset-x: 0, $offset-y: 0) { @extend #{$%(map_name)s-sprite-base-class}; - @include sprite($%(map_name)s-sprites, $name, $offset-x, $offset-y, False); + @include sprite($%(map_name)s-sprites, $name, $dimensions, $offset-x, $offset-y); } - @mixin %(map_name)s-sprites($sprite-names) { - @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class); + @mixin %(map_name)s-sprites($sprite-names, $dimensions: $%(map_name)s-sprite-dimensions) { + @include sprites($%(map_name)s-sprites, $sprite-names, $%(map_name)s-sprite-base-class, $dimensions); } // Generates a class for each sprited image. - @mixin all-%(map_name)s-sprites() { - @include %(map_name)s-sprites(%(sprites)s); + @mixin all-%(map_name)s-sprites($dimensions: $%(map_name)s-sprite-dimensions) { + @include %(map_name)s-sprites(%(sprites)s, $dimensions); } ''' % {'map_name': map_name, 'sprites': ' '.join(names)} return ret From c0e687a26b40288e41b8f61f5fafe18c1ae4202b Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Wed, 6 Aug 2014 15:22:52 +1200 Subject: [PATCH 6/9] Adds support for $-layout variable --- scss/__init__.py | 1 + scss/functions/compass/sprites.py | 6 ++++-- scss/tests/files/kronuz/sprite-import.css | 6 +++--- scss/tests/files/kronuz/sprite-import.scss | 1 + 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/scss/__init__.py b/scss/__init__.py index 7f84335f..d3fb6c39 100644 --- a/scss/__init__.py +++ b/scss/__init__.py @@ -1045,6 +1045,7 @@ def setdefault(var, val): setdefault('sprite-base-class', String('.' + map_name + '-sprite', quotes=None)) setdefault('sprite-dimensions', Boolean(False)) + setdefault('layout', String('vertical')) position = setdefault('position', Number(0, '%')) spacing = setdefault('spacing', Number(0)) repeat = setdefault('repeat', String('no-repeat', quotes=None)) diff --git a/scss/functions/compass/sprites.py b/scss/functions/compass/sprites.py index 76cf5e31..9b32bbce 100644 --- a/scss/functions/compass/sprites.py +++ b/scss/functions/compass/sprites.py @@ -98,7 +98,7 @@ def sprite_map(g, **kwargs): Generates a sprite map from the files matching the glob pattern. Uses the keyword-style arguments passed in to control the placement. - $direction - Sprite map layout. Can be `vertical` (default), `horizontal`, `diagonal` or `smart`. + $direction, $layout - Sprite map layout. Can be `vertical` (default), `horizontal`, `diagonal` or `smart`. $position - For `horizontal` and `vertical` directions, the position of the sprite. (defaults to `0`) $-position - Position of a given sprite. @@ -203,7 +203,9 @@ def sprite_map(g, **kwargs): if sprite_map is None or asset is None: cache_buster = Boolean(kwargs.get('cache_buster', True)) - direction = String.unquoted(kwargs.get('direction', config.SPRTE_MAP_DIRECTION)).value + direction = String.unquoted(kwargs.get('direction', + kwargs.get('layout', + config.SPRTE_MAP_DIRECTION))).value repeat = String.unquoted(kwargs.get('repeat', 'no-repeat')).value collapse = kwargs.get('collapse', Number(0)) if isinstance(collapse, List): diff --git a/scss/tests/files/kronuz/sprite-import.css b/scss/tests/files/kronuz/sprite-import.css index 957b6c35..8b0101c6 100644 --- a/scss/tests/files/kronuz/sprite-import.css +++ b/scss/tests/files/kronuz/sprite-import.css @@ -1,10 +1,10 @@ -.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty { - background: url(static/assets/squares-3YSC5aV_o5mClGk1bBuyjg.png) no-repeat; +.squares-sprite, .squares-ten-by-ten, .squares-twenty-by-twenty { + background: url(static/assets/squares-1FgKVnsRrRsnYnidUFyJwA.png) no-repeat; } .squares-ten-by-ten { background-position: 0px 0px; } .squares-twenty-by-twenty { - background-position: 0px -10px; + background-position: -10px 0px; } diff --git a/scss/tests/files/kronuz/sprite-import.scss b/scss/tests/files/kronuz/sprite-import.scss index 282026fc..52717d7b 100644 --- a/scss/tests/files/kronuz/sprite-import.scss +++ b/scss/tests/files/kronuz/sprite-import.scss @@ -1,3 +1,4 @@ +$squares-layout:horizontal; @import "squares/*.png"; @include all-squares-sprites; From 756d4f7d94089da904394d189a3dc00cb46f02a6 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Wed, 6 Aug 2014 15:50:25 +1200 Subject: [PATCH 7/9] Gets test_pair_programmatic to clean after itself (assets) So that one doesn't have to clear the assets dirs between test runs --- scss/tests/test_files.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/scss/tests/test_files.py b/scss/tests/test_files.py index 0141010f..ec77793c 100644 --- a/scss/tests/test_files.py +++ b/scss/tests/test_files.py @@ -10,7 +10,7 @@ from __future__ import absolute_import -import os.path +import os import logging import sys from importlib import import_module @@ -48,13 +48,22 @@ def test_pair_programmatic(scss_file_pair): include_dir = os.path.join(directory, 'include') scss.config.STATIC_ROOT = os.path.join(directory, 'static') - compiler = scss.Scss(scss_opts=dict(style='expanded'), search_paths=[include_dir, directory]) - actual = compiler.compile(source) + try: + compiler = scss.Scss(scss_opts=dict(style='expanded'), search_paths=[include_dir, directory]) + actual = compiler.compile(source) + + getattr(mod, 'tearDown', lambda:None)() - getattr(mod, 'tearDown', lambda:None)() + # Normalize leading and trailing newlines + actual = actual.strip('\n') + expected = expected.strip('\n') - # Normalize leading and trailing newlines - actual = actual.strip('\n') - expected = expected.strip('\n') + assert expected == actual - assert expected == actual + finally: + # cleanup generated assets if any + assets_dir = os.path.join(directory, 'static', 'assets') + if os.path.isdir(assets_dir): + for x in os.listdir(assets_dir): + if x != '.placeholder': + os.remove(os.path.join(assets_dir, x)) From 18aae0e0a043534bf1524c58bf0ce8d4f3af571e Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Mon, 25 Aug 2014 16:54:46 +1200 Subject: [PATCH 8/9] Fix bytestring in make_filename_hash (py3 compat) --- scss/util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scss/util.py b/scss/util.py index 9c1a8690..481a0c0c 100644 --- a/scss/util.py +++ b/scss/util.py @@ -113,7 +113,7 @@ def make_filename_hash(key): # the expected test results if sys.platform == 'win32': # this is to make sure the hash is the same on win and unix platforms - key_repr = key_repr.replace('\\\\', '/') + key_repr = key_repr.replace(b'\\\\', b'/') key_repr = re.sub(b"\\bu'", b"'", key_repr) key_hash = hashlib.md5(key_repr).digest() return base64.b64encode(key_hash, b'__').decode('ascii').rstrip('=') From 23830c896a7c19efbbc8859f8461cf808ef8b462 Mon Sep 17 00:00:00 2001 From: Thomas Khyn Date: Thu, 16 Oct 2014 17:21:53 +1300 Subject: [PATCH 9/9] Adds support for use-percentages --- scss/functions/compass/sprites.py | 75 ++++++++++++------- .../files/kronuz/sprite-pos-percentage.css | 11 +++ .../files/kronuz/sprite-pos-percentage.scss | 16 ++++ 3 files changed, 74 insertions(+), 28 deletions(-) create mode 100644 scss/tests/files/kronuz/sprite-pos-percentage.css create mode 100644 scss/tests/files/kronuz/sprite-pos-percentage.scss diff --git a/scss/functions/compass/sprites.py b/scss/functions/compass/sprites.py index 18f14400..b62b7a44 100644 --- a/scss/functions/compass/sprites.py +++ b/scss/functions/compass/sprites.py @@ -410,6 +410,7 @@ def images(f=lambda x: x): sprite_map['*k*'] = key sprite_map['*n*'] = map_name sprite_map['*t*'] = filetime + sprite_map['*s*'] = new_image.size sizes = zip(files, sizes) cache_tmp = tempfile.NamedTemporaryFile(delete=False, dir=ASSETS_ROOT) @@ -485,7 +486,9 @@ def sprite_classes(map): @register('sprite', 3) @register('sprite', 4) @register('sprite', 5) -def sprite(map, sprite, offset_x=None, offset_y=None, cache_buster=True): +@register('sprite', 6) +def sprite(map, sprite, offset_x=None, offset_y=None, use_percentages=False, + cache_buster=True): """ Returns the image and background position for use in a single shorthand property @@ -502,14 +505,20 @@ def sprite(map, sprite, offset_x=None, offset_y=None, cache_buster=True): url = '%s%s' % (config.ASSETS_URL, sprite_map['*f*']) if cache_buster: url += '?_=%s' % sprite_map['*t*'] - x = Number(offset_x or 0, 'px') - y = Number(offset_y or 0, 'px') - if not x.value or (x.value <= -1 or x.value >= 1) and not x.is_simple_unit('%'): - x -= Number(sprite[2], 'px') - if not y.value or (y.value <= -1 or y.value >= 1) and not y.is_simple_unit('%'): - y -= Number(sprite[3], 'px') + unit = '%' if use_percentages else 'px' + coors = [Number(offset_x or 0, unit), Number(offset_y or 0, unit)] + map_size = sprite_map['*s*'] + for i, coor in enumerate(coors): + if not coor.value or (coor.value <= -1 or coor.value >= 1) \ + and (coor.is_simple_unit('%') and unit == '%' or + coor.is_simple_unit('px') and unit == 'px'): + value = sprite[i + 2] + if use_percentages and value: + value = -100.0 * value / (map_size[i] - sprite[0][i]) + coors[i] -= Number(value, unit) + url = "url(%s)" % escape(url) - return List([String.unquoted(url), x, y]) + return List([String.unquoted(url)] + coors) return List([Number(0), Number(0)]) @@ -546,7 +555,9 @@ def has_sprite(map, sprite): @register('sprite-position', 2) @register('sprite-position', 3) @register('sprite-position', 4) -def sprite_position(map, sprite, offset_x=None, offset_y=None): +@register('sprite-position', 5) +def sprite_position(map, sprite, offset_x=None, offset_y=None, + use_percentages=False): """ Returns the position for the original image in the sprite. This is suitable for use as a value to background-position. @@ -560,25 +571,33 @@ def sprite_position(map, sprite, offset_x=None, offset_y=None): elif not sprite: log.error("No sprite found: %s in %s", sprite_name, sprite_map['*n*'], extra={'stack': True}) if sprite: - x = None - if offset_x is not None and not isinstance(offset_x, Number): - x = offset_x - if not x or x.value not in ('left', 'right', 'center'): - if x: - offset_x = None - x = Number(offset_x or 0, 'px') - if not x.value or (x.value <= -1 or x.value >= 1) and not x.is_simple_unit('%'): - x -= Number(sprite[2], 'px') - y = None - if offset_y is not None and not isinstance(offset_y, Number): - y = offset_y - if not y or y.value not in ('top', 'bottom', 'center'): - if y: - offset_y = None - y = Number(offset_y or 0, 'px') - if not y.value or (y.value <= -1 or y.value >= 1) and not y.is_simple_unit('%'): - y -= Number(sprite[3], 'px') - return List([x, y]) + unit = '%' if use_percentages else 'px' + map_size = sprite_map['*s*'] + + coors = [offset_x, offset_y] + positions = (('left', 'right', 'center'), ('top', 'bottom', 'center')) + + for i, coor in enumerate(coors): + c = None + if coor is not None and not isinstance(coor, Number): + c = coor + if not c or c.value not in positions[i]: + if c: + coor = None + c = Number(offset_x or 0, unit) + if not c.value or (c.value <= -1 or c.value >= 1) \ + and (c.is_simple_unit('%') and unit == '%' or + c.is_simple_unit('px') and unit == 'px'): + value = sprite[i + 2] + if use_percentages and value: + value = -100.0 * value / (map_size[i] - sprite[0][i]) + coors[i] = c - Number(value, unit) + else: + coors[i] = c + else: + coors[i] = None + + return List(coors) return List([Number(0), Number(0)]) diff --git a/scss/tests/files/kronuz/sprite-pos-percentage.css b/scss/tests/files/kronuz/sprite-pos-percentage.css new file mode 100644 index 00000000..02fad7d6 --- /dev/null +++ b/scss/tests/files/kronuz/sprite-pos-percentage.css @@ -0,0 +1,11 @@ +.mod { + background: url(static/assets/squares-nafpKaM4RlNNZcIbS0wv_g.png) no-repeat; +} +.mod.ten-by-ten { + background: url(static/assets/squares-nafpKaM4RlNNZcIbS0wv_g.png) 0% 0%; +} +.mod.twenty-by-twenty { + width: 20px; + height: 20px; + background-position: 0% 100%; +} diff --git a/scss/tests/files/kronuz/sprite-pos-percentage.scss b/scss/tests/files/kronuz/sprite-pos-percentage.scss new file mode 100644 index 00000000..b9968aa8 --- /dev/null +++ b/scss/tests/files/kronuz/sprite-pos-percentage.scss @@ -0,0 +1,16 @@ +@option style:legacy; + +$images: sprite-map("squares/*.png", $cache-buster: false); + +.mod { + background: $images; + &.ten-by-ten { + background: sprite($images, "ten-by-ten", $use-percentages: true, $cache-buster: false); + } + &.twenty-by-twenty { + $file: sprite-file($images, "twenty-by-twenty"); + width: image-width($file); + height: image-height($file); + background-position: sprite-position($images, "twenty-by-twenty", $use-percentages: true); + } +}