def call(name, args, R, library, is_function=True): C, O = R.context, R.options # Function call: _name = normalize_var(name) s = args and args.value.items() or [] _args = [v for n, v in s if isinstance(n, int)] _kwargs = dict((str(n[1:]).replace('-', '_'), v) for n, v in s if not isinstance(n, int) and n != '_') _fn_a = '%s:%d' % (_name, len(_args)) try: fn = O and O.get('@function ' + _fn_a) if fn: node = fn(R, *_args, **_kwargs) else: fn = library.lookup(_name, len(_args)) node = fn(*_args, **_kwargs) except KeyError: sp = args and args.value.get('_') or '' if is_function: if not _css_functions_re.match(_name): log.error("Required function not found: %s (%s)", _fn_a, R.file_and_line, extra={'stack': True}) _args = (sp + ' ').join(to_str(v) for n, v in s if isinstance(n, int)) _kwargs = (sp + ' ').join('%s: %s' % (n, to_str(v)) for n, v in s if not isinstance(n, int) and n != '_') if _args and _kwargs: _args += (sp + ' ') # Function not found, simply write it as a string: node = StringValue(name + '(' + _args + _kwargs + ')') else: node = StringValue((sp + ' ').join(str(v) for n, v in s if n != '_')) return node
def __radial_svg(color_stops, cx, cy, r): gradient = '<radialGradient id="grad" gradientUnits="userSpaceOnUse" cx="%s" cy="%s" r="%s">%s</radialGradient>' % ( to_str(Number(cx)), to_str(Number(cy)), to_str(Number(r)), __color_stops_svg(color_stops) ) return __svg_template(gradient)
def linear_gradient(*args): if len(args) == 1 and isinstance(args[0], (list, tuple, ListValue)): args = ListValue(args[0]).values() 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('%s %s' % (c, to_str(s)) for s, c in color_stops) to__s = 'linear-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' ret = StringValue(to__s) def to__css2(): return StringValue('') ret.to__css2 = to__css2 def to__moz(): return StringValue('-moz-' + to__s) ret.to__moz = to__moz def to__pie(): return StringValue('-pie-' + to__s) ret.to__pie = to__pie def to__ms(): return StringValue('-ms-' + to__s) ret.to__ms = to__ms def to__o(): return StringValue('-o-' + to__s) ret.to__o = to__o def to__webkit(): return StringValue('-webkit-' + to__s) ret.to__webkit = to__webkit def to__owg(): args = [ 'linear', position(position_and_angle or 'center top'), opposite_position(position_and_angle or 'center top'), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' return StringValue(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
def to__owg(): args = [ 'linear', position(position_and_angle or 'center top'), opposite_position(position_and_angle or 'center top'), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' return StringValue(ret)
def _linear_svg(color_stops, x1, y1, x2, y2): gradient = '<linearGradient id="grad" x1="%s" y1="%s" x2="%s" y2="%s">%s</linearGradient>' % ( to_str(Number(x1)), to_str(Number(y1)), to_str(Number(x2)), to_str(Number(y2)), __color_stops_svg(color_stops) ) return __svg_template(gradient)
def radial_gradient(*args): if len(args) == 1 and isinstance(args[0], (list, tuple, ListValue)): args = ListValue(args[0]).values() position_and_angle = _get_gradient_position_and_angle(args) shape_and_size = _get_gradient_shape_and_size(args) color_stops = _get_gradient_color_stops(args) if color_stops is None: raise Exception('No color stops provided to radial-gradient function') color_stops = __color_stops(False, *color_stops) args = [ position(position_and_angle) if position_and_angle is not None else None, shape_and_size if shape_and_size is not None else None, ] args.extend('%s %s' % (c, to_str(s)) for s, c in color_stops) to__s = 'radial-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' ret = StringValue(to__s) def to__css2(): return StringValue('') ret.to__css2 = to__css2 def to__moz(): return StringValue('-moz-' + to__s) ret.to__moz = to__moz def to__pie(): log.warn("PIE does not support radial-gradient.") return StringValue('-pie-radial-gradient(unsupported)') ret.to__pie = to__pie def to__webkit(): return StringValue('-webkit-' + to__s) ret.to__webkit = to__webkit def to__owg(): args = [ 'radial', grad_point(position_and_angle) if position_and_angle is not None else 'center', '0', grad_point(position_and_angle) if position_and_angle is not None else 'center', __grad_end_position(True, color_stops), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' return StringValue(ret) ret.to__owg = to__owg def to__svg(): return radial_svg_gradient(color_stops, position_and_angle or 'center') ret.to__svg = to__svg return ret
def to__owg(): args = [ 'radial', grad_point(position_and_angle) if position_and_angle is not None else 'center', '0', grad_point(position_and_angle) if position_and_angle is not None else 'center', __grad_end_position(True, color_stops), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join(to_str(a) for a in args or [] if a is not None) + ')' return StringValue(ret)
def to__owg(): args = [ 'linear', position(position_and_angle or None), opposite_position(position_and_angle or None), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join( to_str(a) for a in args or [] if a is not None) + ')' return StringValue(ret, quotes=None)
def _av(m): v = self.namespace.variable(m.group(2)) if v: v = to_str(v) # TODO this used to test for _dequote if m.group(1): v = dequote(v) elif v is not None: v = to_str(v) else: v = m.group(0) return v
def to__owg(): args = [ 'radial', grad_point(position_and_angle) if position_and_angle is not None else 'center', '0', grad_point(position_and_angle) if position_and_angle is not None else 'center', __grad_end_position(True, color_stops), ] args.extend('color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops) ret = '-webkit-gradient(' + ', '.join( to_str(a) for a in args or [] if a is not None) + ')' return StringValue(ret)
def __init__(self, tokens, separator=None): self.tokens = tokens if tokens is None: self.value = {} elif isinstance(tokens, ListValue): self.value = tokens.value.copy() elif isinstance(tokens, Value): self.value = {0: tokens} elif isinstance(tokens, dict): self.value = self._reorder_list(tokens) elif isinstance(tokens, (list, tuple)): self.value = dict(enumerate(tokens)) else: if isinstance(tokens, StringValue): tokens = tokens.value tokens = to_str(tokens) lst = [i for i in tokens.split() if i] if len(lst) == 1: lst = [i.strip() for i in lst[0].split(",") if i.strip()] if len(lst) > 1: separator = "," if separator is None else separator else: lst = [tokens] self.value = dict(enumerate(lst)) if separator is None: separator = self.value.pop("_", None) if separator: self.value["_"] = separator
def test_subtraction(): calc = lambda expr: to_str(Calculator(Namespace()).calculate(expr)) assert calc('123 - 456') == '-333' assert calc('456 - 123') == '333' # TODO test that subtracting e.g. strings doesn't work assert calc('#0f0f0f - #050505') == '#0a0a0a'
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)
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
def __init__(self, tokens): self.tokens = tokens if tokens is None: self.value = '' elif isinstance(tokens, ParserValue): self.value = dequote(tokens.value) elif isinstance(tokens, QuotedStringValue): self.value = tokens.value else: self.value = to_str(tokens)
def _calculate_expr(self, result): _group0 = result.group(1) _base_str = _group0 better_expr_str = self.evaluate_expression(_base_str) if better_expr_str is None: better_expr_str = self.apply_vars(_base_str) else: better_expr_str = dequote(str(to_str(better_expr_str))) return better_expr_str
def prefixed(prefix, *args): to_fnct_str = 'to_' + to_str(prefix).replace('-', '_') for arg in args: if isinstance(arg, ListValue): for k, iarg in arg.value.items(): if hasattr(iarg, to_fnct_str): return BooleanValue(True) else: if hasattr(arg, to_fnct_str): return BooleanValue(True) return BooleanValue(False)
def radial_gradient(*args): args = List.from_maybe_starargs(args) position_and_angle = _get_gradient_position_and_angle(args) shape_and_size = _get_gradient_shape_and_size(args) color_stops = _get_gradient_color_stops(args) if color_stops is None: raise Exception('No color stops provided to radial-gradient function') color_stops = __color_stops(False, *color_stops) args = [ position(position_and_angle) if position_and_angle is not None else None, shape_and_size if shape_and_size is not None else None, ] args.extend(_render_standard_color_stops(color_stops)) to__s = 'radial-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(): log.warn("PIE does not support radial-gradient.") return String.unquoted('-pie-radial-gradient(unsupported)') ret.to__pie = to__pie def to__webkit(): return String.unquoted('-webkit-' + to__s) ret.to__webkit = to__webkit def to__owg(): args = [ 'radial', grad_point(position_and_angle) if position_and_angle is not None else 'center', '0', grad_point(position_and_angle) if position_and_angle is not None else 'center', __grad_end_position(True, color_stops), ] 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 or [] if a is not None) + ')' return String.unquoted(ret) ret.to__owg = to__owg def to__svg(): return radial_svg_gradient(color_stops, position_and_angle or 'center') ret.to__svg = to__svg return ret
def __init__(self, tokens): self.tokens = tokens if tokens is None: self.value = False elif isinstance(tokens, ParserValue): self.value = (tokens.value.lower() == 'true') elif isinstance(tokens, BooleanValue): self.value = tokens.value elif isinstance(tokens, NumberValue): self.value = bool(tokens.value) elif isinstance(tokens, (float, int)): self.value = bool(tokens) else: self.value = to_str(tokens).lower() in ('true', '1', 'on', 'yes', 't', 'y') or bool(tokens)
def test_addition(): calc = lambda expr: to_str(Calculator(Namespace()).calculate(expr)) assert calc('123 + 456') == '579' assert calc('1px + 2px') == '3px' assert calc('123 + abc') == '123abc' assert calc('abc + 123') == 'abc123' assert calc('abc + def') == 'abcdef' assert calc('abc + "def"') == 'abcdef' assert calc('"abc" + def') == '"abcdef"' assert calc('"abc" + "def"') == '"abcdef"' assert calc('#010305 + #050301') == '#060606' assert calc('#ffffff + #ffffff') == '#ffffff'
def evaluate(self, calculator): # TODO bake this into the context and options "dicts", plus library name = normalize_var(self.func_name) # Turn the pairs of arg tuples into *args and **kwargs # TODO unclear whether this is correct -- how does arg, kwarg, arg # work? args = [] kwargs = {} evald_argpairs = [] for var, expr in self.argspec.argpairs: value = expr.evaluate(calculator) evald_argpairs.append((var, value)) if var is None: args.append(value) else: kwargs[var.lstrip("$").replace("-", "_")] = value num_args = len(self.argspec.argpairs) # TODO merge this with the library try: func = calculator.namespace.function(self.func_name, num_args) # @functions take a ns as first arg. TODO: Python functions possibly # should too if getattr(func, "__name__", None) == "__call": func = partial(func, calculator.namespace) except KeyError: if not is_builtin_css_function(self.func_name): # TODO log.warn, log.error, warning, exception? log.warn("no such function") rendered_args = [] for var, value in evald_argpairs: rendered_value = to_str(value) if var is None: rendered_args.append(rendered_value) else: rendered_args.append("%s: %s" % (var, rendered_value)) return String(u"%s(%s)" % (self.func_name, u", ".join(rendered_args)), quotes=None) else: return func(*args, **kwargs)
def prefix(prefix, *args): to_fnct_str = 'to_' + to_str(prefix).replace('-', '_') args = list(args) for i, arg in enumerate(args): if isinstance(arg, List): _value = [] for iarg in arg: to_fnct = getattr(iarg, to_fnct_str, None) if to_fnct: _value.append(to_fnct()) else: _value.append(iarg) args[i] = List(_value) else: to_fnct = getattr(arg, to_fnct_str, None) if to_fnct: args[i] = to_fnct() return List.maybe_new(args, use_comma=True)
def __init__(self, tokens, separator=None, use_comma=True): if isinstance(tokens, ListValue): tokens = tokens.value if not isinstance(tokens, (list, tuple)): raise TypeError("Expected list, got %r" % (tokens,)) self.value = list(tokens) # TODO... self.use_comma = separator == "," return self.tokens = tokens if tokens is None: self.value = {} elif isinstance(tokens, ListValue): self.value = tokens.value.copy() elif isinstance(tokens, Value): self.value = {0: tokens} elif isinstance(tokens, dict): self.value = self._reorder_list(tokens) elif isinstance(tokens, (list, tuple)): self.value = dict(enumerate(tokens)) else: if isinstance(tokens, StringValue): tokens = tokens.value tokens = to_str(tokens) lst = [i for i in tokens.split() if i] if len(lst) == 1: lst = [i.strip() for i in lst[0].split(',') if i.strip()] if len(lst) > 1: separator = ',' if separator is None else separator else: lst = [tokens] self.value = dict(enumerate(lst)) if separator is None: separator = self.value.pop('_', None) if separator: self.value['_'] = separator
def test_reference_operations(): """Test the example expressions in the reference document: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#operations """ # TODO: break this into its own file and add the entire reference guide ns = Namespace() calc = lambda expr: to_str(Calculator(ns).calculate(expr)) # Simple example assert calc('1in + 8pt') == '1.111in' # Division ns.set_variable('$width', '1000px') ns.set_variable('$font-size', '12px') ns.set_variable('$line-height', '30px') assert calc('10px/8px') == '10px/8px' # plain CSS; no division assert calc('$width/2') == '500px' # uses a variable; does division assert calc('(500px/2)') == '250px' # uses parens; does division assert calc('5px + 8px/2px') == '9px' # uses +; does division assert calc('#{$font-size}/#{$line-height}') == '12px/30px' # uses #{}; does no division # Color operations ns.set_variable('$translucent-red', 'rgba(255, 0, 0, 0.5)') ns.set_variable('$green', '#00ff00') assert calc('#010203 + #040506') == '#050709' assert calc('#010203 * 2') == '#020406' assert calc('rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75)') == 'rgba(255, 255, 0, 0.75)' assert calc('opacify($translucent-red, 0.3)') == 'rgba(255, 0, 0, 0.9)' assert calc('transparentize($translucent-red, 0.25)') == 'rgba(255, 0, 0, 0.25)' assert calc("progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}')" ) == "progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=#FF00FF00, endColorstr=#80FF0000)" # String operations ns.set_variable('$value', 'null') assert calc('e + -resize') == 'e-resize' assert calc('"Foo " + Bar') == '"Foo Bar"' assert calc('sans- + "serif"') == 'sans-serif' assert calc('3px + 4px auto') == '7px auto' assert calc('"I ate #{5 + 10} pies!"') == '"I ate 15 pies!"' assert calc('"I ate #{$value} pies!"') == '"I ate pies!"'
def prefix(prefix, *args): to_fnct_str = 'to_' + to_str(prefix).replace('-', '_') args = list(args) for i, arg in enumerate(args): if isinstance(arg, ListValue): _value = {} for k, iarg in arg.value.items(): to_fnct = getattr(iarg, to_fnct_str, None) if to_fnct: _value[k] = to_fnct() else: _value[k] = iarg args[i] = ListValue(_value) else: to_fnct = getattr(arg, to_fnct_str, None) if to_fnct: args[i] = to_fnct() if len(args) == 1: return args[0] return ListValue(args, ',')
def grad_color_stops(*args): args = List.from_maybe_starargs(args) color_stops = __color_stops(True, *args) ret = ', '.join( ['color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops]) return StringValue(ret)
def __color_stops_svg(color_stops): ret = ''.join('<stop offset="%s" stop-color="%s"/>' % (to_str(s), c) for s, c in color_stops) return ret
def main(): logging.basicConfig(format="%(levelname)s: %(message)s") from optparse import OptionGroup, OptionParser, SUPPRESS_HELP parser = OptionParser(usage="Usage: %prog [options] [file]", description="Converts Scss files to CSS.", add_help_option=False) parser.add_option("-i", "--interactive", action="store_true", help="Run an interactive Scss shell") parser.add_option( "-w", "--watch", metavar="DIR", help="Watch the files in DIR, and recompile when they change") parser.add_option( "-r", "--recursive", action="store_true", default=False, help="Also watch directories inside of the watch directory") parser.add_option( "-o", "--output", metavar="PATH", help= "Write output to PATH (a directory if using watch, a file otherwise)") parser.add_option( "-s", "--suffix", metavar="STRING", help= "If using watch, a suffix added to the output filename (i.e. filename.STRING.css)" ) parser.add_option("--time", action="store_true", help="Display compliation times") parser.add_option("--debug-info", action="store_true", help="Turns on scss's debugging information") parser.add_option("--no-debug-info", action="store_false", dest="debug_info", default=False, help="Turns off scss's debugging information") parser.add_option("-t", "--test", action="store_true", help=SUPPRESS_HELP) parser.add_option("-C", "--no-compress", action="store_false", dest="compress", default=True, help="Don't minify outputted CSS") parser.add_option("-?", action="help", help=SUPPRESS_HELP) parser.add_option("-h", "--help", action="help", help="Show this message and exit") parser.add_option("-v", "--version", action="store_true", help="Print version and exit") paths_group = OptionGroup(parser, "Resource Paths") paths_group.add_option( "-I", "--load-path", metavar="PATH", action="append", dest="load_paths", help="Add a scss import path, may be given multiple times") paths_group.add_option( "-S", "--static-root", metavar="PATH", dest="static_root", help="Static root path (Where images and static resources are located)" ) paths_group.add_option( "-A", "--assets-root", metavar="PATH", dest="assets_root", help="Assets root path (Sprite images will be created here)") paths_group.add_option("-a", "--assets-url", metavar="URL", dest="assets_url", help="URL to reach the files in your assets_root") paths_group.add_option( "--cache-root", metavar="PATH", dest="cache_root", help="Cache root path (Cache files will be created here)") parser.add_option_group(paths_group) parser.add_option("--sass", action="store_true", dest="is_sass", default=None, help="Sass mode") (options, args) = parser.parse_args() # General runtime configuration config.VERBOSITY = 0 if options.time: config.VERBOSITY = 2 if options.static_root is not None: config.STATIC_ROOT = options.static_root if options.assets_root is not None: config.ASSETS_ROOT = options.assets_root if options.cache_root is not None: config.CACHE_ROOT = options.cache_root if options.load_paths is not None: # TODO: Convert global LOAD_PATHS to a list. Use it directly. # Doing the above will break backwards compatibility! if hasattr(config.LOAD_PATHS, 'split'): load_path_list = [p.strip() for p in config.LOAD_PATHS.split(',')] else: load_path_list = list(config.LOAD_PATHS) for path_param in options.load_paths: for p in path_param.replace(os.pathsep, ',').replace(';', ',').split(','): p = p.strip() if p and p not in load_path_list: load_path_list.append(p) # TODO: Remove this once global LOAD_PATHS is a list. if hasattr(config.LOAD_PATHS, 'split'): config.LOAD_PATHS = ','.join(load_path_list) else: config.LOAD_PATHS = load_path_list if options.assets_url is not None: config.ASSETS_URL = options.assets_url # Execution modes if options.test: import doctest doctest.testfile('tests/tests.rst') elif options.version: print(BUILD_INFO) elif options.interactive: from pprint import pprint try: import atexit import readline histfile = os.path.expanduser('~/.scss-history') try: readline.read_history_file(histfile) except IOError: pass atexit.register(readline.write_history_file, histfile) except ImportError: pass is_sass = options.is_sass css = Scss() context = css.scss_vars options = css.scss_opts source_file = SourceFile.from_string('', '<shell>', line_numbers=False) rule = SassRule(source_file, context=context, options=options, is_sass=is_sass) print("Welcome to %s interactive shell" % (BUILD_INFO, )) while True: try: s = raw_input('>>> ').strip() except EOFError: print break except KeyboardInterrupt: print break if s in ('exit', 'quit'): break for s in s.split(';'): s = source_file.prepare_source(s.strip()) if not s: continue elif s.startswith('@'): properties = [] children = deque() SassRule(source_file, context=context, options=options, properties=properties) code, name = (s.split(None, 1) + [''])[:2] if code == '@option': css._settle_options(rule, [''], set(), children, None, None, s, None, code, name) continue elif code == '@import': css._do_import(rule, [''], set(), children, None, None, s, None, code, name) continue elif code == '@include': final_cont = '' css._do_include(rule, [''], set(), children, None, None, s, None, code, name) code = css._print_properties(properties).rstrip('\n') if code: final_cont += code if children: css.children.extendleft(children) css.parse_children() code = css._create_css(css.rules).rstrip('\n') if code: final_cont += code final_cont = css.post_process(final_cont) print(final_cont) continue elif s == 'ls' or s.startswith('show(') or s.startswith( 'show ') or s.startswith('ls(') or s.startswith('ls '): m = re.match( r'(?:show|ls)(\()?\s*([^,/\\) ]*)(?:[,/\\ ]([^,/\\ )]+))*(?(1)\))', s, re.IGNORECASE) if m: name = m.group(2) code = m.group(3) name = name and name.strip().rstrip( 's') # remove last 's' as in functions code = code and code.strip() if not name: pprint( sorted( ['vars', 'options', 'mixins', 'functions'])) elif name in ('v', 'var', 'variable'): if code == '*': d = dict((k, v) for k, v in context.items()) pprint(d) elif code: d = dict((k, v) for k, v in context.items() if code in k) pprint(d) else: d = dict((k, v) for k, v in context.items() if k.startswith('$') and not k.startswith('$__')) pprint(d) elif name in ('o', 'opt', 'option'): if code == '*': d = dict((k, v) for k, v in options.items()) pprint(d) elif code: d = dict((k, v) for k, v in options.items() if code in k) pprint(d) else: d = dict((k, v) for k, v in options.items() if not k.startswith('@')) pprint(d) elif name in ('m', 'mix', 'mixin', 'f', 'func', 'funct', 'function'): if name.startswith('m'): name = 'mixin' elif name.startswith('f'): name = 'function' if code == '*': d = dict((k[len(name) + 2:], v) for k, v in options.items() if k.startswith('@' + name + ' ')) pprint(sorted(d)) elif code: d = dict((k, v) for k, v in options.items() if k.startswith('@' + name + ' ') and code in k) seen = set() for k, mixin in d.items(): mixin = getattr(mixin, 'mixin', mixin) fn_name, _, _ = k.partition(':') if fn_name not in seen: seen.add(fn_name) print(fn_name + '(' + ', '.join( p + (': ' + mixin[1].get(p) if p in mixin[1] else '') for p in mixin[0]) + ') {') print(' ' + '\n '.join( l for l in mixin[2].split('\n'))) print('}') else: d = dict((k[len(name) + 2:].split(':')[0], v) for k, v in options.items() if k.startswith('@' + name + ' ')) pprint(sorted(d)) continue elif s.startswith('$') and (':' in s or '=' in s): prop, value = [ a.strip() for a in _prop_split_re.split(s, 1) ] prop = css.calculator.do_glob_math(prop, context, options, rule, True) value = css.calculator.calculate(value, context, rule) context[prop] = value continue s = to_str(css.calculator.calculate(s, context, rule)) s = css.post_process(s) print(s) print("Bye!") elif options.watch: import time try: from watchdog.observers import Observer from watchdog.events import PatternMatchingEventHandler except ImportError: sys.stderr.write( "Using watch functionality requires the `watchdog` library: http://pypi.python.org/pypi/watchdog/" ) sys.exit(1) if options.output and not os.path.isdir(options.output): sys.stderr.write("watch file output directory is invalid: '%s'" % (options.output)) sys.exit(2) class ScssEventHandler(PatternMatchingEventHandler): def __init__(self, *args, **kwargs): super(ScssEventHandler, self).__init__(*args, **kwargs) self.css = Scss( scss_opts={ 'compress': options.compress, 'debug_info': options.debug_info, }) self.output = options.output self.suffix = options.suffix def is_valid(self, path): return os.path.isfile(path) and ( path.endswith('.scss') or path.endswith('.sass') ) and not os.path.basename(path).startswith('_') def process(self, path): if os.path.isdir(path): for f in os.listdir(path): full = os.path.join(path, f) if self.is_valid(full): self.compile(full) elif self.is_valid(path): self.compile(path) def compile(self, src_path): fname = os.path.basename(src_path) if fname.endswith('.scss') or fname.endswith('.sass'): fname = fname[:-5] if self.suffix: fname += '.' + self.suffix fname += '.css' else: # you didn't give me a file of the correct type! return False if self.output: dest_path = os.path.join(self.output, fname) else: dest_path = os.path.join(os.path.dirname(src_path), fname) print("Compiling %s => %s" % (src_path, dest_path)) dest_file = open(dest_path, 'w') dest_file.write(self.css.compile(scss_file=src_path)) def on_moved(self, event): super(ScssEventHandler, self).on_moved(event) self.process(event.dest_path) def on_created(self, event): super(ScssEventHandler, self).on_created(event) self.process(event.src_path) def on_modified(self, event): super(ScssEventHandler, self).on_modified(event) self.process(event.src_path) event_handler = ScssEventHandler(patterns=['*.scss', '*.sass']) observer = Observer() observer.schedule(event_handler, path=options.watch, recursive=options.recursive) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join() else: if options.output is not None: output = open(options.output, 'wt') else: output = sys.stdout css = Scss(scss_opts={ 'compress': options.compress, 'debug_info': options.debug_info, }) if args: for path in args: output.write( css.compile(scss_file=path, is_sass=options.is_sass)) else: output.write(css.compile(sys.stdin.read(), is_sass=options.is_sass)) for f, t in profiling.items(): sys.stderr.write("%s took %03fs" % (f, t))
def __str__(self): # TODO bit of a hack? if self.tokens is not None and isinstance(self.tokens, ParserValue): return self.tokens.value type = self.type c = self.value if type == "hsl" or type == "hsla" and c[3] == 1: h, l, s = colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0) return "hsl(%s, %s%%, %s%%)" % (to_str(h * 360.0), to_str(s * 100.0), to_str(l * 100.0)) if type == "hsla": h, l, s = colorsys.rgb_to_hls(c[0] / 255.0, c[1] / 255.0, c[2] / 255.0) return "hsla(%s, %s%%, %s%%, %s)" % (to_str(h * 360.0), to_str(s * 100.0), to_str(l * 100.0), to_str(c[3])) r, g, b = to_str(c[0]), to_str(c[1]), to_str(c[2]) are_integral = True for n in c[:3]: # replicating old logic; perhaps needs rethinking n2 = round(n * 100, 1) if n2 != int(n2): are_integral = False break if c[3] == 1: if not are_integral: return "rgb(%s%%, %s%%, %s%%)" % ( to_str(c[0] * 100.0 / 255.0), to_str(c[1] * 100.0 / 255.0), to_str(c[2] * 100.0 / 255.0), ) return "#%02x%02x%02x" % (round(c[0]), round(c[1]), round(c[2])) if not are_integral: return "rgba(%s%%, %s%%, %s%%, %s)" % ( to_str(c[0] * 100.0 / 255.0), to_str(c[1] * 100.0 / 255.0), to_str(c[2] * 100.0 / 255.0), to_str(c[3]), ) return "rgba(%d, %d, %d, %s)" % (round(c[0]), round(c[1]), round(c[2]), to_str(c[3]))
def prefixed(prefix, *args): to_fnct_str = 'to_' + to_str(prefix).replace('-', '_') for arg in List.from_maybe_starargs(args): if hasattr(arg, to_fnct_str): return Boolean(True) return Boolean(False)
def __str__(self): return to_str(self.value)
def __init__(self, tokens): self.tokens = tokens self.value = (0, 0, 0, 1) self.types = {} if tokens is None: self.value = (0, 0, 0, 1) elif isinstance(tokens, ParserValue): hex = tokens.value self.value = self.HEX2RGBA[len(hex)](hex) self.types = {"rgba": 1} elif isinstance(tokens, ColorValue): self.value = tokens.value self.types = tokens.types.copy() elif isinstance(tokens, NumberValue): val = tokens.value self.value = (val, val, val, 1) elif isinstance(tokens, (list, tuple)): c = tokens[:4] r = 255.0, 255.0, 255.0, 1.0 c = [0.0 if c[i] < 0 else r[i] if c[i] > r[i] else c[i] for i in range(4)] self.value = tuple(c) type = tokens[-1] if type in ("rgb", "rgba", "hsl", "hsla"): self.types = {type: 1} elif isinstance(tokens, (int, float)): val = float(tokens) self.value = (val, val, val, 1) else: if isinstance(tokens, StringValue): tokens = tokens.value tokens = to_str(tokens) tokens.replace(" ", "").lower() try: self.value = self.HEX2RGBA[len(tokens)](tokens) except: try: val = to_float(tokens) self.value = (val, val, val, 1) except ValueError: try: type, _, colors = tokens.partition("(") colors = colors.rstrip(")") if type in ("rgb", "rgba"): c = tuple(colors.split(",")) try: c = [to_float(c[i]) for i in range(4)] col = [0.0 if c[i] < 0 else 255.0 if c[i] > 255 else c[i] for i in range(3)] col += [0.0 if c[3] < 0 else 1.0 if c[3] > 1 else c[3]] self.value = tuple(col) self.types = {type: 1} except: raise ValueError("Value is not a Color! (%s)" % tokens) elif type in ("hsl", "hsla"): c = colors.split(",") try: c = [to_float(c[i]) for i in range(4)] col = [c[0] % 360.0] / 360.0 col += [0.0 if c[i] < 0 else 1.0 if c[i] > 1 else c[i] for i in range(1, 4)] self.value = tuple( [ c * 255.0 for c in colorsys.hls_to_rgb( col[0], 0.999999 if col[2] == 1 else col[2], 0.999999 if col[1] == 1 else col[1], ) ] + [col[3]] ) self.types = {type: 1} except: raise ValueError("Value is not a Color! (%s)" % tokens) else: raise ValueError("Value is not a Color! (%s)" % tokens) except: raise ValueError("Value is not a Color! (%s)" % tokens)
def grad_color_stops(*args): if len(args) == 1 and isinstance(args[0], (list, tuple, ListValue)): args = ListValue(args[0]).values() color_stops = __color_stops(True, *args) ret = ', '.join(['color-stop(%s, %s)' % (to_str(s), c) for s, c in color_stops]) return StringValue(ret)
def __str__(self): unit = self.unit val = self.value / _conv_factor.get(unit, 1.0) val = to_str(val) + unit return val
def color_stops_in_percentages(*args): args = List.from_maybe_starargs(args) color_stops = __color_stops(True, *args) ret = ', '.join(['%s %s' % (c, to_str(s)) for s, c in color_stops]) return StringValue(ret)