コード例 #1
0
ファイル: sprites.py プロジェクト: 571451370/devstack_mitaka
def sprite(map, sprite, offset_x=None, offset_y=None, cache_buster=True):
    """
    Returns the image and background position for use in a single shorthand
    property
    """
    map = map.render()
    sprite_maps = _get_cache('sprite_maps')
    sprite_map = sprite_maps.get(map)
    sprite_name = String.unquoted(sprite).value
    sprite = sprite_map and sprite_map.get(sprite_name)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    elif not sprite:
        log.error("No sprite found: %s in %s",
                  sprite_name,
                  sprite_map['*n*'],
                  extra={'stack': True})
    if sprite:
        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')
        url = "url(%s)" % escape(url)
        return List([String.unquoted(url), x, y])
    return List([Number(0), Number(0)])
コード例 #2
0
ファイル: helpers.py プロジェクト: 4teamwork/pyScss
def stylesheet_url(path, only_path=False, cache_buster=True):
    """
    Generates a path to an asset found relative to the project's css directory.
    Passing a true value as the second argument will cause the only the path to
    be returned instead of a `url()` function
    """
    filepath = String.unquoted(path).value
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
        except IndexError:
            filetime = None
        else:
            filetime = getmtime(_file, _storage)
        if filetime is None:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        filetime = getmtime(_path)
        if filetime is None:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL

    url = '%s%s' % (BASE_URL, filepath)
    if cache_buster:
        url = add_cache_buster(url, filetime)
    if only_path:
        return String.unquoted(url)
    else:
        return Url.unquoted(url)
コード例 #3
0
ファイル: helpers.py プロジェクト: return42/pyScss
def enumerate_(prefix, frm, through, separator='-'):
    separator = String.unquoted(separator).value
    try:
        frm = int(getattr(frm, 'value', frm))
    except ValueError:
        frm = 1
    try:
        through = int(getattr(through, 'value', through))
    except ValueError:
        through = frm
    if frm > through:
        # DEVIATION: allow reversed enumerations (and ranges as range() uses enumerate, like '@for .. from .. through')
        frm, through = through, frm
        rev = reversed
    else:
        rev = lambda x: x

    ret = []
    for i in rev(range(frm, through + 1)):
        if prefix and prefix.value:
            ret.append(
                String.unquoted(prefix.value + separator + six.text_type(i)))
        else:
            ret.append(Number(i))

    return List(ret, use_comma=True)
コード例 #4
0
ファイル: helpers.py プロジェクト: return42/pyScss
def _font_files(args, inline):
    if args == ():
        return String.unquoted("")

    fonts = []
    args_len = len(args)
    skip_next = False
    for index, arg in enumerate(args):
        if not skip_next:
            font_type = args[index + 1] if args_len > (index + 1) else None
            if font_type and font_type.value in FONT_TYPES:
                skip_next = True
            else:
                if re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value):
                    font_type = String.unquoted(
                        re.match(r'^([^?]+)[.](.*)([?].*)?$',
                                 arg.value).groups()[1])

            if font_type.value in FONT_TYPES:
                fonts.append(
                    List([
                        _font_url(arg, inline=inline),
                        Function(FONT_TYPES[font_type.value], 'format'),
                    ],
                         use_comma=False))
            else:
                raise Exception('Could not determine font type for "%s"' %
                                arg.value)
        else:
            skip_next = False

    return List(fonts, separator=',')
コード例 #5
0
ファイル: helpers.py プロジェクト: Foued123/qooxdoo
def enumerate_(prefix, frm, through, separator='-'):
    separator = String.unquoted(separator).value
    try:
        frm = int(getattr(frm, 'value', frm))
    except ValueError:
        frm = 1
    try:
        through = int(getattr(through, 'value', through))
    except ValueError:
        through = frm
    if frm > through:
        # DEVIATION: allow reversed enumerations (and ranges as range() uses enumerate, like '@for .. from .. through')
        frm, through = through, frm
        rev = reversed
    else:
        rev = lambda x: x

    ret = []
    for i in rev(range(frm, through + 1)):
        if prefix and prefix.value:
            ret.append(String.unquoted(prefix.value + separator + str(i)))
        else:
            ret.append(Number(i))

    return List(ret, use_comma=True)
コード例 #6
0
ファイル: helpers.py プロジェクト: Foued123/qooxdoo
def _font_files(args, inline):
    if args == ():
        return String.unquoted("")

    fonts = []
    args_len = len(args)
    skip_next = False
    for index in range(len(args)):
        arg = args[index]
        if not skip_next:
            font_type = args[index + 1] if args_len > (index + 1) else None
            if font_type and font_type.value in FONT_TYPES:
                skip_next = True
            else:
                if re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value):
                    font_type = String.unquoted(re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value).groups()[1])

            if font_type.value in FONT_TYPES:
                fonts.append(String.unquoted('%s format("%s")' % (_font_url(arg, inline=inline), String.unquoted(FONT_TYPES[font_type.value]).value)))
            else:
                raise Exception('Could not determine font type for "%s"' % arg.value)
        else:
            skip_next = False

    return List(fonts, separator=',')
