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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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