Module: Sass::Script::Functions

Included in:
CanIUse
Defined in:
lib/base.sass/selector.rb,
lib/base.sass/sass-to-ruby.rb,
lib/base.sass/ruby-to-sass.rb,
lib/base.sass/parse-rules.rb,
lib/base.sass/parse-json.rb,
lib/base.sass/strftime.rb,
lib/base.sass/caniuse.rb,
lib/base.sass/url.rb,
lib/base.sass/map.rb,
lib/base.sass/env.rb

Overview

Overrides official map functions to support nest keys.

Constant Summary collapse

COMMA_SEPARATOR =
/\s*,\s*/
FONT_TYPES =
{
  eot: 'embedded-opentype',
  woff: 'woff',
  ttf: 'truetype',
  svg: 'svg'
}
MIME_TYPES =
{
  png: 'image/png',
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  gif: 'image/gif',
  eot: 'application/vnd.ms-fontobject',
  woff: 'application/font-woff',
  ttf: 'font/truetype',
  svg: 'image/svg+xml'
}
PATH_REGEX =
/^(.*)(\.\w+)(\??[^#]*)(#?.*)$/

Instance Method Summary collapse

Instance Method Details

#app_config(name) ⇒ Object

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



33
34
35
36
37
38
39
40
# 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

#append_selector(selector, to_append) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
# 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

#caniuse(cond) ⇒ Object

Returns the data in CanIUse which base.sass used.

Examples: caniuse(browsers) caniuse(Chrome versions) caniuse(Chrome all versions)



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 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

#enumerate(prefix, from, through, separator = identifier('-')) ⇒ Object



34
35
36
37
38
39
40
# 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

#env(name) ⇒ Object

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



10
11
12
13
# File 'lib/base.sass/env.rb', line 10

def env(name)
  assert_type name, :String
  ruby_to_sass(ENV[name.value.gsub('-', '_').upcase])
end

#headers(from = nil, to = nil) ⇒ Object Also known as: headings



42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 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

#map_delete(map, *keys) ⇒ Object

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)))



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 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

#map_get(map, *keys) ⇒ Object

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



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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

#map_has_key(map, *keys) ⇒ Object

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



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 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

#map_merge(map1, map2, deep = bool(false)) ⇒ Object

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)))



90
91
92
93
94
95
96
97
98
99
100
101
102
103
# 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

#nest(*args) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 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

#parse_json(path) ⇒ Object

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’)



13
14
15
16
17
18
19
20
21
22
23
# 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

#parse_rules(*rules) ⇒ Object

Returns the specified browsers and versions associated with the given rules.

Rules:

  1. last 1 version is last versions for each browser.

  2. last 2 Chrome versions is last versions of the specified browser.

  3. IE > 8 is ie versions newer than 8.

  4. IE >= 8 is ie version 8 or newer.

  5. iOS 7 to set browser version directly.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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

#strftime(format = nil) ⇒ Object

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



10
11
12
13
14
15
16
17
18
19
# 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

#url(*paths) ⇒ Object

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…)

Raises:

  • (Sass::SyntaxError)


42
43
44
45
46
47
48
49
50
51
52
53
# 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