コード例 #7
0
ファイル: helpers.py プロジェクト: return42/pyScss
def stylesheet_url(path, only_path=False, cache_buster=True):
    """
    Generates a path to an asset found relative to the project's css directory.
    Passing a true value as the second argument will cause the only the path to
    be returned instead of a `url()` function
    """
    filepath = String.unquoted(path).value
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
        except IndexError:
            filetime = None
        else:
            filetime = getmtime(_file, _storage)
        if filetime is None:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        filetime = getmtime(_path)
        if filetime is None:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL

    url = '%s%s' % (BASE_URL, filepath)
    if cache_buster:
        url = add_cache_buster(url, filetime)
    if only_path:
        return String.unquoted(url)
    else:
        return Url.unquoted(url)
コード例 #8
0
ファイル: helpers.py プロジェクト: lamby/31bananas
def _font_files(args, inline):
    if args == ():
        return String.unquoted("")

    fonts = []
    args_len = len(args)
    skip_next = False
    for index in range(len(args)):
        arg = args[index]
        if not skip_next:
            font_type = args[index + 1] if args_len > (index + 1) else None
            if font_type and font_type.value in FONT_TYPES:
                skip_next = True
            else:
                if re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value):
                    font_type = String.unquoted(
                        re.match(r'^([^?]+)[.](.*)([?].*)?$',
                                 arg.value).groups()[1])

            if font_type.value in FONT_TYPES:
                fonts.append(
                    String.unquoted(
                        '%s format("%s")' %
                        (_font_url(arg, inline=inline),
                         String.unquoted(FONT_TYPES[font_type.value]).value)))
            else:
                raise Exception('Could not determine font type for "%s"' %
                                arg.value)
        else:
            skip_next = False

    return List(fonts, separator=',')
コード例 #9
0
ファイル: helpers.py プロジェクト: Foued123/qooxdoo
def stylesheet_url(path, only_path=False, cache_buster=True):
    """
    Generates a path to an asset found relative to the project's css directory.
    Passing a true value as the second argument will cause the only the path to
    be returned instead of a `url()` function
    """
    filepath = String.unquoted(path).value
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
        else:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL

    url = '%s%s' % (BASE_URL, filepath)
    if cache_buster:
        url = add_cache_buster(url, filetime)
    if not only_path:
        url = 'url("%s")' % (url)
    return String.unquoted(url)
コード例 #10
0
ファイル: helpers.py プロジェクト: hblanks/pyScss
def _font_url(path, only_path=False, cache_buster=True, inline=False):
    filepath = String.unquoted(path).value
    path = None
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
            if inline:
                path = _storage.open(_file)
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
            if inline:
                path = open(_path, 'rb')
        else:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL

    if path and inline:
        mime_type = mimetypes.guess_type(filepath)[0]
        url = 'data:' + mime_type + ';base64,' + base64.b64encode(path.read())
    else:
        url = '%s%s' % (BASE_URL, filepath)
        if cache_buster:
            url = add_cache_buster(url, filetime)

    if not only_path:
        url = 'url("%s")' % escape(url)
    return String.unquoted(url)
コード例 #11
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def sprite(map, sprite, offset_x=None, offset_y=None, cache_buster=True):
    """
    Returns the image and background position for use in a single shorthand
    property
    """
    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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    elif not sprite:
        log.error("No sprite found: %s in %s", sprite_name, sprite_map['*n*'], extra={'stack': True})
    if sprite:
        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')
        url = "url(%s)" % escape(url)
        return List([String.unquoted(url), x, y])
    return List([Number(0), Number(0)])
