haml 3.1.0.alpha.19 → 3.1.0.alpha.22
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of haml might be problematic. Click here for more details.
- data/EDGE_GEM_VERSION +1 -1
- data/VERSION +1 -1
- data/lib/haml/precompiler.rb +1 -0
- data/lib/haml/template/plugin.rb +16 -6
- data/vendor/sass/doc-src/SASS_CHANGELOG.md +41 -0
- data/vendor/sass/doc-src/SASS_REFERENCE.md +37 -6
- data/vendor/sass/lib/sass.rb +7 -3
- data/vendor/sass/lib/sass/engine.rb +4 -4
- data/vendor/sass/lib/sass/environment.rb +24 -15
- data/vendor/sass/lib/sass/less.rb +31 -12
- data/vendor/sass/lib/sass/plugin/staleness_checker.rb +1 -1
- data/vendor/sass/lib/sass/script/funcall.rb +51 -9
- data/vendor/sass/lib/sass/script/functions.rb +189 -57
- data/vendor/sass/lib/sass/script/node.rb +7 -1
- data/vendor/sass/lib/sass/script/number.rb +21 -15
- data/vendor/sass/lib/sass/script/operation.rb +10 -5
- data/vendor/sass/lib/sass/script/parser.rb +61 -17
- data/vendor/sass/lib/sass/script/string.rb +2 -3
- data/vendor/sass/lib/sass/script/variable.rb +6 -0
- data/vendor/sass/lib/sass/scss/parser.rb +8 -5
- data/vendor/sass/lib/sass/selector/sequence.rb +2 -2
- data/vendor/sass/lib/sass/tree/mixin_node.rb +25 -5
- data/vendor/sass/lib/sass/tree/node.rb +2 -2
- data/vendor/sass/lib/sass/tree/prop_node.rb +9 -6
- data/vendor/sass/lib/sass/tree/rule_node.rb +9 -8
- data/vendor/sass/lib/sass/util.rb +5 -3
- data/vendor/sass/test/sass/conversion_test.rb +14 -0
- data/vendor/sass/test/sass/engine_test.rb +85 -0
- data/vendor/sass/test/sass/functions_test.rb +89 -0
- data/vendor/sass/test/sass/less_conversion_test.rb +24 -3
- data/vendor/sass/test/sass/script_conversion_test.rb +65 -0
- data/vendor/sass/test/sass/scss/scss_test.rb +63 -0
- metadata +2 -2
@@ -32,7 +32,13 @@ module Sass::Script
|
|
32
32
|
# @param options [{Symbol => Object}] The options
|
33
33
|
def options=(options)
|
34
34
|
@options = options
|
35
|
-
children.each
|
35
|
+
children.each do |c|
|
36
|
+
if c.is_a? Hash
|
37
|
+
c.values.each {|v| v.options = options }
|
38
|
+
else
|
39
|
+
c.options = options
|
40
|
+
end
|
41
|
+
end
|
36
42
|
end
|
37
43
|
|
38
44
|
# Sets the context for this node,
|
@@ -41,10 +41,13 @@ module Sass::Script
|
|
41
41
|
# @api public
|
42
42
|
PRECISION = 1000.0
|
43
43
|
|
44
|
+
# Used so we don't allocate two new arrays for each new number.
|
45
|
+
NO_UNITS = []
|
46
|
+
|
44
47
|
# @param value [Numeric] The value of the number
|
45
48
|
# @param numerator_units [Array<String>] See \{#numerator\_units}
|
46
49
|
# @param denominator_units [Array<String>] See \{#denominator\_units}
|
47
|
-
def initialize(value, numerator_units =
|
50
|
+
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
|
48
51
|
super(value)
|
49
52
|
@numerator_units = numerator_units
|
50
53
|
@denominator_units = denominator_units
|
@@ -107,7 +110,7 @@ module Sass::Script
|
|
107
110
|
#
|
108
111
|
# @return [Number] The negative value of this number
|
109
112
|
def unary_minus
|
110
|
-
Number.new(-value, numerator_units, denominator_units)
|
113
|
+
Number.new(-value, @numerator_units, @denominator_units)
|
111
114
|
end
|
112
115
|
|
113
116
|
# The SassScript `*` operation.
|
@@ -183,7 +186,7 @@ module Sass::Script
|
|
183
186
|
if unitless?
|
184
187
|
this = this.coerce(other.numerator_units, other.denominator_units)
|
185
188
|
else
|
186
|
-
other = other.coerce(numerator_units, denominator_units)
|
189
|
+
other = other.coerce(@numerator_units, @denominator_units)
|
187
190
|
end
|
188
191
|
rescue Sass::UnitConversionError
|
189
192
|
return Sass::Script::Bool.new(false)
|
@@ -248,7 +251,8 @@ module Sass::Script
|
|
248
251
|
#
|
249
252
|
# @return [String] The representation
|
250
253
|
def inspect(opts = {})
|
251
|
-
|
254
|
+
value = self.class.round(self.value)
|
255
|
+
unitless? ? value.to_s : "#{value}#{unit_str}"
|
252
256
|
end
|
253
257
|
alias_method :to_sass, :inspect
|
254
258
|
|
@@ -266,13 +270,13 @@ module Sass::Script
|
|
266
270
|
|
267
271
|
# @return [Boolean] Whether or not this number has no units.
|
268
272
|
def unitless?
|
269
|
-
numerator_units.empty? && denominator_units.empty?
|
273
|
+
@numerator_units.empty? && @denominator_units.empty?
|
270
274
|
end
|
271
275
|
|
272
276
|
# @return [Boolean] Whether or not this number has units that can be represented in CSS
|
273
277
|
# (that is, zero or one \{#numerator\_units}).
|
274
278
|
def legal_units?
|
275
|
-
(numerator_units.empty? || numerator_units.size == 1) && denominator_units.empty?
|
279
|
+
(@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
|
276
280
|
end
|
277
281
|
|
278
282
|
# Returns this number converted to other units.
|
@@ -295,8 +299,8 @@ module Sass::Script
|
|
295
299
|
Number.new(if unitless?
|
296
300
|
self.value
|
297
301
|
else
|
298
|
-
self.value * coercion_factor(
|
299
|
-
coercion_factor(
|
302
|
+
self.value * coercion_factor(@numerator_units, num_units) /
|
303
|
+
coercion_factor(@denominator_units, den_units)
|
300
304
|
end, num_units, den_units)
|
301
305
|
end
|
302
306
|
|
@@ -316,10 +320,10 @@ module Sass::Script
|
|
316
320
|
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
|
317
321
|
# @return [String] a string that represents the units in this number
|
318
322
|
def unit_str
|
319
|
-
rv = numerator_units.sort.join("*")
|
320
|
-
if denominator_units.any?
|
323
|
+
rv = @numerator_units.sort.join("*")
|
324
|
+
if @denominator_units.any?
|
321
325
|
rv << "/"
|
322
|
-
rv << denominator_units.sort.join("*")
|
326
|
+
rv << @denominator_units.sort.join("*")
|
323
327
|
end
|
324
328
|
rv
|
325
329
|
end
|
@@ -337,13 +341,15 @@ module Sass::Script
|
|
337
341
|
end
|
338
342
|
end
|
339
343
|
|
344
|
+
OPERATIONS = [:+, :-, :<=, :<, :>, :>=]
|
345
|
+
|
340
346
|
def operate(other, operation)
|
341
347
|
this = self
|
342
|
-
if
|
348
|
+
if OPERATIONS.include?(operation)
|
343
349
|
if unitless?
|
344
350
|
this = this.coerce(other.numerator_units, other.denominator_units)
|
345
351
|
else
|
346
|
-
other = other.coerce(numerator_units, denominator_units)
|
352
|
+
other = other.coerce(@numerator_units, @denominator_units)
|
347
353
|
end
|
348
354
|
end
|
349
355
|
# avoid integer division
|
@@ -381,7 +387,7 @@ module Sass::Script
|
|
381
387
|
|
382
388
|
def normalize!
|
383
389
|
return if unitless?
|
384
|
-
@numerator_units, @denominator_units = sans_common_units(numerator_units, denominator_units)
|
390
|
+
@numerator_units, @denominator_units = sans_common_units(@numerator_units, @denominator_units)
|
385
391
|
|
386
392
|
@denominator_units.each_with_index do |d, i|
|
387
393
|
if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?)))
|
@@ -407,7 +413,7 @@ module Sass::Script
|
|
407
413
|
end
|
408
414
|
|
409
415
|
def convertable?(units)
|
410
|
-
Array(units).all?
|
416
|
+
Array(units).all? {|u| CONVERTABLE_UNITS.include?(u)}
|
411
417
|
end
|
412
418
|
|
413
419
|
def sans_common_units(units1, units2)
|
@@ -36,8 +36,8 @@ module Sass::Script
|
|
36
36
|
# @see Node#to_sass
|
37
37
|
def to_sass(opts = {})
|
38
38
|
pred = Sass::Script::Parser.precedence_of(@operator)
|
39
|
-
o1 = operand_to_sass
|
40
|
-
o2 = operand_to_sass
|
39
|
+
o1 = operand_to_sass @operand1, :left, opts
|
40
|
+
o2 = operand_to_sass @operand2, :right, opts
|
41
41
|
sep =
|
42
42
|
case @operator
|
43
43
|
when :comma; ", "
|
@@ -81,9 +81,14 @@ module Sass::Script
|
|
81
81
|
|
82
82
|
private
|
83
83
|
|
84
|
-
def operand_to_sass(
|
85
|
-
return
|
86
|
-
|
84
|
+
def operand_to_sass(op, side, opts)
|
85
|
+
return op.to_sass(opts) unless op.is_a?(Operation)
|
86
|
+
|
87
|
+
pred = Sass::Script::Parser.precedence_of(@operator)
|
88
|
+
sub_pred = Sass::Script::Parser.precedence_of(op.operator)
|
89
|
+
assoc = Sass::Script::Parser.associative?(@operator)
|
90
|
+
return "(#{op.to_sass(opts)})" if sub_pred < pred ||
|
91
|
+
(side == :right && sub_pred == pred && !assoc)
|
87
92
|
op.to_sass(opts)
|
88
93
|
end
|
89
94
|
end
|
@@ -74,19 +74,21 @@ module Sass
|
|
74
74
|
|
75
75
|
# Parses the argument list for a mixin include.
|
76
76
|
#
|
77
|
-
# @return [Array<Script::Node
|
77
|
+
# @return [(Array<Script::Node>, {String => Script::Note})]
|
78
|
+
# The root nodes of the arguments.
|
79
|
+
# Keyword arguments are in a hash from names to values.
|
78
80
|
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
|
79
81
|
def parse_mixin_include_arglist
|
80
|
-
args = []
|
81
|
-
|
82
|
+
args, keywords = [], {}
|
82
83
|
if try_tok(:lparen)
|
83
|
-
args =
|
84
|
+
args, keywords = mixin_arglist || [[], {}]
|
84
85
|
assert_tok(:rparen)
|
85
86
|
end
|
86
87
|
assert_done
|
87
88
|
|
88
89
|
args.each {|a| a.options = @options}
|
89
|
-
|
90
|
+
keywords.each {|k, v| v.options = @options}
|
91
|
+
return args, keywords
|
90
92
|
rescue Sass::SyntaxError => e
|
91
93
|
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
|
92
94
|
raise e
|
@@ -128,6 +130,8 @@ module Sass
|
|
128
130
|
[:times, :div, :mod],
|
129
131
|
]
|
130
132
|
|
133
|
+
ASSOCIATIVE = [:comma, :concat, :plus, :times]
|
134
|
+
|
131
135
|
class << self
|
132
136
|
# Returns an integer representing the precedence
|
133
137
|
# of the given operator.
|
@@ -141,6 +145,13 @@ module Sass
|
|
141
145
|
raise "[BUG] Unknown operator #{op}"
|
142
146
|
end
|
143
147
|
|
148
|
+
# Returns whether or not the given operation is associative.
|
149
|
+
#
|
150
|
+
# @private
|
151
|
+
def associative?(op)
|
152
|
+
ASSOCIATIVE.include?(op)
|
153
|
+
end
|
154
|
+
|
144
155
|
private
|
145
156
|
|
146
157
|
# Defines a simple left-associative production.
|
@@ -254,9 +265,9 @@ RUBY
|
|
254
265
|
|
255
266
|
def funcall
|
256
267
|
return raw unless tok = try_tok(:funcall)
|
257
|
-
args = fn_arglist || []
|
268
|
+
args, keywords = fn_arglist || [[], {}]
|
258
269
|
assert_tok(:rparen)
|
259
|
-
node(Script::Funcall.new(tok.value, args))
|
270
|
+
node(Script::Funcall.new(tok.value, args, keywords))
|
260
271
|
end
|
261
272
|
|
262
273
|
def defn_arglist!(must_have_default)
|
@@ -289,15 +300,48 @@ RUBY
|
|
289
300
|
end
|
290
301
|
|
291
302
|
def fn_arglist
|
292
|
-
|
293
|
-
|
294
|
-
|
303
|
+
arglist(:fn_arglist, :equals)
|
304
|
+
end
|
305
|
+
|
306
|
+
def mixin_arglist
|
307
|
+
arglist(:mixin_arglist, :interpolation)
|
295
308
|
end
|
296
309
|
|
297
|
-
def arglist
|
298
|
-
return unless e =
|
299
|
-
|
300
|
-
|
310
|
+
def arglist(type, subexpr)
|
311
|
+
return unless e = send(subexpr)
|
312
|
+
if @lexer.peek && @lexer.peek.type == :colon
|
313
|
+
name = e
|
314
|
+
@lexer.expected!("comma") unless name.is_a?(Variable)
|
315
|
+
assert_tok(:colon)
|
316
|
+
keywords = {name.underscored_name => assert_expr(subexpr, EXPR_NAMES[type])}
|
317
|
+
end
|
318
|
+
|
319
|
+
unless try_tok(:comma)
|
320
|
+
return [], keywords if keywords
|
321
|
+
return [e], {}
|
322
|
+
end
|
323
|
+
|
324
|
+
other_args, other_keywords = assert_expr(type)
|
325
|
+
if keywords
|
326
|
+
if other_keywords[name.underscored_name]
|
327
|
+
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
|
328
|
+
end
|
329
|
+
return other_args, keywords.merge(other_keywords)
|
330
|
+
else
|
331
|
+
return [e, *other_args], other_keywords
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def keyword_arglist
|
336
|
+
return unless var = try_tok(:const)
|
337
|
+
unless try_tok(:colon)
|
338
|
+
return_tok!
|
339
|
+
return
|
340
|
+
end
|
341
|
+
name = var[1]
|
342
|
+
value = interpolation
|
343
|
+
return {name => value} unless try_tok(:comma)
|
344
|
+
{name => value}.merge(assert_expr(:keyword_arglist))
|
301
345
|
end
|
302
346
|
|
303
347
|
def raw
|
@@ -359,13 +403,13 @@ RUBY
|
|
359
403
|
EXPR_NAMES = {
|
360
404
|
:string => "string",
|
361
405
|
:default => "expression (e.g. 1px, bold)",
|
362
|
-
:
|
406
|
+
:mixin_arglist => "mixin argument",
|
363
407
|
:fn_arglist => "function argument",
|
364
408
|
}
|
365
409
|
|
366
|
-
def assert_expr(name)
|
410
|
+
def assert_expr(name, expected = nil)
|
367
411
|
(e = send(name)) && (return e)
|
368
|
-
@lexer.expected!(EXPR_NAMES[name] || EXPR_NAMES[:default])
|
412
|
+
@lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default])
|
369
413
|
end
|
370
414
|
|
371
415
|
def assert_tok(*names)
|
@@ -41,9 +41,8 @@ module Sass::Script
|
|
41
41
|
|
42
42
|
# @see Node#to_s
|
43
43
|
def to_s(opts = {})
|
44
|
-
if
|
45
|
-
return
|
46
|
-
return self.value.gsub("\n", " ")
|
44
|
+
if @type == :identifier
|
45
|
+
return @context == :equals && @value.empty? ? %q{""} : @value.tr("\n", " ")
|
47
46
|
end
|
48
47
|
|
49
48
|
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
|
@@ -7,9 +7,15 @@ module Sass
|
|
7
7
|
# @return [String]
|
8
8
|
attr_reader :name
|
9
9
|
|
10
|
+
# The underscored name of the variable.
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
attr_reader :underscored_name
|
14
|
+
|
10
15
|
# @param name [String] See \{#name}
|
11
16
|
def initialize(name)
|
12
17
|
@name = name
|
18
|
+
@underscored_name = name.gsub(/-/,"_")
|
13
19
|
super()
|
14
20
|
end
|
15
21
|
|
@@ -138,9 +138,9 @@ module Sass
|
|
138
138
|
|
139
139
|
def include_directive
|
140
140
|
name = tok! IDENT
|
141
|
-
args = sass_script(:parse_mixin_include_arglist)
|
141
|
+
args, keywords = sass_script(:parse_mixin_include_arglist)
|
142
142
|
ss
|
143
|
-
node(Sass::Tree::MixinNode.new(name, args))
|
143
|
+
node(Sass::Tree::MixinNode.new(name, args, keywords))
|
144
144
|
end
|
145
145
|
|
146
146
|
def debug_directive
|
@@ -821,17 +821,20 @@ MESSAGE
|
|
821
821
|
:line => line)
|
822
822
|
end
|
823
823
|
|
824
|
+
# Avoid allocating lots of new strings for `#tok`.
|
825
|
+
# This is important because `#tok` is called all the time.
|
826
|
+
NEWLINE = "\n"
|
827
|
+
|
824
828
|
def tok(rx)
|
825
829
|
res = @scanner.scan(rx)
|
826
830
|
if res
|
827
|
-
@line += res.count(
|
831
|
+
@line += res.count(NEWLINE)
|
828
832
|
@expected = nil
|
829
833
|
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
|
830
834
|
@strs.each {|s| s << res}
|
831
835
|
end
|
836
|
+
res
|
832
837
|
end
|
833
|
-
|
834
|
-
res
|
835
838
|
end
|
836
839
|
end
|
837
840
|
end
|
@@ -48,12 +48,12 @@ module Sass
|
|
48
48
|
# @raise [Sass::SyntaxError] If a parent selector is invalid
|
49
49
|
def resolve_parent_refs(super_seq)
|
50
50
|
members = @members
|
51
|
-
|
51
|
+
nl = (members.first == "\n" && members.shift)
|
52
52
|
unless members.any? do |seq_or_op|
|
53
53
|
seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
|
54
54
|
end
|
55
55
|
members = []
|
56
|
-
members <<
|
56
|
+
members << nl if nl
|
57
57
|
members << SimpleSequence.new([Parent.new])
|
58
58
|
members += @members
|
59
59
|
end
|
@@ -11,13 +11,16 @@ module Sass::Tree
|
|
11
11
|
def options=(opts)
|
12
12
|
super
|
13
13
|
@args.each {|a| a.context = :equals} if opts[:sass2]
|
14
|
+
@keywords.each {|k, v| v.context = :equals} if opts[:sass2]
|
14
15
|
end
|
15
16
|
|
16
17
|
# @param name [String] The name of the mixin
|
17
18
|
# @param args [Array<Script::Node>] The arguments to the mixin
|
18
|
-
|
19
|
+
# @param keywords [{String => Script::Node}] A hash from keyword argument names to values
|
20
|
+
def initialize(name, args, keywords)
|
19
21
|
@name = name
|
20
22
|
@args = args
|
23
|
+
@keywords = keywords
|
21
24
|
super()
|
22
25
|
end
|
23
26
|
|
@@ -42,8 +45,12 @@ module Sass::Tree
|
|
42
45
|
|
43
46
|
# @see Node#to_src
|
44
47
|
def to_src(tabs, opts, fmt)
|
45
|
-
|
46
|
-
|
48
|
+
unless @args.empty? && @keywords.empty?
|
49
|
+
args = @args.map {|a| a.to_sass(opts)}.join(", ")
|
50
|
+
keywords = @keywords.map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
|
51
|
+
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
52
|
+
end
|
53
|
+
"#{' ' * tabs}#{fmt == :sass ? '+' : '@include '}#{dasherize(@name, opts)}#{arglist}#{semi fmt}\n"
|
47
54
|
end
|
48
55
|
|
49
56
|
# @see Node#_cssize
|
@@ -73,15 +80,28 @@ module Sass::Tree
|
|
73
80
|
original_env.prepare_frame(:mixin => @name)
|
74
81
|
raise Sass::SyntaxError.new("Undefined mixin '#{@name}'.") unless mixin = environment.mixin(@name)
|
75
82
|
|
76
|
-
|
83
|
+
passed_args = @args.dup
|
84
|
+
passed_keywords = @keywords.dup
|
85
|
+
|
86
|
+
raise Sass::SyntaxError.new(<<END.gsub("\n", "")) if mixin.args.size < passed_args.size
|
77
87
|
Mixin #{@name} takes #{mixin.args.size} argument#{'s' if mixin.args.size != 1}
|
78
88
|
but #{@args.size} #{@args.size == 1 ? 'was' : 'were'} passed.
|
79
89
|
END
|
80
|
-
|
90
|
+
|
91
|
+
passed_keywords.each do |name, value|
|
92
|
+
# TODO: Make this fast
|
93
|
+
unless mixin.args.find {|(var, default)| var.underscored_name == name}
|
94
|
+
raise Sass::SyntaxError.new("Mixin #{@name} doesn't have an argument named $#{name}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
environment = mixin.args.zip(passed_args).
|
81
99
|
inject(Sass::Environment.new(mixin.environment)) do |env, ((var, default), value)|
|
82
100
|
env.set_local_var(var.name,
|
83
101
|
if value
|
84
102
|
value.perform(environment)
|
103
|
+
elsif kv = passed_keywords[var.underscored_name]
|
104
|
+
kv.perform(env)
|
85
105
|
elsif default
|
86
106
|
val = default.perform(env)
|
87
107
|
if default.context == :equals && val.is_a?(Sass::Script::String)
|
@@ -148,8 +148,8 @@ module Sass
|
|
148
148
|
# @param args [Array] Passed on to \{#\_to\_s}
|
149
149
|
# @return [String, nil] The resulting CSS
|
150
150
|
# @see Sass::Tree
|
151
|
-
def to_s(
|
152
|
-
_to_s(
|
151
|
+
def to_s(opts = nil)
|
152
|
+
_to_s(opts)
|
153
153
|
rescue Sass::SyntaxError => e
|
154
154
|
e.modify_backtrace(:filename => filename, :line => line)
|
155
155
|
raise e
|