module Sass::Script::Functions
Overrides official map functions to support nest keys.
Thanks for Compass Document reference: beta.compass-style.org/reference/compass/helpers/selectors/
Constants
- COMMA_SEPARATOR
- FONT_TYPES
- MIME_TYPES
- PATH_REGEX
Public Instance Methods
Returns the config associated with the given name. Configs are be grouped by ‘SASS_ENV` environment.
Examples: $app-config: (
development: ( foo: bar ), production: ( foo: baz )
);
$ sass –watch -r base.sass src:dist app-config(foo) => bar
$ SASS_ENV=production sass –watch -r base.sass src:dist app-config(foo) => baz
# File lib/base.sass/env.rb, line 33 def app_config(name) assert_type name, :String config = environment.global_env.var('app-config') return null unless config.is_a? Sass::Script::Value::Map map_get(config, *[env(identifier('sass-env')), name]) end
# File lib/base.sass/selector.rb, line 21 def append_selector(selector, to_append) ancestors = selector.value.split(COMMA_SEPARATOR) descendants = to_append.value.split(COMMA_SEPARATOR) nested = ancestors.map { |a| descendants.map { |d| "#{a}#{d}" }.join(', ') }.join(', ') identifier(nested) end
Returns the data in CanIUse
which base.sass used.
Examples: caniuse(browsers) caniuse(Chrome versions) caniuse(Chrome all versions)
# File lib/base.sass/caniuse.rb, line 35 def caniuse(cond) cond = [sass_to_ruby(cond)].flatten.map { |w| w.downcase } output = if cond.first == 'browsers' caniuse_browsers elsif cond.last == 'versions' browser = cond.first assert_valid_browser(browser) caniuse_versions(browser, cond.include?('all')) else raise Sass::SyntaxError, "Unknown condition.\nYou can try `caniuse(browsers)` or `caniuse(Chrome versions)`" end ruby_to_sass(output) end
# File lib/base.sass/selector.rb, line 34 def enumerate(prefix, from, through, separator = identifier('-')) selectors = (from.value..through.value).map { |i| "#{prefix.value}#{separator.value}#{i}" }.join(', ') identifier(selectors) end
Returns the value of environment variable associated with the given name. Returns null if the named variable does not exist.
Examples: env(SASS_ENV) => development env(sass_env) => development env(sass-env) => development
# File lib/base.sass/env.rb, line 10 def env(name) assert_type name, :String ruby_to_sass(ENV[name.value.gsub('-', '_').upcase]) end
# File lib/base.sass/selector.rb, line 42 def headers(from = nil, to = nil) if from && !to if from.is_a?(Sass::Script::Value::String) && from.value == 'all' to, from = number(6), number(1) else to, from = from, number(1) end else from ||= number(1) to ||= number(6) end list((from.value..to.value).map { |n| identifier("h#{n}") }, :comma) end
Returns a new map with keys removed.
Examples: $map: (a: (b: (c: 1))); map-delete($map, a) => () map-delete($map, a, b) => (a: ()) map-delete($map, a, b, c) => (a: (b: ())) map-delete($map, x) => (a: (b: (c: 1))) map-delete($map, a, x, c) => (a: (b: (c: 1))) map-delete($map, a, b, c, x) => (a: (b: (c: 1)))
# File lib/base.sass/map.rb, line 43 def map_delete(map, *keys) return map unless map_has_key(map, *keys).to_bool target, hash = keys.pop, get_hash(map, keys) hash.delete target while keys.size > 0 target = keys.pop _hash, hash = map(hash), get_hash(map, keys) hash[target] = _hash end map(hash) end
Returns the value in a map associated with the given keys.
Examples: $map: (a: (b: (c: 1))); map-get($map, a) => (b: (c: 1)) map-get($map, a, b) => (c: 1) map-get($map, a, b, c) => 1 map-get($map, x) => null map-get($map, a, x, c) => null map-get($map, a, b, c, x) => null map-get((), x) => null
# File lib/base.sass/map.rb, line 15 def map_get(map, *keys) assert_type map, :Map assert_args_number(keys) hash, target = map.to_h, keys.pop keys.each do |key| # Each parent node must be a map unless hash[key].is_a? Sass::Script::Value::Map hash = {} break end hash = hash[key].value end hash[target] || null end
Returns whether a map has a value associated with a given keys.
Examples: $map: (a: (b: (c: 1))); map-has-key($map, a) => true map-has-key($map, a, b) => true map-has-key($map, a, c) => false map-has-key($map, a, b, c) => true map-has-key($map, a, x, c) => false map-has-key($map, a, b, c, x) => false
# File lib/base.sass/map.rb, line 68 def map_has_key(map, *keys) assert_type map, :Map assert_args_number(keys) hash = map.to_h keys.each do |key| # Each parent node must be a map return bool(false) unless hash.is_a?(Hash) && hash.key?(key) hash = hash[key].value end bool(true) end
Merges two maps together into a new map recursively.
Examples: $map1: (a: (b: (c: 1 2, d: foo, e: baz))); $map2: (a: (b: (c: 3 4, d: bar))); map-merge($map1, $map2) => (a: (b: (c: 3 4, d: bar))) map-merge($map1, $map2, true) => (a: (b: (c: 1 2 3 4, d: bar, e: baz)))
# File lib/base.sass/map.rb, line 90 def map_merge(map1, map2, deep = bool(false)) assert_type map1, :Map assert_type map2, :Map map1, map2 = map1.to_h.dup, map2.to_h return map(map1.merge(map2)) unless deep.to_bool map2.each do |k, v| orig = map1[k] map1[k] = compare_value(orig, v) end map(map1) end
# File lib/base.sass/selector.rb, line 6 def nest(*args) nested = args.map { |a| a.value }.inject do |memo, arg| ancestors = memo.split(COMMA_SEPARATOR) descendants = arg.split(COMMA_SEPARATOR) ancestors.map { |a| descendants.map { |d| "#{a} #{d}" }.join(', ') }.join(', ') end identifier(nested) end
Parses a local json file, returns a map, and the result will be cached. If the ‘path` is not a absolute path, relative to current process directory.
Examples: parse-json(‘~/Desktop/example.json’) parse-json(‘package.json’)
# File lib/base.sass/parse-json.rb, line 13 def parse_json(path) assert_type path, :String path = File.expand_path(path.value) if $cached_files.key? path Sass.logger.debug "Reading file from cache: #{path}" $cached_files[path] else $cached_files[path] = ruby_to_sass(load_json(path)) end end
Returns the specified browsers and versions associated with the given rules.
Rules:
-
last 1 version is last versions for each browser.
-
last 2 Chrome versions is last versions of the specified browser.
-
IE > 8 is ie versions newer than 8.
-
IE >= 8 is ie version 8 or newer.
-
iOS 7 to set browser version directly.
# File lib/base.sass/parse-rules.rb, line 11 def parse_rules(*rules) rules = rules.map { |r| sass_to_ruby(r) }.flatten.uniq # Parse selected = rules.map { |r| rules_parser(r.downcase) }.compact # Merge selected = selected.inject { |memo, p| memo.merge(p) { |k, orig, added| orig + added } } # Uniq & Sort selected.each { |k, v| v.uniq! v.sort! } ruby_to_sass(selected) end
Formats time according to the directives in the given format string. Read more: www.ruby-doc.org/core-2.1.1/Time.html#method-i-strftime
Examples: strftime() => 1399392214 strftime(‘%FT%T%:z’) => 2014-05-07T00:03:34+08:00 strftime(‘at %I:%M%p’) => at 12:03AM
# File lib/base.sass/strftime.rb, line 10 def strftime(format = nil) time = Time.now.localtime if format assert_type format, :String identifier(time.strftime(format.value)) else identifier(time.to_i.to_s) end end
Reinforce the official ‘url()` in CSS to support multi url and data url. Activates only when all paths are wrapped with quotes.
Examples: url() => url(
) # Did nothing url(‘
’) => url(a.com/b.png?1399394203) url(‘a.png’, ‘b.png’) => url(a.png?1399394203), url(b.png?1399394203) url(‘a.eot#iefix’, ‘b.woff’) => url(a.eot?1399394203#iefix) format(‘embedded-opentype’), url(b.woff?1399394203) format(‘woff’)
url(‘a.png’, $timestamp: false) => url(a.png) url(‘a.png’, $timestamp: ‘1.0.0’) => url(a.png?1.0.0)
$app-config: (timestamp: ‘1.0.0’); url(‘a.png’) => url(a.png?1.0.0)
$app-config: (timestamp: ‘p1’); url(‘a.png’, $timestamp: ‘p0’) => url(a.png?p0)
url(‘a.png’, $base64: true) => url(data:image/png;base64,iVBORw…)
# File lib/base.sass/url.rb, line 42 def url(*paths) kwargs = paths.last.is_a?(Hash) ? paths.pop : {} raise Sass::SyntaxError, 'url() needs one path at least' if paths.empty? encode = kwargs['base64'] == bool(true) ts = timestamp(kwargs['timestamp']) paths = paths.map { |path| sass_to_ruby(path) }.flatten .map { |path| to_url(path, encode, ts) } list(paths, :comma) end
Protected Instance Methods
# File lib/base.sass/parse-rules.rb, line 42 def assert_valid_browser(browser, version = nil) unless caniuse_browsers.include? browser raise Sass::SyntaxError, "Unknown browser name: #{browser}\nYou can find all valid names according to `caniuse(browsers)`" end unless version.nil? || caniuse_versions(browser, true).include?(version) raise Sass::SyntaxError, "Unknown version for #{browser}: #{version}\nYou can find all valid versions according to `caniuse(#{browser} all versions)`" end end
# File lib/base.sass/parse-rules.rb, line 32 def caniuse_browsers @browsers ||= CanIUse.instance.browsers end
# File lib/base.sass/parse-rules.rb, line 36 def caniuse_versions(browser, include_future) @versions ||= {} k = browser.to_s + include_future.to_s @versions[k] ||= CanIUse.instance.versions(browser, include_future) end
# File lib/base.sass/parse-json.rb, line 28 def load_json(path) JSON.load( read_file(File.expand_path(path)).to_s.gsub(/(\\r|\\n)/, '') ) end
# File lib/base.sass/parse-json.rb, line 34 def read_file(path) raise Sass::SyntaxError, "File not found or cannot be read: #{path}" unless File.readable? path Sass.logger.debug "Reading file: #{path}" File.open(path, 'rb') { |f| f.read } end
# File lib/base.sass/ruby-to-sass.rb, line 5 def ruby_to_sass(obj) return bool(obj) if obj.is_a?(TrueClass) || obj.is_a?(FalseClass) return null if obj.nil? return number(obj) if obj.is_a? Numeric return to_sass_list(obj) if obj.is_a? Array return to_sass_map(obj) if obj.is_a? Hash identifier(obj.to_s) end
# File lib/base.sass/sass-to-ruby.rb, line 5 def sass_to_ruby(obj) return to_ruby_hash(obj) if obj.is_a? Sass::Script::Value::Map return to_ruby_array(obj) if obj.is_a? Sass::Script::Value::List return obj.inspect if obj.is_a? Sass::Script::Value::Color obj.value end
# File lib/base.sass/sass-to-ruby.rb, line 19 def to_ruby_array(sass_list) sass_list.to_a.map do |item| sass_to_ruby(item) end end
# File lib/base.sass/sass-to-ruby.rb, line 12 def to_ruby_hash(sass_map) sass_map.to_h.inject({}) do |memo, (k, v)| memo[k.to_s] = sass_to_ruby(v) memo end end
# File lib/base.sass/ruby-to-sass.rb, line 27 def to_sass_list(ruby_array) list(ruby_array.map { |item| ruby_to_sass(item) }, :comma) end
# File lib/base.sass/ruby-to-sass.rb, line 14 def to_sass_map(ruby_hash) sass_map = map({}) ruby_hash.each do |k, v| sass_map = map_merge( sass_map, map(Hash[identifier(k.to_s), ruby_to_sass(v)]) ) end sass_map end
Private Instance Methods
# File lib/base.sass/map.rb, line 108 def assert_args_number(keys) raise ArgumentError.new('wrong number of arguments (1 for 2+)') if keys.empty? end
# File lib/base.sass/map.rb, line 116 def compare_value(oldVal, newVal) if oldVal.is_a?(Sass::Script::Value::Map) && newVal.is_a?(Sass::Script::Value::Map) map_merge(oldVal, newVal, bool(true)) elsif oldVal.is_a?(Sass::Script::Value::List) && newVal.is_a?(Sass::Script::Value::List) join(oldVal, newVal) else newVal end end
# File lib/base.sass/parse-rules.rb, line 104 def direct_parser(browser, version) version = to_if(version) assert_valid_browser(browser, version) Hash[browser, [version]] end
# File lib/base.sass/map.rb, line 112 def get_hash(map, keys) (keys.empty? ? map : map_get(map, *keys)).to_h.dup end
# File lib/base.sass/parse-rules.rb, line 85 def last_browser_versions_parser(num, browser, assert) assert_valid_browser(browser) if assert Hash[browser, caniuse_versions(browser, false).last(num.to_i)] end
# File lib/base.sass/parse-rules.rb, line 79 def last_versions_parser(num) caniuse_browsers.map { |browser| last_browser_versions_parser(num, browser, false) }.inject :merge end
# File lib/base.sass/parse-rules.rb, line 90 def newer_then_parser(browser, sign, version) assert_valid_browser(browser) versions = caniuse_versions(browser, false) versions = case sign when '>=' versions.select { |n| n >= version.to_f } when '>' versions.select { |n| n > version.to_f } end versions.empty? ? nil : Hash[browser, versions] end
# File lib/base.sass/url.rb, line 106 def output_data(path, ext) data = [read_file(File.expand_path(path))].pack('m').gsub(/\s/, '') "url(data:#{MIME_TYPES[ext]};base64,#{data})" end
# File lib/base.sass/url.rb, line 111 def output_path(path, ext, query, anchor, ts) query += sign(query) + ts unless ts.nil? output = "url(#{path}#{query}#{anchor})" return output unless FONT_TYPES.key? ext [identifier(output), identifier("format('#{FONT_TYPES[ext]}')")] end
# File lib/base.sass/parse-rules.rb, line 55 def rules_parser(rule) case rule # match `last 1 version` when /^last (\d+) versions?$/ last_versions_parser($1) # match `last 3 chrome versions` when /^last (\d+) (\w+) versions?$/ last_browser_versions_parser($1, $2, true) # match `ie > 9` when /^(\w+) (>=?) ([\d\.]+)$/ newer_then_parser($1, $2, $3) # match `ios 7` when /^(\w+) ([\d\.]+)$/ direct_parser($1, $2) else raise Sass::SyntaxError, "Unknown rule: `#{rule}`" end end
# File lib/base.sass/url.rb, line 71 def sign(query) case query.size when 0 '?' when 1 '' else '&' end end
# File lib/base.sass/url.rb, line 59 def timestamp(ts) # no kwargs if ts.nil? cfg = app_config(identifier('timestamp')) ts = cfg == null ? bool(true) : cfg end return nil unless ts.to_bool return strftime.value if ts.is_a? Sass::Script::Value::Bool ts.value.to_s end
# File lib/base.sass/parse-rules.rb, line 110 def to_if(s) s.include?('.') ? s.to_f : s.to_i end
# File lib/base.sass/url.rb, line 82 def to_url(path, encode, ts) output = "url(#{path})" if path.is_a?(String) && path =~ PATH_REGEX path, ext, query, anchor = $1 + $2, $2[1..-1].downcase.to_sym, $3, $4 if MIME_TYPES.key? ext output = if encode output_data(path, ext) else output_path(path, ext, query, anchor, ts) end end end if output.is_a? Array list(output, :space) else identifier(output) end end