コード例 #12
0
def stylesheet_url(path, only_path=False, cache_buster=True):
    """
    Generates a path to an asset found relative to the project's css directory.
    Passing a true value as the second argument will cause the only the path to
    be returned instead of a `url()` function
    """
    filepath = String.unquoted(path).value
    if callable(config.STATIC_ROOT):
        try:
            _file, _storage = list(config.STATIC_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(config.STATIC_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
        else:
            filetime = 'NA'
    BASE_URL = config.STATIC_URL

    url = '%s%s' % (BASE_URL, filepath)
    if cache_buster:
        url = add_cache_buster(url, filetime)
    if not only_path:
        url = 'url("%s")' % (url)
    return String.unquoted(url)
コード例 #13
0
ファイル: helpers.py プロジェクト: 4teamwork/pyScss
def _font_files(args, inline):
    if args == ():
        return String.unquoted("")

    fonts = []
    args_len = len(args)
    skip_next = False
    for index, arg in enumerate(args):
        if not skip_next:
            font_type = args[index + 1] if args_len > (index + 1) else None
            if font_type and font_type.value in FONT_TYPES:
                skip_next = True
            else:
                if re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value):
                    font_type = String.unquoted(re.match(r'^([^?]+)[.](.*)([?].*)?$', arg.value).groups()[1])

            if font_type.value in FONT_TYPES:
                fonts.append(List([
                    _font_url(arg, inline=inline),
                    Function(FONT_TYPES[font_type.value], 'format'),
                ], use_comma=False))
            else:
                raise Exception('Could not determine font type for "%s"' % arg.value)
        else:
            skip_next = False

    return List(fonts, separator=',')
コード例 #14
0
ファイル: helpers.py プロジェクト: return42/pyScss
def append_selector(selector, to_append):
    if isinstance(selector, List):
        lst = selector.value
    else:
        lst = String.unquoted(selector).value.split(',')
    to_append = String.unquoted(to_append).value.strip()
    ret = sorted(set(s.strip() + to_append for s in lst if s.strip()))
    ret = dict(enumerate(ret))
    ret['_'] = ','
    return ret
コード例 #15
0
ファイル: helpers.py プロジェクト: Foued123/qooxdoo
def append_selector(selector, to_append):
    if isinstance(selector, List):
        lst = selector.value
    else:
        lst = String.unquoted(selector).value.split(',')
    to_append = String.unquoted(to_append).value.strip()
    ret = sorted(set(s.strip() + to_append for s in lst if s.strip()))
    ret = dict(enumerate(ret))
    ret['_'] = ','
    return ret
コード例 #16
0
ファイル: helpers.py プロジェクト: return42/pyScss
def _font_url(path, only_path=False, cache_buster=True, inline=False):
    filepath = String.unquoted(path).value
    file = None
    FONTS_ROOT = _fonts_root()
    if callable(FONTS_ROOT):
        try:
            _file, _storage = list(FONTS_ROOT(filepath))[0]
        except IndexError:
            filetime = None
        else:
            filetime = getmtime(_file, _storage)
        if filetime is None:
            filetime = 'NA'
        elif inline:
            file = _storage.open(_file)
    else:
        _path = os.path.join(FONTS_ROOT, filepath.strip('/'))
        filetime = getmtime(_path)
        if filetime is None:
            filetime = 'NA'
        elif inline:
            file = open(_path, 'rb')

    BASE_URL = config.FONTS_URL or config.STATIC_URL
    if file and inline:
        font_type = None
        if re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value):
            font_type = String.unquoted(
                re.match(r'^([^?]+)[.](.*)([?].*)?$',
                         path.value).groups()[1]).value

        try:
            mime = FONT_TYPES[font_type]
        except KeyError:
            raise Exception('Could not determine font type for "%s"' %
                            path.value)

        mime = FONT_TYPES.get(font_type)
        if font_type == 'woff':
            mime = 'application/font-woff'
        elif font_type == 'eot':
            mime = 'application/vnd.ms-fontobject'
        url = make_data_url((mime if '/' in mime else 'font/%s' % mime),
                            file.read())
        file.close()
    else:
        url = '%s/%s' % (BASE_URL.rstrip('/'), filepath.lstrip('/'))
        if cache_buster and filetime != 'NA':
            url = add_cache_buster(url, filetime)

    if only_path:
        return String.unquoted(url)
    else:
        return Url.unquoted(url)
コード例 #17
0
ファイル: sprites.py プロジェクト: Rehy0810/database
def sprite_map_name(map):
    """
    Returns the name of a sprite map The name is derived from the folder than
    contains the sprites.
    """
    map = map.render()
    sprite_map = sprite_maps.get(map)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    if sprite_map:
        return String.unquoted(sprite_map['*n*'])
    return String.unquoted('')
コード例 #18
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def sprite_map_name(map):
    """
    Returns the name of a sprite map The name is derived from the folder than
    contains the sprites.
    """
    map = map.render()
    sprite_map = sprite_maps.get(map)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    if sprite_map:
        return String.unquoted(sprite_map['*n*'])
    return String.unquoted('')
コード例 #19
0
ファイル: helpers.py プロジェクト: 4teamwork/pyScss
def _font_url(path, only_path=False, cache_buster=True, inline=False):
    filepath = String.unquoted(path).value
    file = None
    FONTS_ROOT = _fonts_root()
    if callable(FONTS_ROOT):
        try:
            _file, _storage = list(FONTS_ROOT(filepath))[0]
        except IndexError:
            filetime = None
        else:
            filetime = getmtime(_file, _storage)
        if filetime is None:
            filetime = 'NA'
        elif inline:
            file = _storage.open(_file)
    else:
        _path = os.path.join(FONTS_ROOT, filepath.strip('/'))
        filetime = getmtime(_path)
        if filetime is None:
            filetime = 'NA'
        elif inline:
            file = open(_path, 'rb')

    BASE_URL = config.FONTS_URL or config.STATIC_URL
    if file and inline:
        font_type = None
        if re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value):
            font_type = String.unquoted(re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value).groups()[1]).value

        try:
            mime = FONT_TYPES[font_type]
        except KeyError:
            raise Exception('Could not determine font type for "%s"' % path.value)

        mime = FONT_TYPES.get(font_type)
        if font_type == 'woff':
            mime = 'application/font-woff'
        elif font_type == 'eot':
            mime = 'application/vnd.ms-fontobject'
        url = make_data_url(
            (mime if '/' in mime else 'font/%s' % mime),
            file.read())
        file.close()
    else:
        url = '%s/%s' % (BASE_URL.rstrip('/'), filepath.lstrip('/'))
        if cache_buster and filetime != 'NA':
            url = add_cache_buster(url, filetime)

    if only_path:
        return String.unquoted(url)
    else:
        return Url.unquoted(url)
