sass 3.3.0.alpha.231 → 3.3.0.alpha.243

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -56,7 +56,7 @@ to `config.ru`.
56
56
  Then any Sass files in `public/stylesheets/sass`
57
57
  will be compiled into CSS files in `public/stylesheets` on every request.
58
58
 
59
- To use Sass programatically,
59
+ To use Sass programmatically,
60
60
  check out the [YARD documentation](http://sass-lang.com/docs/yardoc/).
61
61
 
62
62
  ## Formatting
data/REVISION CHANGED
@@ -1 +1 @@
1
- 4b12122cb838c1c8b2488b2c0bb3f252d261de2a
1
+ 01ca24be4a152901b7caffd7d3eeaf717dda29d4
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0.alpha.231
1
+ 3.3.0.alpha.243
@@ -1 +1 @@
1
- 23 August 2013 23:13:38 GMT
1
+ 29 August 2013 23:26:16 GMT
@@ -874,21 +874,25 @@ WARNING
874
874
  end
875
875
 
876
876
  def parse_each(line, root, text)
877
- var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
877
+ vars, list_expr = text.scan(/^([^\s]+(?:\s*,\s*[^\s]+)*)\s+in\s+(.+)$/).first
878
878
 
879
- if var.nil? # scan failed, try to figure out why for error message
879
+ if vars.nil? # scan failed, try to figure out why for error message
880
880
  if text !~ /^[^\s]+/
881
881
  expected = "variable name"
882
- elsif text !~ /^[^\s]+\s+from\s+.+/
882
+ elsif text !~ /^[^\s]+(?:\s*,\s*[^\s]+)*[^\s]+\s+from\s+.+/
883
883
  expected = "'in <expr>'"
884
884
  end
885
- raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
885
+ raise SyntaxError.new("Invalid each directive '@each #{text}': expected #{expected}.")
886
+ end
887
+
888
+ vars = vars.split(',').map do |var|
889
+ var.strip!
890
+ raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
891
+ var[1..-1]
886
892
  end
887
- raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
888
893
 
889
- var = var[1..-1]
890
894
  parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
891
- Tree::EachNode.new(var, parsed_list)
895
+ Tree::EachNode.new(vars, parsed_list)
892
896
  end
893
897
 
894
898
  def parse_else(parent, line, text)
@@ -28,6 +28,7 @@ module Sass
28
28
  def initialize(parent = nil, options = nil)
29
29
  @parent = parent
30
30
  @options = options || (parent && parent.options) || {}
31
+ @stack = Sass::Stack.new if @parent.nil?
31
32
  end
32
33
 
33
34
  # The environment of the caller of this environment's mixin or function.
@@ -47,6 +48,13 @@ module Sass
47
48
  @global_env ||= parent.nil? ? self : parent.global_env
48
49
  end
49
50
 
51
+ # The import/mixin stack.
52
+ #
53
+ # @return [Sass::Stack]
54
+ def stack
55
+ @stack || global_env.stack
56
+ end
57
+
50
58
  private
51
59
 
52
60
  class << self
@@ -40,7 +40,7 @@ module Sass::Script
40
40
  #
41
41
  # \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
42
42
  # : Creates a {Sass::Script::Value::Color Color} from hue, saturation,
43
- # lightness, lightness, and alpha values.
43
+ # lightness, and alpha values.
44
44
  #
45
45
  # \{#hue hue($color)}
46
46
  # : Gets the hue component of a color.
@@ -154,6 +154,8 @@ module Sass::Script
154
154
  #
155
155
  # ## List Functions {#list-functions}
156
156
  #
157
+ # All list functions work for maps as well, treating them as lists of pairs.
158
+ #
157
159
  # \{#length length($list)}
158
160
  # : Returns the length of a list.
159
161
  #
@@ -175,6 +177,23 @@ module Sass::Script
175
177
  # \{#list_separator list-separator(#list)}
176
178
  # : Returns the separator of a list.
177
179
  #
180
+ # ## Map Functions {#map-functions}
181
+ #
182
+ # \{#map_get map-get($map, $key)}
183
+ # : Returns the value in a map associated with a given key.
184
+ #
185
+ # \{#map_merge map-merge($map1, $map2)}
186
+ # : Merges two maps together into a new map.
187
+ #
188
+ # \{#map_keys map-keys($map)}
189
+ # : Returns a list of all keys in a map.
190
+ #
191
+ # \{#map_values map-values($map)}
192
+ # : Returns a list of all values in a map.
193
+ #
194
+ # \{#map_has_key map-has-key($key)}
195
+ # : Returns whether a map has a value associated with a given key.
196
+ #
178
197
  # ## Introspection Functions
179
198
  #
180
199
  # \{#feature_exists feature-exists($feature)}
@@ -348,8 +367,7 @@ module Sass::Script
348
367
  class EvaluationContext
349
368
  include Functions
350
369
 
351
-
352
- # The environment of the {Sass::Engine}
370
+ # The global environment.
353
371
  #
354
372
  # @return [Environment]
355
373
  attr_reader :environment
@@ -380,7 +398,9 @@ module Sass::Script
380
398
  # @param name [String, Symbol, nil] The name of the argument.
381
399
  # @raise [ArgumentError] if value is not of the correct type.
382
400
  def assert_type(value, type, name = nil)
383
- return if value.is_a?(Sass::Script::Value.const_get(type))
401
+ klass = Sass::Script::Value.const_get(type)
402
+ return if value.is_a?(klass)
403
+ return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.is_pseudo_map?
384
404
  err = "#{value.inspect} is not a #{type.to_s.downcase}"
385
405
  err = "$#{name.to_s.gsub('_', '-')}: " + err if name
386
406
  raise ArgumentError.new(err)
@@ -552,9 +572,9 @@ module Sass::Script
552
572
  end
553
573
  declare :hsl, [:hue, :saturation, :lightness]
554
574
 
555
- # Creates a {Sass::Script::Value::Color Color} from hue, saturation,
556
- # lightness, lightness, and alpha values. Uses the algorithm from the [CSS3
557
- # spec][].
575
+ # Creates a {Sass::Script::Value::Color Color} from hue,
576
+ # saturation, lightness, and alpha values. Uses the algorithm from
577
+ # the [CSS3 spec][].
558
578
  #
559
579
  # [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
560
580
  #
@@ -1561,9 +1581,12 @@ module Sass::Script
1561
1581
 
1562
1582
  # Return the length of a list.
1563
1583
  #
1584
+ # This can return the number of pairs in a map as well.
1585
+ #
1564
1586
  # @example
1565
1587
  # length(10px) => 1
1566
1588
  # length(10px 20px 30px) => 3
1589
+ # length((width: 10px, height: 20px)) => 2
1567
1590
  # @overload length($list)
1568
1591
  # @param $list [Sass::Script::Value::Base]
1569
1592
  # @return [Sass::Script::Value::Number]
@@ -1577,9 +1600,12 @@ module Sass::Script
1577
1600
  # Note that unlike some languages, the first item in a Sass list is number
1578
1601
  # 1, the second number 2, and so forth.
1579
1602
  #
1603
+ # This can return the nth pair in a map as well.
1604
+ #
1580
1605
  # @example
1581
1606
  # nth(10px 20px 30px, 1) => 10px
1582
1607
  # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
1608
+ # nth((width: 10px, length: 20px), 2) => length, 20px
1583
1609
  # @overload nth($list, $n)
1584
1610
  # @param $list [Sass::Script::Value::Base]
1585
1611
  # @param $n [Sass::Script::Value::Number] The index of the item to get.
@@ -1627,12 +1653,10 @@ module Sass::Script
1627
1653
  unless %w[auto space comma].include?(separator.value)
1628
1654
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1629
1655
  end
1630
- sep1 = list1.separator if list1.is_a?(Sass::Script::Value::List) && !list1.value.empty?
1631
- sep2 = list2.separator if list2.is_a?(Sass::Script::Value::List) && !list2.value.empty?
1632
1656
  Sass::Script::Value::List.new(
1633
1657
  list1.to_a + list2.to_a,
1634
1658
  if separator.value == 'auto'
1635
- sep1 || sep2 || :space
1659
+ list1.separator || list2.separator || :space
1636
1660
  else
1637
1661
  separator.value.to_sym
1638
1662
  end)
@@ -1663,11 +1687,10 @@ module Sass::Script
1663
1687
  unless %w[auto space comma].include?(separator.value)
1664
1688
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1665
1689
  end
1666
- sep = list.separator if list.is_a?(Sass::Script::Value::List)
1667
1690
  Sass::Script::Value::List.new(
1668
1691
  list.to_a + [val],
1669
1692
  if separator.value == 'auto'
1670
- sep || :space
1693
+ list.separator || :space
1671
1694
  else
1672
1695
  separator.value.to_sym
1673
1696
  end)
@@ -1711,9 +1734,12 @@ module Sass::Script
1711
1734
  # Note that unlike some languages, the first item in a Sass list is number
1712
1735
  # 1, the second number 2, and so forth.
1713
1736
  #
1737
+ # This can return the position of a pair in a map as well.
1738
+ #
1714
1739
  # @example
1715
1740
  # index(1px solid red, solid) => 2
1716
1741
  # index(1px solid red, dashed) => false
1742
+ # index((width: 10px, height: 20px), (height, 20px)) => 2
1717
1743
  # @overload index($list, $value)
1718
1744
  # @param $list [Sass::Script::Value::Base]
1719
1745
  # @param $value [Sass::Script::Value::Base]
@@ -1740,14 +1766,99 @@ module Sass::Script
1740
1766
  # @param $list [Sass::Script::Value::Base]
1741
1767
  # @return [Sass::Script::Value::String] `comma` or `space`
1742
1768
  def list_separator(list)
1743
- if list.is_a?(Sass::Script::Value::List)
1744
- Sass::Script::Value::String.new(list.separator.to_s)
1745
- else
1746
- Sass::Script::Value::String.new('space')
1747
- end
1769
+ Sass::Script::Value::String.new((list.separator || :space).to_s)
1748
1770
  end
1749
1771
  declare :separator, [:list]
1750
1772
 
1773
+ # Returns the value in a map associated with the given key. If the map
1774
+ # doesn't have such a key, returns `null`.
1775
+ #
1776
+ # @example
1777
+ # map-get(("foo": 1, "bar": 2), "foo") => 1
1778
+ # map-get(("foo": 1, "bar": 2), "bar") => 2
1779
+ # map-get(("foo": 1, "bar": 2), "baz") => null
1780
+ # @overload map_get($map, $key)
1781
+ # @param $map [Sass::Script::Value::Map]
1782
+ # @param $key [Sass::Script::Value::Base]
1783
+ # @return [Sass::Script::Value::Base] The value indexed by `$key`, or `null`
1784
+ # if the map doesn't contain the given key
1785
+ # @raise [ArgumentError] if `$map` is not a map
1786
+ def map_get(map, key)
1787
+ assert_type map, :Map
1788
+ to_h(map)[key] || Sass::Script::Value::Null.new
1789
+ end
1790
+ declare :map_get, [:map, :key]
1791
+
1792
+ # Merges two maps together into a new map. Keys in `$map2` will take
1793
+ # precedence over keys in `$map1`.
1794
+ #
1795
+ # This is the best way to add new values to a map.
1796
+ #
1797
+ # All keys in the returned map that also appear in `$map1` will have the
1798
+ # same order as in `$map1`. New keys from `$map2` will be placed at the end
1799
+ # of the map.
1800
+ #
1801
+ # @example
1802
+ # map-merge(("foo": 1), ("bar": 2)) => ("foo": 1, "bar": 2)
1803
+ # map-merge(("foo": 1, "bar": 2), ("bar": 3)) => ("foo": 1, "bar": 3)
1804
+ # @overload map_merge($map1, $map2)
1805
+ # @param $map1 [Sass::Script::Value::Map]
1806
+ # @param $map2 [Sass::Script::Value::Map]
1807
+ # @return [Sass::Script::Value::Map]
1808
+ # @raise [ArgumentError] if either parameter is not a map
1809
+ def map_merge(map1, map2)
1810
+ assert_type map1, :Map
1811
+ assert_type map2, :Map
1812
+ Sass::Script::Value::Map.new(to_h(map1).merge(to_h(map2)))
1813
+ end
1814
+ declare :map_get, [:map1, :map2]
1815
+
1816
+ # Returns a list of all keys in a map.
1817
+ #
1818
+ # @example
1819
+ # map-keys(("foo": 1, "bar": 2)) => "foo", "bar"
1820
+ # @overload map_keys($map)
1821
+ # @param $map [Map]
1822
+ # @return [List] the list of keys, comma-separated
1823
+ # @raise [ArgumentError] if `$map` is not a map
1824
+ def map_keys(map)
1825
+ assert_type map, :Map
1826
+ Sass::Script::Value::List.new(to_h(map).keys, :comma)
1827
+ end
1828
+ declare :map_keys, [:map]
1829
+
1830
+ # Returns a list of all values in a map. This list may include duplicate
1831
+ # values, if multiple keys have the same value.
1832
+ #
1833
+ # @example
1834
+ # map-keys(("foo": 1, "bar": 2)) => 1, 2
1835
+ # map-keys(("foo": 1, "bar": 2, "baz": 1)) => 1, 2, 1
1836
+ # @overload map_values($map)
1837
+ # @param $map [Map]
1838
+ # @return [List] the list of values, comma-separated
1839
+ # @raise [ArgumentError] if `$map` is not a map
1840
+ def map_values(map)
1841
+ assert_type map, :Map
1842
+ Sass::Script::Value::List.new(to_h(map).values, :comma)
1843
+ end
1844
+ declare :map_values, [:map]
1845
+
1846
+ # Returns whether a map has a value associated with a given key.
1847
+ #
1848
+ # @example
1849
+ # map-has-key(("foo": 1, "bar": 2), "foo") => true
1850
+ # map-has-key(("foo": 1, "bar": 2), "baz") => false
1851
+ # @overload map_has_key($map, $key)
1852
+ # @param $map [Sass::Script::Value::Map]
1853
+ # @param $key [Sass::Script::Value::Base]
1854
+ # @return [Sass::Script::Value::Bool]
1855
+ # @raise [ArgumentError] if `$map` is not a map
1856
+ def map_has_key(map, key)
1857
+ assert_type map, :Map
1858
+ Sass::Script::Value::Bool.new(to_h(map).has_key?(key))
1859
+ end
1860
+ declare :map_has_key, [:map, :key]
1861
+
1751
1862
  # Returns one of two values, depending on whether or not `$condition` is
1752
1863
  # true. Just like in `@if`, all values other than `false` and `null` are
1753
1864
  # considered to be true.
@@ -1847,5 +1958,17 @@ module Sass::Script
1847
1958
  color.with(attr => Sass::Util.restrict(
1848
1959
  color.send(attr).send(op, amount.value), range))
1849
1960
  end
1961
+
1962
+ def to_h(obj)
1963
+ return obj.to_h unless obj.is_a?(Sass::Script::Value::List) && obj.needs_map_warning?
1964
+
1965
+ fn_name = Sass::Util.caller_info.last.gsub('_', '-')
1966
+ Sass::Util.sass_warn <<WARNING + environment.stack.to_s.gsub(/^/, ' ')
1967
+ DEPRECATION WARNING: Passing lists of pairs to #{fn_name} is deprecated and will
1968
+ be removed in future versions of Sass. Use Sass maps instead. For details, see
1969
+ http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#maps.
1970
+ WARNING
1971
+ return obj.to_h
1972
+ end
1850
1973
  end
1851
1974
  end
@@ -253,11 +253,34 @@ RUBY
253
253
  # @private
254
254
  def lexer_class; Lexer; end
255
255
 
256
+ def map
257
+ start_pos = source_position
258
+ return unless e = interpolation
259
+ return list e, start_pos unless @lexer.peek && @lexer.peek.type == :colon
260
+
261
+ key, value = map_pair(e)
262
+ map = node(Sass::Script::Tree::MapLiteral.new([[key, value]]), start_pos)
263
+ while tok = try_tok(:comma)
264
+ key, value = assert_expr(:map_pair)
265
+ map.pairs << [key, value]
266
+ end
267
+ map
268
+ end
269
+
270
+ def map_pair(key=nil)
271
+ return unless key ||= interpolation
272
+ assert_tok :colon
273
+ return key, assert_expr(:interpolation)
274
+ end
275
+
256
276
  def expr
257
- interp = try_ops_after_interp([:comma], :expr) and return interp
258
277
  start_pos = source_position
259
278
  return unless e = interpolation
260
- list = node(Sass::Script::Tree::ListLiteral.new([e], :comma), start_pos)
279
+ list e, start_pos
280
+ end
281
+
282
+ def list(first, start_pos)
283
+ list = node(Sass::Script::Tree::ListLiteral.new([first], :comma), start_pos)
261
284
  while tok = try_tok(:comma)
262
285
  if interp = try_op_before_interp(tok, list)
263
286
  return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp)
