sass 3.2.0.alpha.57 → 3.2.0.alpha.59
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/REVISION +1 -1
- data/VERSION +1 -1
- data/lib/sass/engine.rb +22 -13
- data/lib/sass/media.rb +300 -0
- data/lib/sass/script/funcall.rb +2 -2
- data/lib/sass/script/literal.rb +0 -20
- data/lib/sass/script/operation.rb +8 -0
- data/lib/sass/script/parser.rb +21 -0
- data/lib/sass/scss/css_parser.rb +0 -11
- data/lib/sass/scss/parser.rb +95 -61
- data/lib/sass/scss/static_parser.rb +1 -0
- data/lib/sass/selector.rb +1 -1
- data/lib/sass/tree/directive_node.rb +17 -3
- data/lib/sass/tree/media_node.rb +8 -5
- data/lib/sass/tree/visitors/convert.rb +15 -11
- data/lib/sass/tree/visitors/cssize.rb +1 -3
- data/lib/sass/tree/visitors/deep_copy.rb +10 -0
- data/lib/sass/tree/visitors/perform.rb +10 -5
- data/lib/sass/tree/visitors/set_options.rb +10 -0
- data/lib/sass/tree/visitors/to_css.rb +16 -8
- data/lib/sass/util.rb +14 -0
- data/test/sass/conversion_test.rb +60 -0
- data/test/sass/engine_test.rb +171 -16
- data/test/sass/script_test.rb +5 -0
- data/test/sass/scss/css_test.rb +1 -1
- data/test/sass/scss/scss_test.rb +46 -24
- metadata +9 -8
data/REVISION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
77d6cfe978e5d2d28ee6126e207e19b86b661c27
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.2.0.alpha.
|
|
1
|
+
3.2.0.alpha.59
|
data/lib/sass/engine.rb
CHANGED
|
@@ -39,6 +39,7 @@ require 'sass/scss'
|
|
|
39
39
|
require 'sass/error'
|
|
40
40
|
require 'sass/importers'
|
|
41
41
|
require 'sass/shared'
|
|
42
|
+
require 'sass/media'
|
|
42
43
|
|
|
43
44
|
module Sass
|
|
44
45
|
|
|
@@ -223,7 +224,7 @@ module Sass
|
|
|
223
224
|
# If you're compiling a single Sass file from the filesystem,
|
|
224
225
|
# use \{Sass::Engine.for\_file}.
|
|
225
226
|
# If you're compiling multiple files from the filesystem,
|
|
226
|
-
# use {Sass::Plugin.
|
|
227
|
+
# use {Sass::Plugin}.
|
|
227
228
|
#
|
|
228
229
|
# @param template [String] The Sass template.
|
|
229
230
|
# This template can be encoded using any encoding
|
|
@@ -639,7 +640,7 @@ WARNING
|
|
|
639
640
|
# If value begins with url( or ",
|
|
640
641
|
# it's a CSS @import rule and we don't want to touch it.
|
|
641
642
|
if directive == "import"
|
|
642
|
-
parse_import(line, value)
|
|
643
|
+
parse_import(line, value, offset)
|
|
643
644
|
elsif directive == "mixin"
|
|
644
645
|
parse_mixin_definition(line)
|
|
645
646
|
elsif directive == "content"
|
|
@@ -691,9 +692,11 @@ WARNING
|
|
|
691
692
|
:line => @line + 1) unless line.children.empty?
|
|
692
693
|
Tree::CharsetNode.new(name)
|
|
693
694
|
elsif directive == "media"
|
|
694
|
-
|
|
695
|
+
parser = Sass::SCSS::SassParser.new(value, @options[:filename], @line)
|
|
696
|
+
Tree::MediaNode.new(parser.parse_media_query_list)
|
|
695
697
|
else
|
|
696
|
-
Tree::DirectiveNode.new(
|
|
698
|
+
Tree::DirectiveNode.new(
|
|
699
|
+
value.nil? ? ["@#{directive}"] : ["@#{directive} "] + parse_interp(value, offset))
|
|
697
700
|
end
|
|
698
701
|
end
|
|
699
702
|
|
|
@@ -753,7 +756,7 @@ WARNING
|
|
|
753
756
|
nil
|
|
754
757
|
end
|
|
755
758
|
|
|
756
|
-
def parse_import(line, value)
|
|
759
|
+
def parse_import(line, value, offset)
|
|
757
760
|
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
|
|
758
761
|
:line => @line + 1) unless line.children.empty?
|
|
759
762
|
|
|
@@ -761,7 +764,7 @@ WARNING
|
|
|
761
764
|
values = []
|
|
762
765
|
|
|
763
766
|
loop do
|
|
764
|
-
unless node = parse_import_arg(scanner)
|
|
767
|
+
unless node = parse_import_arg(scanner, offset + scanner.pos)
|
|
765
768
|
raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
|
|
766
769
|
:line => @line)
|
|
767
770
|
end
|
|
@@ -777,21 +780,27 @@ WARNING
|
|
|
777
780
|
return values
|
|
778
781
|
end
|
|
779
782
|
|
|
780
|
-
def parse_import_arg(scanner)
|
|
783
|
+
def parse_import_arg(scanner, offset)
|
|
781
784
|
return if scanner.eos?
|
|
782
|
-
|
|
783
|
-
|
|
785
|
+
|
|
786
|
+
if scanner.match?(/url\(/i)
|
|
787
|
+
parser = Sass::Script::Parser.new(scanner, @line, offset, @options)
|
|
788
|
+
str = parser.parse_string
|
|
789
|
+
media = scanner.scan(/\s*[^,;].*/)
|
|
790
|
+
media &&= " #{media}"
|
|
791
|
+
return Tree::DirectiveNode.new(["@import ", str, media || ''])
|
|
792
|
+
end
|
|
793
|
+
|
|
794
|
+
unless str = scanner.scan(Sass::SCSS::RX::STRING)
|
|
784
795
|
return Tree::ImportNode.new(scanner.scan(/[^,;]+/))
|
|
785
796
|
end
|
|
786
797
|
|
|
787
798
|
val = scanner[1] || scanner[2]
|
|
788
799
|
scanner.scan(/\s*/)
|
|
789
800
|
if media = scanner.scan(/[^,;].*/)
|
|
790
|
-
Tree::DirectiveNode.new("@import #{str || uri} #{media}")
|
|
791
|
-
elsif uri
|
|
792
|
-
Tree::DirectiveNode.new("@import #{uri}")
|
|
801
|
+
Tree::DirectiveNode.new(["@import #{str || uri} #{media}"])
|
|
793
802
|
elsif val =~ /^http:\/\//
|
|
794
|
-
Tree::DirectiveNode.new("@import url(#{val})")
|
|
803
|
+
Tree::DirectiveNode.new(["@import url(#{val})"])
|
|
795
804
|
else
|
|
796
805
|
Tree::ImportNode.new(val)
|
|
797
806
|
end
|
data/lib/sass/media.rb
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# A namespace for the `@media` query parse tree.
|
|
2
|
+
module Sass::Media
|
|
3
|
+
# A comma-separated list of queries.
|
|
4
|
+
#
|
|
5
|
+
# media_query [ ',' S* media_query ]*
|
|
6
|
+
class QueryList
|
|
7
|
+
# The queries contained in this list.
|
|
8
|
+
#
|
|
9
|
+
# @return [Array<Query>]
|
|
10
|
+
attr_accessor :queries
|
|
11
|
+
|
|
12
|
+
# @param queries [Array<Query>] See \{#queries}
|
|
13
|
+
def initialize(queries)
|
|
14
|
+
@queries = queries
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Runs the SassScript in the media query list.
|
|
18
|
+
#
|
|
19
|
+
# @yield [interp] A block that should perform interpolation.
|
|
20
|
+
# @yieldparam interp [Array<String, Sass::Script::Node>]
|
|
21
|
+
# An interpolation array to perform.
|
|
22
|
+
# @yieldreturn [String] The interpolated value.
|
|
23
|
+
def perform(&run_interp)
|
|
24
|
+
@queries.each {|q| q.perform(&run_interp)}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Merges this query list with another. The returned query list
|
|
28
|
+
# queries for the intersection between the two inputs.
|
|
29
|
+
#
|
|
30
|
+
# Both query lists should be resolved.
|
|
31
|
+
#
|
|
32
|
+
# @param other [QueryList]
|
|
33
|
+
# @return [QueryList?] The merged list, or nil if there is no intersection.
|
|
34
|
+
def merge(other)
|
|
35
|
+
new_queries = queries.map {|q1| other.queries.map {|q2| q1.merge(q2)}}.flatten.compact
|
|
36
|
+
return if new_queries.empty?
|
|
37
|
+
QueryList.new(new_queries)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the CSS for the media query list.
|
|
41
|
+
#
|
|
42
|
+
# @return [String]
|
|
43
|
+
def to_css
|
|
44
|
+
queries.map {|q| q.to_css}.join(', ')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Returns the Sass/SCSS code for the media query list.
|
|
48
|
+
#
|
|
49
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
50
|
+
# @return [String]
|
|
51
|
+
def to_src(options)
|
|
52
|
+
queries.map {|q| q.to_src(options)}.join(', ')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Returns a deep copy of this query list and all its children.
|
|
56
|
+
#
|
|
57
|
+
# @return [QueryList]
|
|
58
|
+
def deep_copy
|
|
59
|
+
QueryList.new(queries.map {|q| q.deep_copy})
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Sets the options hash for the script nodes in the media query.
|
|
63
|
+
#
|
|
64
|
+
# @param options [{Symbol => Object}] The options has to set.
|
|
65
|
+
def options=(options)
|
|
66
|
+
queries.each {|q| q.options = options}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# A single media query.
|
|
71
|
+
#
|
|
72
|
+
# [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
|
|
73
|
+
class Query
|
|
74
|
+
# The modifier for the query.
|
|
75
|
+
#
|
|
76
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
77
|
+
attr_accessor :modifier
|
|
78
|
+
|
|
79
|
+
# The modifier for the query after any SassScript has been resolved.
|
|
80
|
+
# Only set once \{Tree::Visitors::Perform} has been run.
|
|
81
|
+
#
|
|
82
|
+
# @return [String]
|
|
83
|
+
attr_accessor :resolved_modifier
|
|
84
|
+
|
|
85
|
+
# The type of the query (e.g. `"screen"` or `"print"`).
|
|
86
|
+
#
|
|
87
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
88
|
+
attr_accessor :type
|
|
89
|
+
|
|
90
|
+
# The type of the query after any SassScript has been resolved.
|
|
91
|
+
# Only set once \{Tree::Visitors::Perform} has been run.
|
|
92
|
+
#
|
|
93
|
+
# @return [String]
|
|
94
|
+
attr_accessor :resolved_type
|
|
95
|
+
|
|
96
|
+
# The trailing expressions in the query.
|
|
97
|
+
#
|
|
98
|
+
# @return [Array<Expression>]
|
|
99
|
+
attr_accessor :expressions
|
|
100
|
+
|
|
101
|
+
# @param modifier [Array<String, Sass::Script::Node>] See \{#modifier}
|
|
102
|
+
# @param type [Array<String, Sass::Script::Node>] See \{#type}
|
|
103
|
+
# @param expressions [Array<Expression>] See \{#expressions}
|
|
104
|
+
def initialize(modifier, type, expressions)
|
|
105
|
+
@modifier = modifier
|
|
106
|
+
@type = type
|
|
107
|
+
@expressions = expressions
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Runs the SassScript in the media query.
|
|
111
|
+
#
|
|
112
|
+
# @yield [interp] A block that should perform interpolation.
|
|
113
|
+
# @yieldparam interp [Array<String, Sass::Script::Node>]
|
|
114
|
+
# An interpolation array to perform.
|
|
115
|
+
# @yieldreturn [String] The interpolated value.
|
|
116
|
+
def perform(&run_interp)
|
|
117
|
+
@resolved_modifier = yield modifier
|
|
118
|
+
@resolved_type = yield type
|
|
119
|
+
expressions.each {|e| e.perform(&run_interp)}
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Merges this query with another. The returned query queries for
|
|
123
|
+
# the intersection between the two inputs.
|
|
124
|
+
#
|
|
125
|
+
# Both queries should be resolved.
|
|
126
|
+
#
|
|
127
|
+
# @param other [Query]
|
|
128
|
+
# @return [Query?] The merged query, or nil if there is no intersection.
|
|
129
|
+
def merge(other)
|
|
130
|
+
m1, t1 = resolved_modifier.downcase, resolved_type.downcase
|
|
131
|
+
m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
|
|
132
|
+
t1 = t2 if t1.empty?
|
|
133
|
+
t2 = t1 if t2.empty?
|
|
134
|
+
if ((m1 == 'not') ^ (m2 == 'not'))
|
|
135
|
+
return if t1 == t2
|
|
136
|
+
type = m1 == 'not' ? t2 : t1
|
|
137
|
+
mod = m1 == 'not' ? m2 : m1
|
|
138
|
+
elsif m1 == 'not' && m2 == 'not'
|
|
139
|
+
# CSS has no way of representing "neither screen nor print"
|
|
140
|
+
return unless t1 == t2
|
|
141
|
+
type = t1
|
|
142
|
+
mod = 'not'
|
|
143
|
+
elsif t1 != t2
|
|
144
|
+
return
|
|
145
|
+
else # t1 == t2, neither m1 nor m2 are "not"
|
|
146
|
+
type = t1
|
|
147
|
+
mod = m1.empty? ? m2 : m1
|
|
148
|
+
end
|
|
149
|
+
q = Query.new([], [], other.expressions + expressions)
|
|
150
|
+
q.resolved_type = type
|
|
151
|
+
q.resolved_modifier = mod
|
|
152
|
+
return q
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Returns the CSS for the media query.
|
|
156
|
+
#
|
|
157
|
+
# @return [String]
|
|
158
|
+
def to_css
|
|
159
|
+
css = ''
|
|
160
|
+
css << resolved_modifier
|
|
161
|
+
css << ' ' unless resolved_modifier.empty?
|
|
162
|
+
css << resolved_type
|
|
163
|
+
css << ' and ' unless resolved_type.empty? || expressions.empty?
|
|
164
|
+
css << expressions.map {|e| e.to_css}.join(' and ')
|
|
165
|
+
css
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns the Sass/SCSS code for the media query.
|
|
169
|
+
#
|
|
170
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
171
|
+
# @return [String]
|
|
172
|
+
def to_src(options)
|
|
173
|
+
src = ''
|
|
174
|
+
src << Sass::Media._interp_or_var_to_src(modifier, options)
|
|
175
|
+
src << ' ' unless modifier.empty?
|
|
176
|
+
src << Sass::Media._interp_or_var_to_src(type, options)
|
|
177
|
+
src << ' and ' unless type.empty? || expressions.empty?
|
|
178
|
+
src << expressions.map {|e| e.to_src(options)}.join(' and ')
|
|
179
|
+
src
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Returns a deep copy of this query and all its children.
|
|
183
|
+
#
|
|
184
|
+
# @return [Query]
|
|
185
|
+
def deep_copy
|
|
186
|
+
Query.new(
|
|
187
|
+
modifier.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
|
188
|
+
type.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
|
189
|
+
expressions.map {|q| q.deep_copy})
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Sets the options hash for the script nodes in the media query.
|
|
193
|
+
#
|
|
194
|
+
# @param options [{Symbol => Object}] The options has to set.
|
|
195
|
+
def options=(options)
|
|
196
|
+
modifier.each {|m| m.options = options if m.is_a?(Sass::Script::Node)}
|
|
197
|
+
type.each {|t| t.options = options if t.is_a?(Sass::Script::Node)}
|
|
198
|
+
expressions.each {|e| e.options = options}
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# A media query expression.
|
|
203
|
+
#
|
|
204
|
+
# '(' S* media_feature S* [ ':' S* expr ]? ')'
|
|
205
|
+
class Expression
|
|
206
|
+
# The name of the feature being queried for.
|
|
207
|
+
#
|
|
208
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
209
|
+
attr_accessor :name
|
|
210
|
+
|
|
211
|
+
# The name of the feature after any SassScript has been resolved.
|
|
212
|
+
# Only set once \{Tree::Visitors::Perform} has been run.
|
|
213
|
+
#
|
|
214
|
+
# @return [String]
|
|
215
|
+
attr_accessor :resolved_name
|
|
216
|
+
|
|
217
|
+
# The value of the feature.
|
|
218
|
+
#
|
|
219
|
+
# @return [Array<String, Sass::Script::Node>]
|
|
220
|
+
attr_accessor :value
|
|
221
|
+
|
|
222
|
+
# The value of the feature after any SassScript has been resolved.
|
|
223
|
+
# Only set once \{Tree::Visitors::Perform} has been run.
|
|
224
|
+
#
|
|
225
|
+
# @return [String]
|
|
226
|
+
attr_accessor :resolved_value
|
|
227
|
+
|
|
228
|
+
# @param name [Array<String, Sass::Script::Node>] See \{#name}
|
|
229
|
+
# @param value [Array<String, Sass::Script::Node>] See \{#value}
|
|
230
|
+
def initialize(name, value)
|
|
231
|
+
@name = name
|
|
232
|
+
@value = value
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# Runs the SassScript in the expression.
|
|
236
|
+
#
|
|
237
|
+
# @yield [interp] A block that should perform interpolation.
|
|
238
|
+
# @yieldparam interp [Array<String, Sass::Script::Node>]
|
|
239
|
+
# An interpolation array to perform.
|
|
240
|
+
# @yieldreturn [String] The interpolated value.
|
|
241
|
+
def perform
|
|
242
|
+
@resolved_name = yield name
|
|
243
|
+
@resolved_value = yield value
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Returns the CSS for the expression.
|
|
247
|
+
#
|
|
248
|
+
# @return [String]
|
|
249
|
+
def to_css
|
|
250
|
+
css = '('
|
|
251
|
+
css << resolved_name
|
|
252
|
+
css << ': ' << resolved_value unless resolved_value.empty?
|
|
253
|
+
css << ')'
|
|
254
|
+
css
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Returns the Sass/SCSS code for the expression.
|
|
258
|
+
#
|
|
259
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
260
|
+
# @return [String]
|
|
261
|
+
def to_src(options)
|
|
262
|
+
src = '('
|
|
263
|
+
src << Sass::Media._interp_or_var_to_src(name, options)
|
|
264
|
+
src << ': ' << Sass::Media._interp_or_var_to_src(value, options) unless value.empty?
|
|
265
|
+
src << ')'
|
|
266
|
+
src
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Returns a deep copy of this expression.
|
|
270
|
+
#
|
|
271
|
+
# @return [Expression]
|
|
272
|
+
def deep_copy
|
|
273
|
+
Expression.new(
|
|
274
|
+
name.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
|
|
275
|
+
value.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c})
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Sets the options hash for the script nodes in the expression.
|
|
279
|
+
#
|
|
280
|
+
# @param options [{Symbol => Object}] The options has to set.
|
|
281
|
+
def options=(options)
|
|
282
|
+
name.each {|n| n.options = options if n.is_a?(Sass::Script::Node)}
|
|
283
|
+
value.each {|v| v.options = options if v.is_a?(Sass::Script::Node)}
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Converts an interpolation array that may represent a single variable to source.
|
|
288
|
+
#
|
|
289
|
+
# @param [Array<String, Sass::Script::Node>] The interpolation array to convert.
|
|
290
|
+
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
|
|
291
|
+
# @return [String]
|
|
292
|
+
def self._interp_or_var_to_src(interp, options)
|
|
293
|
+
interp = interp.reject {|v| v.is_a?(String) && v.empty?}
|
|
294
|
+
return interp[0].to_sass(options) if interp.length == 1 && interp[0].is_a?(Sass::Script::Variable)
|
|
295
|
+
interp.map do |r|
|
|
296
|
+
next r if r.is_a?(String)
|
|
297
|
+
"\#{#{r.to_sass(options)}}"
|
|
298
|
+
end.join
|
|
299
|
+
end
|
|
300
|
+
end
|
data/lib/sass/script/funcall.rb
CHANGED
|
@@ -36,7 +36,7 @@ module Sass
|
|
|
36
36
|
# @return [String] A string representation of the function call
|
|
37
37
|
def inspect
|
|
38
38
|
args = @args.map {|a| a.inspect}.join(', ')
|
|
39
|
-
keywords = @keywords.
|
|
39
|
+
keywords = Sass::Util.hash_to_a(@keywords).
|
|
40
40
|
map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
|
|
41
41
|
"#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
|
42
42
|
end
|
|
@@ -44,7 +44,7 @@ module Sass
|
|
|
44
44
|
# @see Node#to_sass
|
|
45
45
|
def to_sass(opts = {})
|
|
46
46
|
args = @args.map {|a| a.to_sass(opts)}.join(', ')
|
|
47
|
-
keywords = @keywords.
|
|
47
|
+
keywords = Sass::Util.hash_to_a(@keywords).
|
|
48
48
|
map {|k, v| "$#{dasherize(k, opts)}: #{v.to_sass(opts)}"}.join(', ')
|
|
49
49
|
"#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords})"
|
|
50
50
|
end
|
data/lib/sass/script/literal.rb
CHANGED
|
@@ -55,26 +55,6 @@ The #options attribute is not set on this #{self.class}.
|
|
|
55
55
|
MSG
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
# The SassScript `and` operation.
|
|
59
|
-
#
|
|
60
|
-
# @param other [Literal] The right-hand side of the operator
|
|
61
|
-
# @return [Literal] The result of a logical and:
|
|
62
|
-
# `other` if this literal isn't a false {Bool},
|
|
63
|
-
# and this literal otherwise
|
|
64
|
-
def and(other)
|
|
65
|
-
to_bool ? other : self
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# The SassScript `or` operation.
|
|
69
|
-
#
|
|
70
|
-
# @param other [Literal] The right-hand side of the operator
|
|
71
|
-
# @return [Literal] The result of the logical or:
|
|
72
|
-
# this literal if it isn't a false {Bool},
|
|
73
|
-
# and `other` otherwise
|
|
74
|
-
def or(other)
|
|
75
|
-
to_bool ? self : other
|
|
76
|
-
end
|
|
77
|
-
|
|
78
58
|
# The SassScript `==` operation.
|
|
79
59
|
# **Note that this returns a {Sass::Script::Bool} object,
|
|
80
60
|
# not a Ruby boolean**.
|
|
@@ -72,6 +72,14 @@ module Sass::Script
|
|
|
72
72
|
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
|
|
73
73
|
def _perform(environment)
|
|
74
74
|
literal1 = @operand1.perform(environment)
|
|
75
|
+
|
|
76
|
+
# Special-case :and and :or to support short-circuiting.
|
|
77
|
+
if @operator == :and
|
|
78
|
+
return literal1.to_bool ? @operand2.perform(environment) : literal1
|
|
79
|
+
elsif @operator == :or
|
|
80
|
+
return literal1.to_bool ? literal1 : @operand2.perform(environment)
|
|
81
|
+
end
|
|
82
|
+
|
|
75
83
|
literal2 = @operand2.perform(environment)
|
|
76
84
|
|
|
77
85
|
begin
|