コード例 #20
0
def _font_url(path, only_path=False, cache_buster=True, inline=False):
    filepath = String.unquoted(path).value
    file = None
    FONTS_ROOT = _fonts_root()
    if callable(FONTS_ROOT):
        try:
            _file, _storage = list(FONTS_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
            if inline:
                file = _storage.open(_file)
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(FONTS_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
            if inline:
                file = open(_path, 'rb')
        else:
            filetime = 'NA'

    BASE_URL = config.FONTS_URL or config.STATIC_URL
    if file and inline:
        font_type = None
        if re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value):
            font_type = String.unquoted(
                re.match(r'^([^?]+)[.](.*)([?].*)?$',
                         path.value).groups()[1]).value

        if not FONT_TYPES.get(font_type):
            raise Exception('Could not determine font type for "%s"' %
                            path.value)

        mime = FONT_TYPES.get(font_type)
        if font_type == 'woff':
            mime = 'application/font-woff'
        elif font_type == 'eot':
            mime = 'application/vnd.ms-fontobject'
        url = 'data:' + (mime if '/' in mime else 'font/%s' %
                         mime) + ';base64,' + base64.b64encode(file.read())
        file.close()
    else:
        url = '%s/%s' % (BASE_URL.rstrip('/'), filepath.lstrip('/'))
        if cache_buster and filetime != 'NA':
            url = add_cache_buster(url, filetime)

    if not only_path:
        url = 'url(%s)' % escape(url)
    return String.unquoted(url)
コード例 #21
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def sprite_url(map, cache_buster=True):
    """
    Returns a url to the sprite image.
    """
    map = map.render()
    sprite_map = sprite_maps.get(map)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    if sprite_map:
        url = '%s%s' % (config.ASSETS_URL, sprite_map['*f*'])
        if cache_buster:
            url += '?_=%s' % sprite_map['*t*']
        url = "url(%s)" % escape(url)
        return String.unquoted(url)
    return String.unquoted('')
コード例 #22
0
ファイル: test_expression.py プロジェクト: doug-fish/pyScss
def test_parse_bang_important(calc):
    # The !important flag is treated as part of a spaced list.
    assert calc('40px !important') == List([
        Number(40, 'px'), String.unquoted('!important'),
    ], use_comma=False)

    # And is allowed anywhere in the string.
    assert calc('foo !important bar') == List([
        String('foo'), String('!important'), String('bar'),
    ], use_comma=False)

    # And may have space before the !.
    assert calc('40px ! important') == List([
        Number(40, 'px'), String.unquoted('!important'),
    ], use_comma=False)
コード例 #23
0
ファイル: sprites.py プロジェクト: Rehy0810/database
def sprite_url(map, cache_buster=True):
    """
    Returns a url to the sprite image.
    """
    map = map.render()
    sprite_map = sprite_maps.get(map)
    if not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    if sprite_map:
        url = '%s%s' % (config.ASSETS_URL, sprite_map['*f*'])
        if cache_buster:
            url += '?_=%s' % sprite_map['*t*']
        url = "url(%s)" % escape(url)
        return String.unquoted(url)
    return String.unquoted('')
コード例 #24
0
ファイル: sprites.py プロジェクト: tkhyn/pyScss
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])
コード例 #25
0
ファイル: core.py プロジェクト: MengYing/BibTexParser
def grayscale(color):
    if isinstance(color, Number) and color.is_unitless:
        # grayscale(n) is a CSS3 filter and should be left intact, but only
        # when using the "a" spelling
        return String.unquoted("grayscale(%d)" % (color.value,))
    else:
        return greyscale(color)
コード例 #26
0
ファイル: legacy.py プロジェクト: Alpus/Eth
    def __init__(
            self, scss_vars=None, scss_opts=None, scss_files=None,
            super_selector='', live_errors=False,
            library=None, func_registry=None,
            search_paths=None):

        self.super_selector = super_selector

        self._scss_vars = {}
        if scss_vars:
            calculator = Calculator()
            for var_name, value in scss_vars.items():
                if isinstance(value, six.string_types):
                    scss_value = calculator.evaluate_expression(value)
                    if scss_value is None:
                        # TODO warning?
                        scss_value = String.unquoted(value)
                else:
                    scss_value = value
                self._scss_vars[var_name] = scss_value

        self._scss_opts = scss_opts or {}
        self._scss_files = scss_files
        self._library = func_registry or library
        self._search_paths = search_paths

        # If true, swallow compile errors and embed them in the output instead
        self.live_errors = live_errors
コード例 #27
0
ファイル: images.py プロジェクト: balagopalraj/clearlinux
def image_width(image):
    """
    Returns the width of the image found at the path supplied by `image`
    relative to your project's images directory.
    """
    if not Image:
        raise Exception("Images manipulation require PIL")
    filepath = String.unquoted(image).value
    path = None
    try:
        width = _image_size_cache[filepath][0]
    except KeyError:
        width = 0
        IMAGES_ROOT = _images_root()
        if callable(IMAGES_ROOT):
            try:
                _file, _storage = list(IMAGES_ROOT(filepath))[0]
                path = _storage.open(_file)
            except:
                pass
        else:
            _path = os.path.join(IMAGES_ROOT, filepath.strip("/"))
            if os.path.exists(_path):
                path = open(_path, "rb")
        if path:
            image = Image.open(path)
            size = image.size
            width = size[0]
            _image_size_cache[filepath] = size
    return Number(width, "px")