@@ -447,10 +470,10 @@ RUBY
447
470
  was_in_parens = @in_parens
448
471
  @in_parens = true
449
472
  start_pos = source_position
450
- e = expr
473
+ e = map
451
474
  end_pos = source_position
452
475
  assert_tok(:rparen)
453
- return e || node(Sass::Script::Tree::ListLiteral.new([], :space), start_pos, end_pos)
476
+ return e || node(Sass::Script::Tree::ListLiteral.new([], nil), start_pos, end_pos)
454
477
  ensure
455
478
  @in_parens = was_in_parens
456
479
  end
@@ -502,7 +525,7 @@ RUBY
502
525
  end
503
526
 
504
527
  def try_tok(*names)
505
- peeked = @lexer.peek
528
+ peeked = @lexer.peek
506
529
  peeked && names.include?(peeked.type) && @lexer.next
507
530
  end
508
531
 
@@ -12,3 +12,4 @@ require 'sass/script/tree/interpolation'
12
12
  require 'sass/script/tree/string_interpolation'
13
13
  require 'sass/script/tree/literal'
14
14
  require 'sass/script/tree/list_literal'
15
+ require 'sass/script/tree/map_literal'
@@ -0,0 +1,64 @@
1
+ module Sass::Script::Tree
2
+ # A class representing a map literal. When resolved, this returns a
3
+ # {Sass::Script::Node::Map}.
4
+ class MapLiteral < Node
5
+ # The key/value pairs that make up this map node. This isn't a Hash so that
6
+ # we can detect key collisions once all the keys have been performed.
7
+ #
8
+ # @return [Array<(Node, Node)>]
9
+ attr_reader :pairs
10
+
11
+ # Creates a new map literal.
12
+ #
13
+ # @param pairs [Array<(Node, Node)>] See \{#pairs}
14
+ def initialize(pairs)
15
+ @pairs = pairs
16
+ end
17
+
18
+ # @see Node#children
19
+ def children
20
+ @pairs.flatten
21
+ end
22
+
23
+ # @see Node#to_sass
24
+ def to_sass(opts = {})
25
+ return "()" if pairs.empty?
26
+
27
+ to_sass = lambda do |value|
28
+ if value.is_a?(ListLiteral) && value.separator == :comma
29
+ "(#{value.to_sass(opts)})"
30
+ else
31
+ value.to_sass(opts)
32
+ end
33
+ end
34
+
35
+ "(" + pairs.map {|(k, v)| "#{to_sass[k]}: #{to_sass[v]}"}.join(', ') + ")"
36
+ end
37
+ alias_method :inspect, :to_sass
38
+
39
+ # @see Node#deep_copy
40
+ def deep_copy
41
+ node = dup
42
+ node.instance_variable_set('@pairs',
43
+ pairs.map {|(k, v)| [k.deep_copy, v.deep_copy]})
44
+ node
45
+ end
46
+
47
+ protected
48
+
49
+ # @see Node#_perform
50
+ def _perform(environment)
51
+ keys = Set.new
52
+ map = Sass::Script::Value::Map.new(Sass::Util.to_hash(pairs.map do |(k, v)|
53
+ k, v = k.perform(environment), v.perform(environment)
54
+ if keys.include?(k)
55
+ raise Sass::SyntaxError.new("Duplicate key #{k.inspect} in map #{to_sass}.")
56
+ end
57
+ keys << k
58
+ [k, v]
59
+ end))
60
+ map.options = self.options
61
+ map
62
+ end
63
+ end
64
+ end