コード例 #28
0
def join(lst1, lst2, separator=String.unquoted('auto')):
    expect_type(separator, String)

    ret = []
    ret.extend(List.from_maybe(lst1))
    ret.extend(List.from_maybe(lst2))

    if separator.value == 'comma':
        use_comma = True
    elif separator.value == 'space':
        use_comma = False
    elif separator.value == 'auto':
        # The Sass docs are slightly misleading here, but the algorithm is: use
        # the delimiter from the first list that has at least 2 items, or
        # default to spaces.
        if len(lst1) > 1:
            use_comma = lst1.use_comma
        elif len(lst2) > 1:
            use_comma = lst2.use_comma
        else:
            use_comma = False
    else:
        raise ValueError("separator for join() must be comma, space, or auto")

    return List(ret, use_comma=use_comma)
コード例 #29
0
ファイル: core.py プロジェクト: PhilipGarnero/pyScss
def join(lst1, lst2, separator=String.unquoted('auto')):
    expect_type(separator, String)

    ret = []
    ret.extend(List.from_maybe(lst1))
    ret.extend(List.from_maybe(lst2))

    if separator.value == 'comma':
        use_comma = True
    elif separator.value == 'space':
        use_comma = False
    elif separator.value == 'auto':
        # The Sass docs are slightly misleading here, but the algorithm is: use
        # the delimiter from the first list that has at least 2 items, or
        # default to spaces.
        if len(lst1) > 1:
            use_comma = lst1.use_comma
        elif len(lst2) > 1:
            use_comma = lst2.use_comma
        else:
            use_comma = False
    else:
        raise ValueError("separator for join() must be comma, space, or auto")

    return List(ret, use_comma=use_comma)
コード例 #30
0
ファイル: sprites.py プロジェクト: Rehy0810/database
def sprites(map):
    map = map.render()
    sprite_map = sprite_maps.get(map, {})
    return List(
        list(
            String.unquoted(s)
            for s in sorted(s for s in sprite_map if not s.startswith('*'))))
コード例 #31
0
ファイル: test_expression.py プロジェクト: ziotoie00/pyScss
def test_parse_strings(calc):
    # Escapes in barewords are preserved.
    assert calc('auto\\9') == String.unquoted('auto\\9')

    # Escapes in quoted strings are expanded.
    assert calc('"\\2022"') == String("•", quotes='"')
    assert calc('"\\2022"').render() == '"•"'
コード例 #32
0
def image_width(image):
    """
    Returns the width of the image found at the path supplied by `image`
    relative to your project's images directory.
    """
    if not Image:
        raise SassMissingDependency('PIL', 'image manipulation')

    image_size_cache = _get_cache('image_size_cache')

    filepath = String.unquoted(image).value
    path = None
    try:
        width = image_size_cache[filepath][0]
    except KeyError:
        width = 0
        IMAGES_ROOT = _images_root()
        if callable(IMAGES_ROOT):
            try:
                _file, _storage = list(IMAGES_ROOT(filepath))[0]
            except IndexError:
                pass
            else:
                path = _storage.open(_file)
        else:
            _path = os.path.join(IMAGES_ROOT, filepath.strip(os.sep))
            if os.path.exists(_path):
                path = open(_path, 'rb')
        if path:
            image = Image.open(path)
            size = image.size
            width = size[0]
            image_size_cache[filepath] = size
    return Number(width, 'px')
コード例 #33
0
ファイル: test_expression.py プロジェクト: doug-fish/pyScss
def test_parse_strings(calc):
    # Escapes in barewords are preserved.
    assert calc('auto\\9') == String.unquoted('auto\\9')

    # Escapes in quoted strings are expanded.
    assert calc('"\\2022"') == String("•", quotes='"')
    assert calc('"\\2022"').render() == '"•"'
コード例 #34
0
def grad_color_stops(*args):
    args = List.from_maybe_starargs(args)
    color_stops = __color_stops(True, *args)
    ret = ', '.join([
        'color-stop(%s, %s)' % (s.render(), c.render()) for s, c in color_stops
    ])
    return String.unquoted(ret)
コード例 #35
0
    def evaluate(self, calculator, divide=False):
        left = self.left.evaluate(calculator, divide=True)
        right = self.right.evaluate(calculator, divide=True)

        # Determine whether to actually evaluate, or just print the operator
        # literally.
        literal = False

        # If either operand starts with an interpolation, treat the whole
        # shebang as literal.
        if any(isinstance(operand, Interpolation) and operand.parts[0] == ''
                for operand in (self.left, self.right)):
            literal = True

        # Special handling of division: treat it as a literal slash if both
        # operands are literals, there are no parentheses, and this isn't part
        # of a bigger expression.
        # The first condition is covered by the type check.  The other two are
        # covered by the `divide` argument: other nodes that perform arithmetic
        # will pass in True, indicating that this should always be a division.
        elif (
            self.op is operator.truediv
            and not divide
            and isinstance(self.left, Literal)
            and isinstance(self.right, Literal)
        ):
            literal = True

        if literal:
            # TODO we don't currently preserve the spacing, whereas Sass
            # remembers whether there was space on either side
            op = " {0} ".format(self.OPERATORS[self.op])
            return String.unquoted(left.render() + op + right.render())

        return self.op(left, right)
コード例 #36
0
ファイル: expression.py プロジェクト: 7c00/staticpycon
    def calculate(self, expression, divide=False):
        expression = self.evaluate_expression(expression, divide=divide)

        if expression is None:
            return String.unquoted(self.apply_vars(expression))

        return expression
コード例 #37
0
    def calculate(self, expression, divide=False):
        result = self.evaluate_expression(expression, divide=divide)

        if result is None:
            return String.unquoted(self.apply_vars(expression))

        return result
コード例 #38
0
def sprite_position(map, sprite, offset_x=None, offset_y=None):
    """
    Returns the position for the original image in the sprite.
    This is suitable for use as a value to background-position.
    """
    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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    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])
    return List([Number(0), Number(0)])
コード例 #39
0
ファイル: legacy.py プロジェクト: 571451370/devstack_mitaka
    def __init__(self,
                 scss_vars=None,
                 scss_opts=None,
                 scss_files=None,
                 super_selector='',
                 live_errors=False,
                 library=None,
                 func_registry=None,
                 search_paths=None):

        self.super_selector = super_selector

        self._scss_vars = {}
        if scss_vars:
            calculator = Calculator()
            for var_name, value in scss_vars.items():
                if isinstance(value, six.string_types):
                    scss_value = calculator.evaluate_expression(value)
                    if scss_value is None:
                        # TODO warning?
                        scss_value = String.unquoted(value)
                else:
                    scss_value = value
                self._scss_vars[var_name] = scss_value

        self._scss_opts = scss_opts or {}
        self._scss_files = scss_files
        self._library = func_registry or library
        self._search_paths = search_paths

        # If true, swallow compile errors and embed them in the output instead
        self.live_errors = live_errors
コード例 #40
0
ファイル: core.py プロジェクト: PhilipGarnero/pyScss
def grayscale(color):
    if isinstance(color, Number):
        # grayscale(n) and grayscale(n%) are CSS3 filters and should be left
        # intact, but only when using the "a" spelling
        return String.unquoted("grayscale(%s)" % (color.render(),))
    else:
        return greyscale(color)
コード例 #41
0
ファイル: legacy.py プロジェクト: tkhyn/pyScss
    def __init__(
            self, scss_vars=None, scss_opts=None, scss_files=None,
            super_selector='', live_errors=False,
            library=ALL_BUILTINS_LIBRARY, func_registry=None,
            search_paths=None):

        self.super_selector = super_selector

        self._scss_vars = {}
        if scss_vars:
            calculator = Calculator()
            for var_name, value in scss_vars.items():
                if isinstance(value, six.string_types):
                    scss_value = calculator.evaluate_expression(value)
                    if scss_value is None:
                        # TODO warning?
                        scss_value = String.unquoted(value)
                else:
                    scss_value = value
                self._scss_vars[var_name] = scss_value

        self._scss_opts = scss_opts or {}
        self._scss_files = scss_files
        # NOTE: func_registry is backwards-compatibility for only one user and
        # has never existed in a real release
        self._library = func_registry or library
        self._search_paths = search_paths

        # If true, swallow compile errors and embed them in the output instead
        self.live_errors = live_errors
コード例 #42
0
ファイル: core.py プロジェクト: Rehy0810/database
def grayscale(color):
    if isinstance(color, Number) and color.is_unitless:
        # grayscale(n) is a CSS3 filter and should be left intact, but only
        # when using the "a" spelling
        return String.unquoted("grayscale(%d)" % (color.value, ))
    else:
        return greyscale(color)
コード例 #43
0
def grayscale(color):
    if isinstance(color, Number):
        # grayscale(n) and grayscale(n%) are CSS3 filters and should be left
        # intact, but only when using the "a" spelling
        return String.unquoted("grayscale(%s)" % (color.render(),))
    else:
        return greyscale(color)
コード例 #44
0
ファイル: helpers.py プロジェクト: hblanks/pyScss
def _font_files(args, inline):
    args = List.from_maybe_starargs(args)
    n = 0
    params = [[], []]
    for arg in args:
        if isinstance(arg, List):
            if len(arg) == 2:
                if n % 2 == 1:
                    params[1].append(None)
                    n += 1
                params[0].append(arg[0])
                params[1].append(arg[1])
                n += 2
            else:
                for arg2 in arg:
                    params[n % 2].append(arg2)
                    n += 1
        else:
            params[n % 2].append(arg)
            n += 1
    len0 = len(params[0])
    len1 = len(params[1])
    if len1 < len0:
        params[1] += [None] * (len0 - len1)
    elif len0 < len1:
        params[0] += [None] * (len1 - len0)
    fonts = []
    for font, format in zip(params[0], params[1]):
        if format:
            fonts.append('%s format("%s")' % (_font_url(font, inline=inline), String.unquoted(format).value))
        else:
            fonts.append(_font_url(font, inline=inline))
    return List(fonts)
コード例 #45
0
ファイル: ast.py プロジェクト: kan3dev/pyScss
    def evaluate(self, calculator, divide=False):
        left = self.left.evaluate(calculator, divide=True)
        right = self.right.evaluate(calculator, divide=True)

        # Determine whether to actually evaluate, or just print the operator
        # literally.
        literal = False

        # If either operand starts with an interpolation, treat the whole
        # shebang as literal.
        if any(isinstance(operand, Interpolation) and operand.parts[0] == ''
                for operand in (self.left, self.right)):
            literal = True

        # Special handling of division: treat it as a literal slash if both
        # operands are literals, there are no parentheses, and this isn't part
        # of a bigger expression.
        # The first condition is covered by the type check.  The other two are
        # covered by the `divide` argument: other nodes that perform arithmetic
        # will pass in True, indicating that this should always be a division.
        elif (
            self.op is operator.truediv
            and not divide
            and isinstance(self.left, Literal)
            and isinstance(self.right, Literal)
        ):
            literal = True

        if literal:
            # TODO we don't currently preserve the spacing, whereas Sass
            # remembers whether there was space on either side
            op = " {0} ".format(self.OPERATORS[self.op])
            return String.unquoted(left.render() + op + right.render())

        return self.op(left, right)
コード例 #46
0
ファイル: images.py プロジェクト: lamby/31bananas
def image_height(image):
    """
    Returns the height of the image found at the path supplied by `image`
    relative to your project's images directory.
    """
    if not Image:
        raise Exception("Images manipulation require PIL")
    filepath = String.unquoted(image).value
    path = None
    try:
        height = _image_size_cache[filepath][1]
    except KeyError:
        height = 0
        IMAGES_ROOT = _images_root()
        if callable(IMAGES_ROOT):
            try:
                _file, _storage = list(IMAGES_ROOT(filepath))[0]
            except IndexError:
                pass
            else:
                path = _storage.open(_file)
        else:
            _path = os.path.join(IMAGES_ROOT, filepath.strip('/'))
            if os.path.exists(_path):
                path = open(_path, 'rb')
        if path:
            image = Image.open(path)
            size = image.size
            height = size[1]
            _image_size_cache[filepath] = size
    return Number(height, 'px')
コード例 #47
0
ファイル: images.py プロジェクト: 4teamwork/pyScss
def image_width(image):
    """
    Returns the width of the image found at the path supplied by `image`
    relative to your project's images directory.
    """
    if not Image:
        raise SassMissingDependency('PIL', 'image manipulation')

    image_size_cache = _get_cache('image_size_cache')

    filepath = String.unquoted(image).value
    path = None
    try:
        width = image_size_cache[filepath][0]
    except KeyError:
        width = 0
        IMAGES_ROOT = _images_root()
        if callable(IMAGES_ROOT):
            try:
                _file, _storage = list(IMAGES_ROOT(filepath))[0]
            except IndexError:
                pass
            else:
                path = _storage.open(_file)
        else:
            _path = os.path.join(IMAGES_ROOT, filepath.strip(os.sep))
            if os.path.exists(_path):
                path = open(_path, 'rb')
        if path:
            image = Image.open(path)
            size = image.size
            width = size[0]
            image_size_cache[filepath] = size
    return Number(width, 'px')
コード例 #48
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def sprite_position(map, sprite, offset_x=None, offset_y=None):
    """
    Returns the position for the original image in the sprite.
    This is suitable for use as a value to background-position.
    """
    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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    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])
    return List([Number(0), Number(0)])
コード例 #49
0
ファイル: images.py プロジェクト: asfaltboy/pyScss
def image_height(image):
    """
    Returns the height of the image found at the path supplied by `image`
    relative to your project's images directory.
    """
    if not Image:
        raise Exception("Images manipulation require PIL")
    filepath = String.unquoted(image).value
    path = None
    try:
        height = _image_size_cache[filepath][1]
    except KeyError:
        height = 0
        IMAGES_ROOT = _images_root()
        if callable(IMAGES_ROOT):
            try:
                _file, _storage = list(IMAGES_ROOT(filepath))[0]
            except IndexError:
                pass
            else:
                path = _storage.open(_file)
        else:
            _path = os.path.join(IMAGES_ROOT, filepath.strip(os.sep))
            if os.path.exists(_path):
                path = open(_path, 'rb')
        if path:
            image = Image.open(path)
            size = image.size
            height = size[1]
            _image_size_cache[filepath] = size
    return Number(height, 'px')
コード例 #50
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def has_sprite(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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    return Boolean(bool(sprite))
コード例 #51
0
ファイル: fonts.py プロジェクト: coinpayee/pyScss
def has_glyph(sheet, glyph):
    sheet = sheet.render()
    font_sheet = font_sheets.get(sheet)
    glyph_name = String.unquoted(glyph).value
    glyph = font_sheet and font_sheet.get(glyph_name)
    if not font_sheet:
        log.error("No font sheet found: %s", sheet, extra={'stack': True})
    return Boolean(bool(glyph))
コード例 #52
0
ファイル: sprites.py プロジェクト: yacobell/pyScss
def has_sprite(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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    return Boolean(bool(sprite))
コード例 #53
0
ファイル: sprites.py プロジェクト: coinpayee/pyScss
def sprite_file(map, sprite):
    """
    Returns the relative path (from the images directory) to the original file
    used when construction the sprite. This is suitable for passing to the
    image_width and image_height helpers.
    """
    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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    elif not sprite:
        log.error("No sprite found: %s in %s", sprite_name, sprite_map['*n*'], extra={'stack': True})
    if sprite:
        return String(sprite[1][0])
    return String.unquoted('')
コード例 #54
0
def sprite_file(map, sprite):
    """
    Returns the relative path (from the images directory) to the original file
    used when construction the sprite. This is suitable for passing to the
    image_width and image_height helpers.
    """
    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 not sprite_map:
        log.error("No sprite map found: %s", map, extra={'stack': True})
    elif not sprite:
        log.error("No sprite found: %s in %s", sprite_name, sprite_map['*n*'], extra={'stack': True})
    if sprite:
        return String(sprite[1][0])
    return String.unquoted('')
コード例 #55
0
ファイル: fonts.py プロジェクト: coinpayee/pyScss
def has_glyph(sheet, glyph):
    sheet = sheet.render()
    font_sheet = font_sheets.get(sheet)
    glyph_name = String.unquoted(glyph).value
    glyph = font_sheet and font_sheet.get(glyph_name)
    if not font_sheet:
        log.error("No font sheet found: %s", sheet, extra={'stack': True})
    return Boolean(bool(glyph))
コード例 #56
0
ファイル: helpers.py プロジェクト: Foued123/qooxdoo
def _font_url(path, only_path=False, cache_buster=True, inline=False):
    filepath = String.unquoted(path).value
    file = None
    FONTS_ROOT = _fonts_root()
    if callable(FONTS_ROOT):
        try:
            _file, _storage = list(FONTS_ROOT(filepath))[0]
            d_obj = _storage.modified_time(_file)
            filetime = int(time.mktime(d_obj.timetuple()))
            if inline:
                file = _storage.open(_file)
        except:
            filetime = 'NA'
    else:
        _path = os.path.join(FONTS_ROOT, filepath.strip('/'))
        if os.path.exists(_path):
            filetime = int(os.path.getmtime(_path))
            if inline:
                file = open(_path, 'rb')
        else:
            filetime = 'NA'

    BASE_URL = config.FONTS_URL or config.STATIC_URL
    if file and inline:
        font_type = None
        if re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value):
            font_type = String.unquoted(re.match(r'^([^?]+)[.](.*)([?].*)?$', path.value).groups()[1]).value

        if not FONT_TYPES.get(font_type):
            raise Exception('Could not determine font type for "%s"' % path.value)

        mime = FONT_TYPES.get(font_type)
        if font_type == 'woff':
            mime = 'application/font-woff'
        elif font_type == 'eot':
            mime = 'application/vnd.ms-fontobject'
        url = 'data:' + (mime if '/' in mime else 'font/%s' % mime) + ';base64,' + base64.b64encode(file.read())
        file.close()
    else:
        url = '%s/%s' % (BASE_URL.rstrip('/'), filepath.lstrip('/'))
        if cache_buster and filetime != 'NA':
            url = add_cache_buster(url, filetime)

    if not only_path:
        url = 'url(%s)' % escape(url)
    return String.unquoted(url)
コード例 #57
0
ファイル: fonts.py プロジェクト: coinpayee/pyScss
def glyphs(sheet, remove_suffix=False):
    sheet = sheet.render()
    font_sheet = font_sheets.get(sheet, {})
    return List([
        String.unquoted(f) for f in sorted(
            set(
                f.rsplit('-', 1)[0] if remove_suffix else f for f in font_sheet
                if not f.startswith('*')))
    ])
コード例 #58
0
def linear_gradient(*args):
    args = List.from_maybe_starargs(args)

    position_and_angle = _get_gradient_position_and_angle(args)
    color_stops = _get_gradient_color_stops(args)
    if color_stops is None:
        raise Exception('No color stops provided to linear-gradient function')
    color_stops = __color_stops(False, *color_stops)

    args = [
        position(position_and_angle) if position_and_angle is not None else None,
    ]
    args.extend(_render_standard_color_stops(color_stops))

    to__s = 'linear-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')'
    ret = String.unquoted(to__s)

    def to__css2():
        return String.unquoted('')
    ret.to__css2 = to__css2

    def to__moz():
        return String.unquoted('-moz-' + to__s)
    ret.to__moz = to__moz

    def to__pie():
        return String.unquoted('-pie-' + to__s)
    ret.to__pie = to__pie

    def to__ms():
        return String.unquoted('-ms-' + to__s)
    ret.to__ms = to__ms

    def to__o():
        return String.unquoted('-o-' + to__s)
    ret.to__o = to__o

    def to__webkit():
        return String.unquoted('-webkit-' + to__s)
    ret.to__webkit = to__webkit

    def to__owg():
        args = [
            'linear',
            position(position_and_angle or None),
            opposite_position(position_and_angle or None),
        ]
        args.extend('color-stop(%s, %s)' % (s.render(), c.render()) for s, c in color_stops)
        ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args if a is not None) + ')'
        return String.unquoted(ret)
    ret.to__owg = to__owg

    def to__svg():
        return linear_svg_gradient(color_stops, position_and_angle or 'top')
    ret.to__svg = to__svg

    return ret
コード例 #59
0
 def to__owg():
     args = [
         'linear',
         position(position_and_angle or None),
         opposite_position(position_and_angle or None),
     ]
     args.extend('color-stop(%s, %s)' % (s.render(), c.render()) for s, c in color_stops)
     ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args if a is not None) + ')'
     return String.unquoted(ret)
コード例 #60
0
ファイル: test_gradients.py プロジェクト: ziotoie00/pyScss
def test_linear_gradient():
    # Set up some values
    to = String.unquoted('to')
    bottom = String.unquoted('bottom')
    left = String.unquoted('left')

    red = Color.from_name('red')
    blue = Color.from_name('blue')

    start = Number(0, "%")
    middle = Number(50, "%")
    end = Number(100, "%")

    assert (linear_gradient(left, List((red, start)), List(
        (blue, middle))) == String('linear-gradient(left, red, blue 50%)'))

    assert (linear_gradient(List((to, bottom)), blue, List(
        (red, end))) == String('linear-gradient(to bottom, blue, red)'))