sass 3.2.19 → 3.4.25
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +3 -1
- data/CODE_OF_CONDUCT.md +10 -0
- data/CONTRIBUTING.md +148 -0
- data/MIT-LICENSE +1 -1
- data/README.md +87 -61
- data/Rakefile +119 -15
- data/VERSION +1 -1
- data/VERSION_DATE +1 -1
- data/VERSION_NAME +1 -1
- data/bin/sass +1 -1
- data/bin/scss +1 -1
- data/extra/sass-spec-ref.sh +32 -0
- data/extra/update_watch.rb +1 -1
- data/lib/sass/cache_stores/base.rb +2 -2
- data/lib/sass/cache_stores/chain.rb +2 -1
- data/lib/sass/cache_stores/filesystem.rb +8 -12
- data/lib/sass/cache_stores/memory.rb +5 -6
- data/lib/sass/cache_stores/null.rb +2 -2
- data/lib/sass/callbacks.rb +3 -2
- data/lib/sass/css.rb +22 -23
- data/lib/sass/deprecation.rb +55 -0
- data/lib/sass/engine.rb +487 -191
- data/lib/sass/environment.rb +172 -58
- data/lib/sass/error.rb +21 -24
- data/lib/sass/exec/base.rb +199 -0
- data/lib/sass/exec/sass_convert.rb +283 -0
- data/lib/sass/exec/sass_scss.rb +440 -0
- data/lib/sass/exec.rb +5 -703
- data/lib/sass/features.rb +47 -0
- data/lib/sass/importers/base.rb +50 -7
- data/lib/sass/importers/deprecated_path.rb +51 -0
- data/lib/sass/importers/filesystem.rb +54 -21
- data/lib/sass/importers.rb +1 -0
- data/lib/sass/logger/base.rb +9 -5
- data/lib/sass/logger/delayed.rb +50 -0
- data/lib/sass/logger/log_level.rb +3 -7
- data/lib/sass/logger.rb +9 -7
- data/lib/sass/media.rb +20 -23
- data/lib/sass/plugin/compiler.rb +321 -145
- data/lib/sass/plugin/configuration.rb +45 -34
- data/lib/sass/plugin/merb.rb +3 -3
- data/lib/sass/plugin/rack.rb +3 -3
- data/lib/sass/plugin/rails.rb +1 -1
- data/lib/sass/plugin/staleness_checker.rb +6 -6
- data/lib/sass/plugin.rb +9 -8
- data/lib/sass/repl.rb +3 -3
- data/lib/sass/script/css_lexer.rb +8 -4
- data/lib/sass/script/css_parser.rb +4 -2
- data/lib/sass/script/css_variable_warning.rb +52 -0
- data/lib/sass/script/functions.rb +1583 -433
- data/lib/sass/script/lexer.rb +198 -79
- data/lib/sass/script/parser.rb +463 -133
- data/lib/sass/script/tree/funcall.rb +313 -0
- data/lib/sass/script/tree/interpolation.rb +223 -0
- data/lib/sass/script/tree/list_literal.rb +104 -0
- data/lib/sass/script/tree/literal.rb +49 -0
- data/lib/sass/script/tree/map_literal.rb +64 -0
- data/lib/sass/script/{node.rb → tree/node.rb} +42 -14
- data/lib/sass/script/tree/operation.rb +156 -0
- data/lib/sass/script/tree/selector.rb +26 -0
- data/lib/sass/script/tree/string_interpolation.rb +125 -0
- data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +6 -6
- data/lib/sass/script/tree/variable.rb +57 -0
- data/lib/sass/script/tree.rb +16 -0
- data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +9 -25
- data/lib/sass/script/value/base.rb +241 -0
- data/lib/sass/script/value/bool.rb +35 -0
- data/lib/sass/script/value/color.rb +698 -0
- data/lib/sass/script/value/helpers.rb +272 -0
- data/lib/sass/script/value/list.rb +113 -0
- data/lib/sass/script/value/map.rb +70 -0
- data/lib/sass/script/{null.rb → value/null.rb} +14 -7
- data/lib/sass/script/{number.rb → value/number.rb} +196 -86
- data/lib/sass/script/value/string.rb +138 -0
- data/lib/sass/script/value.rb +11 -0
- data/lib/sass/script.rb +38 -11
- data/lib/sass/scss/css_parser.rb +25 -5
- data/lib/sass/scss/parser.rb +532 -458
- data/lib/sass/scss/rx.rb +21 -14
- data/lib/sass/scss/static_parser.rb +328 -9
- data/lib/sass/scss.rb +0 -2
- data/lib/sass/selector/abstract_sequence.rb +36 -19
- data/lib/sass/selector/comma_sequence.rb +125 -26
- data/lib/sass/selector/pseudo.rb +266 -0
- data/lib/sass/selector/sequence.rb +200 -71
- data/lib/sass/selector/simple.rb +30 -32
- data/lib/sass/selector/simple_sequence.rb +193 -64
- data/lib/sass/selector.rb +65 -194
- data/lib/sass/shared.rb +2 -2
- data/lib/sass/source/map.rb +213 -0
- data/lib/sass/source/position.rb +39 -0
- data/lib/sass/source/range.rb +41 -0
- data/lib/sass/stack.rb +120 -0
- data/lib/sass/supports.rb +19 -23
- data/lib/sass/tree/at_root_node.rb +83 -0
- data/lib/sass/tree/charset_node.rb +1 -1
- data/lib/sass/tree/comment_node.rb +4 -4
- data/lib/sass/tree/css_import_node.rb +19 -11
- data/lib/sass/tree/debug_node.rb +2 -2
- data/lib/sass/tree/directive_node.rb +21 -4
- data/lib/sass/tree/each_node.rb +8 -8
- data/lib/sass/tree/error_node.rb +18 -0
- data/lib/sass/tree/extend_node.rb +14 -7
- data/lib/sass/tree/for_node.rb +4 -4
- data/lib/sass/tree/function_node.rb +14 -4
- data/lib/sass/tree/if_node.rb +1 -1
- data/lib/sass/tree/import_node.rb +10 -10
- data/lib/sass/tree/keyframe_rule_node.rb +15 -0
- data/lib/sass/tree/media_node.rb +4 -14
- data/lib/sass/tree/mixin_def_node.rb +4 -4
- data/lib/sass/tree/mixin_node.rb +21 -8
- data/lib/sass/tree/node.rb +59 -15
- data/lib/sass/tree/prop_node.rb +42 -24
- data/lib/sass/tree/return_node.rb +3 -2
- data/lib/sass/tree/root_node.rb +19 -3
- data/lib/sass/tree/rule_node.rb +49 -26
- data/lib/sass/tree/supports_node.rb +0 -13
- data/lib/sass/tree/trace_node.rb +2 -1
- data/lib/sass/tree/variable_node.rb +9 -3
- data/lib/sass/tree/visitors/base.rb +5 -8
- data/lib/sass/tree/visitors/check_nesting.rb +62 -36
- data/lib/sass/tree/visitors/convert.rb +111 -76
- data/lib/sass/tree/visitors/cssize.rb +206 -74
- data/lib/sass/tree/visitors/deep_copy.rb +11 -6
- data/lib/sass/tree/visitors/extend.rb +19 -17
- data/lib/sass/tree/visitors/perform.rb +308 -190
- data/lib/sass/tree/visitors/set_options.rb +21 -7
- data/lib/sass/tree/visitors/to_css.rb +273 -92
- data/lib/sass/tree/warn_node.rb +2 -2
- data/lib/sass/tree/while_node.rb +2 -2
- data/lib/sass/util/cross_platform_random.rb +19 -0
- data/lib/sass/util/normalized_map.rb +129 -0
- data/lib/sass/util/ordered_hash.rb +192 -0
- data/lib/sass/util/subset_map.rb +5 -5
- data/lib/sass/util/test.rb +0 -1
- data/lib/sass/util.rb +620 -193
- data/lib/sass/version.rb +22 -24
- data/lib/sass.rb +27 -13
- data/test/sass/cache_test.rb +62 -20
- data/test/sass/callbacks_test.rb +1 -1
- data/test/sass/compiler_test.rb +236 -0
- data/test/sass/conversion_test.rb +472 -44
- data/test/sass/css2sass_test.rb +73 -5
- data/test/sass/css_variable_test.rb +132 -0
- data/test/sass/encoding_test.rb +219 -0
- data/test/sass/engine_test.rb +618 -415
- data/test/sass/exec_test.rb +12 -2
- data/test/sass/extend_test.rb +419 -168
- data/test/sass/functions_test.rb +931 -93
- data/test/sass/importer_test.rb +250 -21
- data/test/sass/logger_test.rb +1 -1
- data/test/sass/more_results/more_import.css +1 -1
- data/test/sass/more_templates/more1.sass +10 -10
- data/test/sass/more_templates/more_import.sass +2 -2
- data/test/sass/plugin_test.rb +26 -34
- data/test/sass/results/compact.css +1 -1
- data/test/sass/results/complex.css +4 -4
- data/test/sass/results/expanded.css +1 -1
- data/test/sass/results/import.css +1 -1
- data/test/sass/results/import_charset_ibm866.css +2 -2
- data/test/sass/results/mixins.css +17 -17
- data/test/sass/results/nested.css +1 -1
- data/test/sass/results/parent_ref.css +2 -2
- data/test/sass/results/script.css +5 -5
- data/test/sass/results/scss_import.css +1 -1
- data/test/sass/script_conversion_test.rb +97 -39
- data/test/sass/script_test.rb +911 -102
- data/test/sass/scss/css_test.rb +215 -34
- data/test/sass/scss/rx_test.rb +8 -4
- data/test/sass/scss/scss_test.rb +2424 -325
- data/test/sass/source_map_test.rb +1055 -0
- data/test/sass/superselector_test.rb +210 -0
- data/test/sass/templates/_partial.sass +1 -1
- data/test/sass/templates/basic.sass +10 -10
- data/test/sass/templates/bork1.sass +1 -1
- data/test/sass/templates/bork5.sass +1 -1
- data/test/sass/templates/compact.sass +10 -10
- data/test/sass/templates/complex.sass +187 -187
- data/test/sass/templates/compressed.sass +10 -10
- data/test/sass/templates/expanded.sass +10 -10
- data/test/sass/templates/import.sass +2 -2
- data/test/sass/templates/importee.sass +3 -3
- data/test/sass/templates/mixins.sass +22 -22
- data/test/sass/templates/multiline.sass +4 -4
- data/test/sass/templates/nested.sass +13 -13
- data/test/sass/templates/parent_ref.sass +12 -12
- data/test/sass/templates/script.sass +70 -70
- data/test/sass/templates/scss_import.scss +2 -1
- data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +1 -1
- data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +2 -2
- data/test/sass/templates/subdir/subdir.sass +3 -3
- data/test/sass/templates/units.sass +10 -10
- data/test/sass/test_helper.rb +1 -1
- data/test/sass/util/multibyte_string_scanner_test.rb +11 -3
- data/test/sass/util/normalized_map_test.rb +51 -0
- data/test/sass/util/subset_map_test.rb +2 -2
- data/test/sass/util_test.rb +99 -43
- data/test/sass/value_helpers_test.rb +179 -0
- data/test/sass-spec.yml +3 -0
- data/test/test_helper.rb +42 -12
- data/vendor/listen/CHANGELOG.md +1 -228
- data/vendor/listen/Gemfile +5 -15
- data/vendor/listen/README.md +111 -77
- data/vendor/listen/Rakefile +0 -42
- data/vendor/listen/lib/listen/adapter.rb +195 -82
- data/vendor/listen/lib/listen/adapters/bsd.rb +27 -64
- data/vendor/listen/lib/listen/adapters/darwin.rb +21 -58
- data/vendor/listen/lib/listen/adapters/linux.rb +23 -55
- data/vendor/listen/lib/listen/adapters/polling.rb +25 -34
- data/vendor/listen/lib/listen/adapters/windows.rb +50 -46
- data/vendor/listen/lib/listen/directory_record.rb +96 -61
- data/vendor/listen/lib/listen/listener.rb +135 -37
- data/vendor/listen/lib/listen/turnstile.rb +9 -5
- data/vendor/listen/lib/listen/version.rb +1 -1
- data/vendor/listen/lib/listen.rb +33 -19
- data/vendor/listen/listen.gemspec +6 -0
- data/vendor/listen/spec/listen/adapter_spec.rb +43 -77
- data/vendor/listen/spec/listen/adapters/polling_spec.rb +8 -8
- data/vendor/listen/spec/listen/directory_record_spec.rb +81 -56
- data/vendor/listen/spec/listen/listener_spec.rb +128 -39
- data/vendor/listen/spec/listen_spec.rb +15 -21
- data/vendor/listen/spec/spec_helper.rb +4 -0
- data/vendor/listen/spec/support/adapter_helper.rb +52 -15
- data/vendor/listen/spec/support/directory_record_helper.rb +7 -5
- data/vendor/listen/spec/support/listeners_helper.rb +30 -7
- metadata +161 -111
- data/CONTRIBUTING +0 -3
- data/lib/sass/script/bool.rb +0 -18
- data/lib/sass/script/color.rb +0 -606
- data/lib/sass/script/funcall.rb +0 -245
- data/lib/sass/script/interpolation.rb +0 -79
- data/lib/sass/script/list.rb +0 -85
- data/lib/sass/script/literal.rb +0 -221
- data/lib/sass/script/operation.rb +0 -110
- data/lib/sass/script/string.rb +0 -51
- data/lib/sass/script/string_interpolation.rb +0 -103
- data/lib/sass/script/variable.rb +0 -58
- data/lib/sass/scss/script_lexer.rb +0 -15
- data/lib/sass/scss/script_parser.rb +0 -25
- data/test/Gemfile +0 -3
- data/test/Gemfile.lock +0 -10
- data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
- data/vendor/listen/lib/listen/multi_listener.rb +0 -143
- data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
- data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module Sass::Script
|
1
|
+
module Sass::Script::Value
|
4
2
|
# A SassScript object representing a number.
|
5
3
|
# SassScript numbers can have decimal values,
|
6
4
|
# and can also have units.
|
@@ -9,7 +7,7 @@ module Sass::Script
|
|
9
7
|
#
|
10
8
|
# Numbers can also have more complex units, such as `1px*em/in`.
|
11
9
|
# These cannot be inputted directly in Sass code at the moment.
|
12
|
-
class Number <
|
10
|
+
class Number < Base
|
13
11
|
# The Ruby value of the number.
|
14
12
|
#
|
15
13
|
# @return [Numeric]
|
@@ -17,7 +15,7 @@ module Sass::Script
|
|
17
15
|
|
18
16
|
# A list of units in the numerator of the number.
|
19
17
|
# For example, `1px*em/in*cm` would return `["px", "em"]`
|
20
|
-
# @return [Array<String>]
|
18
|
+
# @return [Array<String>]
|
21
19
|
attr_reader :numerator_units
|
22
20
|
|
23
21
|
# A list of units in the denominator of the number.
|
@@ -36,44 +34,46 @@ module Sass::Script
|
|
36
34
|
attr_accessor :original
|
37
35
|
|
38
36
|
def self.precision
|
39
|
-
|
37
|
+
Thread.current[:sass_numeric_precision] || Thread.main[:sass_numeric_precision] || 5
|
40
38
|
end
|
41
39
|
|
42
40
|
# Sets the number of digits of precision
|
43
41
|
# For example, if this is `3`,
|
44
42
|
# `3.1415926` will be printed as `3.142`.
|
43
|
+
# The numeric precision is stored as a thread local for thread safety reasons.
|
44
|
+
# To set for all threads, be sure to set the precision on the main thread.
|
45
45
|
def self.precision=(digits)
|
46
|
-
|
47
|
-
|
46
|
+
Thread.current[:sass_numeric_precision] = digits.round
|
47
|
+
Thread.current[:sass_numeric_precision_factor] = nil
|
48
|
+
Thread.current[:sass_numeric_epsilon] = nil
|
48
49
|
end
|
49
50
|
|
50
51
|
# the precision factor used in numeric output
|
51
52
|
# it is derived from the `precision` method.
|
52
53
|
def self.precision_factor
|
53
|
-
|
54
|
+
Thread.current[:sass_numeric_precision_factor] ||= 10.0**precision
|
54
55
|
end
|
55
56
|
|
56
|
-
#
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
const_set(:PRECISION, self.precision_factor)
|
62
|
-
else
|
63
|
-
super
|
64
|
-
end
|
57
|
+
# Used in checking equality of floating point numbers. Any
|
58
|
+
# numbers within an `epsilon` of each other are considered functionally equal.
|
59
|
+
# The value for epsilon is one tenth of the current numeric precision.
|
60
|
+
def self.epsilon
|
61
|
+
Thread.current[:sass_numeric_epsilon] ||= 1 / (precision_factor * 10)
|
65
62
|
end
|
66
63
|
|
67
64
|
# Used so we don't allocate two new arrays for each new number.
|
68
|
-
NO_UNITS
|
65
|
+
NO_UNITS = []
|
69
66
|
|
70
67
|
# @param value [Numeric] The value of the number
|
71
|
-
# @param numerator_units [Array
|
72
|
-
# @param denominator_units [Array
|
68
|
+
# @param numerator_units [::String, Array<::String>] See \{#numerator\_units}
|
69
|
+
# @param denominator_units [::String, Array<::String>] See \{#denominator\_units}
|
73
70
|
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
|
71
|
+
numerator_units = [numerator_units] if numerator_units.is_a?(::String)
|
72
|
+
denominator_units = [denominator_units] if denominator_units.is_a?(::String)
|
74
73
|
super(value)
|
75
74
|
@numerator_units = numerator_units
|
76
75
|
@denominator_units = denominator_units
|
76
|
+
@options = nil
|
77
77
|
normalize!
|
78
78
|
end
|
79
79
|
|
@@ -86,11 +86,11 @@ module Sass::Script
|
|
86
86
|
# {Color}
|
87
87
|
# : Adds this number to each of the RGB color channels.
|
88
88
|
#
|
89
|
-
# {
|
90
|
-
# : See {
|
89
|
+
# {Value}
|
90
|
+
# : See {Value::Base#plus}.
|
91
91
|
#
|
92
|
-
# @param other [
|
93
|
-
# @return [
|
92
|
+
# @param other [Value] The right-hand side of the operator
|
93
|
+
# @return [Value] The result of the operation
|
94
94
|
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
|
95
95
|
def plus(other)
|
96
96
|
if other.is_a? Number
|
@@ -108,11 +108,11 @@ module Sass::Script
|
|
108
108
|
# {Number}
|
109
109
|
# : Subtracts this number from the other, converting units if possible.
|
110
110
|
#
|
111
|
-
# {
|
112
|
-
# : See {
|
111
|
+
# {Value}
|
112
|
+
# : See {Value::Base#minus}.
|
113
113
|
#
|
114
|
-
# @param other [
|
115
|
-
# @return [
|
114
|
+
# @param other [Value] The right-hand side of the operator
|
115
|
+
# @return [Value] The result of the operation
|
116
116
|
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
|
117
117
|
def minus(other)
|
118
118
|
if other.is_a? Number
|
@@ -164,16 +164,16 @@ module Sass::Script
|
|
164
164
|
# {Number}
|
165
165
|
# : Divides this number by the other, converting units appropriately.
|
166
166
|
#
|
167
|
-
# {
|
168
|
-
# : See {
|
167
|
+
# {Value}
|
168
|
+
# : See {Value::Base#div}.
|
169
169
|
#
|
170
|
-
# @param other [
|
171
|
-
# @return [
|
170
|
+
# @param other [Value] The right-hand side of the operator
|
171
|
+
# @return [Value] The result of the operation
|
172
172
|
def div(other)
|
173
173
|
if other.is_a? Number
|
174
174
|
res = operate(other, :/)
|
175
|
-
if
|
176
|
-
res.original = "#{
|
175
|
+
if original && other.original
|
176
|
+
res.original = "#{original}/#{other.original}"
|
177
177
|
end
|
178
178
|
res
|
179
179
|
else
|
@@ -186,12 +186,9 @@ module Sass::Script
|
|
186
186
|
# @param other [Number] The right-hand side of the operator
|
187
187
|
# @return [Number] This number modulo the other
|
188
188
|
# @raise [NoMethodError] if `other` is an invalid type
|
189
|
-
# @raise [Sass::UnitConversionError] if `other` has
|
189
|
+
# @raise [Sass::UnitConversionError] if `other` has incompatible units
|
190
190
|
def mod(other)
|
191
191
|
if other.is_a?(Number)
|
192
|
-
unless other.unitless?
|
193
|
-
raise Sass::UnitConversionError.new("Cannot modulo by a number with units: #{other.inspect}.")
|
194
|
-
end
|
195
192
|
operate(other, :%)
|
196
193
|
else
|
197
194
|
raise NoMethodError.new(nil, :mod)
|
@@ -200,10 +197,10 @@ module Sass::Script
|
|
200
197
|
|
201
198
|
# The SassScript `==` operation.
|
202
199
|
#
|
203
|
-
# @param other [
|
200
|
+
# @param other [Value] The right-hand side of the operator
|
204
201
|
# @return [Boolean] Whether this number is equal to the other object
|
205
202
|
def eq(other)
|
206
|
-
return
|
203
|
+
return Bool::FALSE unless other.is_a?(Sass::Script::Value::Number)
|
207
204
|
this = self
|
208
205
|
begin
|
209
206
|
if unitless?
|
@@ -212,10 +209,21 @@ module Sass::Script
|
|
212
209
|
other = other.coerce(@numerator_units, @denominator_units)
|
213
210
|
end
|
214
211
|
rescue Sass::UnitConversionError
|
215
|
-
return
|
212
|
+
return Bool::FALSE
|
216
213
|
end
|
214
|
+
Bool.new(basically_equal?(this.value, other.value))
|
215
|
+
end
|
217
216
|
|
218
|
-
|
217
|
+
def hash
|
218
|
+
[value, numerator_units, denominator_units].hash
|
219
|
+
end
|
220
|
+
|
221
|
+
# Hash-equality works differently than `==` equality for numbers.
|
222
|
+
# Hash-equality must be transitive, so it just compares the exact value,
|
223
|
+
# numerator units, and denominator units.
|
224
|
+
def eql?(other)
|
225
|
+
basically_equal?(value, other.value) && numerator_units == other.numerator_units &&
|
226
|
+
denominator_units == other.denominator_units
|
219
227
|
end
|
220
228
|
|
221
229
|
# The SassScript `>` operation.
|
@@ -274,21 +282,40 @@ module Sass::Script
|
|
274
282
|
#
|
275
283
|
# @return [String] The representation
|
276
284
|
def inspect(opts = {})
|
285
|
+
return original if original
|
286
|
+
|
277
287
|
value = self.class.round(self.value)
|
278
|
-
|
288
|
+
str = value.to_s
|
289
|
+
|
290
|
+
# Ruby will occasionally print in scientific notation if the number is
|
291
|
+
# small enough. That's technically valid CSS, but it's not well-supported
|
292
|
+
# and confusing.
|
293
|
+
str = ("%0.#{self.class.precision}f" % value).gsub(/0*$/, '') if str.include?('e')
|
294
|
+
|
295
|
+
# Sometimes numeric formatting will result in a decimal number with a trailing zero (x.0)
|
296
|
+
if str =~ /(.*)\.0$/
|
297
|
+
str = $1
|
298
|
+
end
|
299
|
+
|
300
|
+
# We omit a leading zero before the decimal point in compressed mode.
|
301
|
+
if @options && options[:style] == :compressed
|
302
|
+
str.sub!(/^(-)?0\./, '\1.')
|
303
|
+
end
|
304
|
+
|
305
|
+
unitless? ? str : "#{str}#{unit_str}"
|
279
306
|
end
|
280
307
|
alias_method :to_sass, :inspect
|
281
308
|
|
282
|
-
# @return [
|
309
|
+
# @return [Integer] The integer value of the number
|
283
310
|
# @raise [Sass::SyntaxError] if the number isn't an integer
|
284
311
|
def to_i
|
285
312
|
super unless int?
|
286
|
-
|
313
|
+
value.to_i
|
287
314
|
end
|
288
315
|
|
289
316
|
# @return [Boolean] Whether or not this number is an integer.
|
290
317
|
def int?
|
291
|
-
value % 1
|
318
|
+
basically_equal?(value % 1, 0.0)
|
292
319
|
end
|
293
320
|
|
294
321
|
# @return [Boolean] Whether or not this number has no units.
|
@@ -296,6 +323,24 @@ module Sass::Script
|
|
296
323
|
@numerator_units.empty? && @denominator_units.empty?
|
297
324
|
end
|
298
325
|
|
326
|
+
# Checks whether the number has the numerator unit specified.
|
327
|
+
#
|
328
|
+
# @example
|
329
|
+
# number = Sass::Script::Value::Number.new(10, "px")
|
330
|
+
# number.is_unit?("px") => true
|
331
|
+
# number.is_unit?(nil) => false
|
332
|
+
#
|
333
|
+
# @param unit [::String, nil] The unit the number should have or nil if the number
|
334
|
+
# should be unitless.
|
335
|
+
# @see Number#unitless? The unitless? method may be more readable.
|
336
|
+
def is_unit?(unit)
|
337
|
+
if unit
|
338
|
+
denominator_units.size == 0 && numerator_units.size == 1 && numerator_units.first == unit
|
339
|
+
else
|
340
|
+
unitless?
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
299
344
|
# @return [Boolean] Whether or not this number has units that can be represented in CSS
|
300
345
|
# (that is, zero or one \{#numerator\_units}).
|
301
346
|
def legal_units?
|
@@ -320,9 +365,9 @@ module Sass::Script
|
|
320
365
|
# current units
|
321
366
|
def coerce(num_units, den_units)
|
322
367
|
Number.new(if unitless?
|
323
|
-
|
368
|
+
value
|
324
369
|
else
|
325
|
-
|
370
|
+
value * coercion_factor(@numerator_units, num_units) /
|
326
371
|
coercion_factor(@denominator_units, den_units)
|
327
372
|
end, num_units, den_units)
|
328
373
|
end
|
@@ -330,12 +375,10 @@ module Sass::Script
|
|
330
375
|
# @param other [Number] A number to decide if it can be compared with this number.
|
331
376
|
# @return [Boolean] Whether or not this number can be compared with the other.
|
332
377
|
def comparable_to?(other)
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
false
|
338
|
-
end
|
378
|
+
operate(other, :+)
|
379
|
+
true
|
380
|
+
rescue Sass::UnitConversionError
|
381
|
+
false
|
339
382
|
end
|
340
383
|
|
341
384
|
# Returns a human readable representation of the units in this number.
|
@@ -353,18 +396,30 @@ module Sass::Script
|
|
353
396
|
|
354
397
|
private
|
355
398
|
|
399
|
+
# @private
|
400
|
+
# @see Sass::Script::Number.basically_equal?
|
401
|
+
def basically_equal?(num1, num2)
|
402
|
+
self.class.basically_equal?(num1, num2)
|
403
|
+
end
|
404
|
+
|
405
|
+
# Checks whether two numbers are within an epsilon of each other.
|
406
|
+
# @return [Boolean]
|
407
|
+
def self.basically_equal?(num1, num2)
|
408
|
+
(num1 - num2).abs < epsilon
|
409
|
+
end
|
410
|
+
|
356
411
|
# @private
|
357
412
|
def self.round(num)
|
358
413
|
if num.is_a?(Float) && (num.infinite? || num.nan?)
|
359
414
|
num
|
360
|
-
elsif num % 1
|
361
|
-
num.
|
415
|
+
elsif basically_equal?(num % 1, 0.0)
|
416
|
+
num.round
|
362
417
|
else
|
363
|
-
((num *
|
418
|
+
((num * precision_factor).round / precision_factor).to_f
|
364
419
|
end
|
365
420
|
end
|
366
421
|
|
367
|
-
OPERATIONS = [:+, :-, :<=, :<, :>,
|
422
|
+
OPERATIONS = [:+, :-, :<=, :<, :>, :>=, :%]
|
368
423
|
|
369
424
|
def operate(other, operation)
|
370
425
|
this = self
|
@@ -376,7 +431,7 @@ module Sass::Script
|
|
376
431
|
end
|
377
432
|
end
|
378
433
|
# avoid integer division
|
379
|
-
value =
|
434
|
+
value = :/ == operation ? this.value.to_f : this.value
|
380
435
|
result = value.send(operation, other.value)
|
381
436
|
|
382
437
|
if result.is_a?(Numeric)
|
@@ -391,63 +446,118 @@ module Sass::Script
|
|
391
446
|
from_units, to_units = sans_common_units(from_units, to_units)
|
392
447
|
|
393
448
|
if from_units.size != to_units.size || !convertable?(from_units | to_units)
|
394
|
-
raise Sass::UnitConversionError.new(
|
449
|
+
raise Sass::UnitConversionError.new(
|
450
|
+
"Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
|
395
451
|
end
|
396
452
|
|
397
|
-
from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1])
|
453
|
+
from_units.zip(to_units).inject(1) {|m, p| m * conversion_factor(p[0], p[1])}
|
398
454
|
end
|
399
455
|
|
400
456
|
def compute_units(this, other, operation)
|
401
457
|
case operation
|
402
458
|
when :*
|
403
|
-
[this.numerator_units + other.numerator_units,
|
459
|
+
[this.numerator_units + other.numerator_units,
|
460
|
+
this.denominator_units + other.denominator_units]
|
404
461
|
when :/
|
405
|
-
[this.numerator_units + other.denominator_units,
|
406
|
-
|
462
|
+
[this.numerator_units + other.denominator_units,
|
463
|
+
this.denominator_units + other.numerator_units]
|
464
|
+
else
|
407
465
|
[this.numerator_units, this.denominator_units]
|
408
466
|
end
|
409
467
|
end
|
410
468
|
|
411
469
|
def normalize!
|
412
470
|
return if unitless?
|
413
|
-
@numerator_units, @denominator_units =
|
471
|
+
@numerator_units, @denominator_units =
|
472
|
+
sans_common_units(@numerator_units, @denominator_units)
|
414
473
|
|
415
474
|
@denominator_units.each_with_index do |d, i|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
end
|
475
|
+
next unless convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
|
476
|
+
@value /= conversion_factor(d, u)
|
477
|
+
@denominator_units.delete_at(i)
|
478
|
+
@numerator_units.delete_at(@numerator_units.index(u))
|
421
479
|
end
|
422
480
|
end
|
423
481
|
|
424
|
-
#
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
482
|
+
# This is the source data for all the unit logic. It's pre-processed to make
|
483
|
+
# it efficient to figure out whether a set of units is mutually compatible
|
484
|
+
# and what the conversion ratio is between two units.
|
485
|
+
#
|
486
|
+
# These come from http://www.w3.org/TR/2012/WD-css3-values-20120308/.
|
487
|
+
relative_sizes = [
|
488
|
+
{
|
489
|
+
'in' => Rational(1),
|
490
|
+
'cm' => Rational(1, 2.54),
|
491
|
+
'pc' => Rational(1, 6),
|
492
|
+
'mm' => Rational(1, 25.4),
|
493
|
+
'q' => Rational(1, 101.6),
|
494
|
+
'pt' => Rational(1, 72),
|
495
|
+
'px' => Rational(1, 96)
|
496
|
+
},
|
497
|
+
{
|
498
|
+
'deg' => Rational(1, 360),
|
499
|
+
'grad' => Rational(1, 400),
|
500
|
+
'rad' => Rational(1, 2 * Math::PI),
|
501
|
+
'turn' => Rational(1)
|
502
|
+
},
|
503
|
+
{
|
504
|
+
's' => Rational(1),
|
505
|
+
'ms' => Rational(1, 1000)
|
506
|
+
},
|
507
|
+
{
|
508
|
+
'Hz' => Rational(1),
|
509
|
+
'kHz' => Rational(1000)
|
510
|
+
},
|
511
|
+
{
|
512
|
+
'dpi' => Rational(1),
|
513
|
+
'dpcm' => Rational(254, 100),
|
514
|
+
'dppx' => Rational(96)
|
515
|
+
}
|
516
|
+
]
|
517
|
+
|
518
|
+
# A hash from each known unit to the set of units that it's mutually
|
519
|
+
# convertible with.
|
520
|
+
MUTUALLY_CONVERTIBLE = {}
|
521
|
+
relative_sizes.map do |values|
|
522
|
+
set = values.keys.to_set
|
523
|
+
values.keys.each {|name| MUTUALLY_CONVERTIBLE[name] = set}
|
524
|
+
end
|
525
|
+
|
526
|
+
# A two-dimensional hash from two units to the conversion ratio between
|
527
|
+
# them. Multiply `X` by `CONVERSION_TABLE[X][Y]` to convert it to `Y`.
|
528
|
+
CONVERSION_TABLE = {}
|
529
|
+
relative_sizes.each do |values|
|
530
|
+
values.each do |(name1, value1)|
|
531
|
+
CONVERSION_TABLE[name1] ||= {}
|
532
|
+
values.each do |(name2, value2)|
|
533
|
+
value = value1 / value2
|
534
|
+
CONVERSION_TABLE[name1][name2] = value.denominator == 1 ? value.to_i : value.to_f
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
432
538
|
|
433
539
|
def conversion_factor(from_unit, to_unit)
|
434
|
-
|
435
|
-
return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
|
436
|
-
res
|
540
|
+
CONVERSION_TABLE[from_unit][to_unit]
|
437
541
|
end
|
438
542
|
|
439
543
|
def convertable?(units)
|
440
|
-
Array(units).
|
544
|
+
units = Array(units).to_set
|
545
|
+
return true if units.empty?
|
546
|
+
return false unless (mutually_convertible = MUTUALLY_CONVERTIBLE[units.first])
|
547
|
+
units.subset?(mutually_convertible)
|
441
548
|
end
|
442
549
|
|
443
550
|
def sans_common_units(units1, units2)
|
444
551
|
units2 = units2.dup
|
445
552
|
# Can't just use -, because we want px*px to coerce properly to px*mm
|
446
|
-
|
447
|
-
|
553
|
+
units1 = units1.map do |u|
|
554
|
+
j = units2.index(u)
|
555
|
+
next u unless j
|
448
556
|
units2.delete_at(j)
|
449
557
|
nil
|
450
|
-
end
|
558
|
+
end
|
559
|
+
units1.compact!
|
560
|
+
return units1, units2
|
451
561
|
end
|
452
562
|
end
|
453
563
|
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module Sass::Script::Value
|
3
|
+
# A SassScript object representing a CSS string *or* a CSS identifier.
|
4
|
+
class String < Base
|
5
|
+
@@interpolation_deprecation = Sass::Deprecation.new
|
6
|
+
|
7
|
+
# The Ruby value of the string.
|
8
|
+
#
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
# Whether this is a CSS string or a CSS identifier.
|
13
|
+
# The difference is that strings are written with double-quotes,
|
14
|
+
# while identifiers aren't.
|
15
|
+
#
|
16
|
+
# @return [Symbol] `:string` or `:identifier`
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
def self.value(contents)
|
20
|
+
contents.gsub("\\\n", "").gsub(/\\(?:([0-9a-fA-F]{1,6})\s?|(.))/) do
|
21
|
+
next $2 if $2
|
22
|
+
# Handle unicode escapes as per CSS Syntax Level 3 section 4.3.8.
|
23
|
+
code_point = $1.to_i(16)
|
24
|
+
if code_point == 0 || code_point > 0x10FFFF ||
|
25
|
+
(code_point >= 0xD800 && code_point <= 0xDFFF)
|
26
|
+
'�'
|
27
|
+
else
|
28
|
+
[code_point].pack("U")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the quoted string representation of `contents`.
|
34
|
+
#
|
35
|
+
# @options opts :quote [String]
|
36
|
+
# The preferred quote style for quoted strings. If `:none`, strings are
|
37
|
+
# always emitted unquoted. If `nil`, quoting is determined automatically.
|
38
|
+
# @options opts :sass [String]
|
39
|
+
# Whether to quote strings for Sass source, as opposed to CSS. Defaults to `false`.
|
40
|
+
def self.quote(contents, opts = {})
|
41
|
+
quote = opts[:quote]
|
42
|
+
|
43
|
+
# Short-circuit if there are no characters that need quoting.
|
44
|
+
unless contents =~ /[\n\\"']|\#\{/
|
45
|
+
quote ||= '"'
|
46
|
+
return "#{quote}#{contents}#{quote}"
|
47
|
+
end
|
48
|
+
|
49
|
+
if quote.nil?
|
50
|
+
if contents.include?('"')
|
51
|
+
if contents.include?("'")
|
52
|
+
quote = '"'
|
53
|
+
else
|
54
|
+
quote = "'"
|
55
|
+
end
|
56
|
+
else
|
57
|
+
quote = '"'
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Replace single backslashes with multiples.
|
62
|
+
contents = contents.gsub("\\", "\\\\\\\\")
|
63
|
+
|
64
|
+
# Escape interpolation.
|
65
|
+
contents = contents.gsub('#{', "\\\#{") if opts[:sass]
|
66
|
+
|
67
|
+
if quote == '"'
|
68
|
+
contents = contents.gsub('"', "\\\"")
|
69
|
+
else
|
70
|
+
contents = contents.gsub("'", "\\'")
|
71
|
+
end
|
72
|
+
|
73
|
+
contents = contents.gsub(/\n(?![a-fA-F0-9\s])/, "\\a").gsub("\n", "\\a ")
|
74
|
+
"#{quote}#{contents}#{quote}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# Creates a new string.
|
78
|
+
#
|
79
|
+
# @param value [String] See \{#value}
|
80
|
+
# @param type [Symbol] See \{#type}
|
81
|
+
# @param deprecated_interp_equivalent [String?]
|
82
|
+
# If this was created via a potentially-deprecated string interpolation,
|
83
|
+
# this is the replacement expression that should be suggested to the user.
|
84
|
+
def initialize(value, type = :identifier, deprecated_interp_equivalent = nil)
|
85
|
+
super(value)
|
86
|
+
@type = type
|
87
|
+
@deprecated_interp_equivalent = deprecated_interp_equivalent
|
88
|
+
end
|
89
|
+
|
90
|
+
# @see Value#plus
|
91
|
+
def plus(other)
|
92
|
+
other_value = if other.is_a?(Sass::Script::Value::String)
|
93
|
+
other.value
|
94
|
+
else
|
95
|
+
other.to_s(:quote => :none)
|
96
|
+
end
|
97
|
+
Sass::Script::Value::String.new(value + other_value, type)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @see Value#to_s
|
101
|
+
def to_s(opts = {})
|
102
|
+
return @value.gsub(/\n\s*/, ' ') if opts[:quote] == :none || @type == :identifier
|
103
|
+
String.quote(value, opts)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @see Value#to_sass
|
107
|
+
def to_sass(opts = {})
|
108
|
+
to_s(opts.merge(:sass => true))
|
109
|
+
end
|
110
|
+
|
111
|
+
def separator
|
112
|
+
check_deprecated_interp
|
113
|
+
super
|
114
|
+
end
|
115
|
+
|
116
|
+
def to_a
|
117
|
+
check_deprecated_interp
|
118
|
+
super
|
119
|
+
end
|
120
|
+
|
121
|
+
# Prints a warning if this string was created using potentially-deprecated
|
122
|
+
# interpolation.
|
123
|
+
def check_deprecated_interp
|
124
|
+
return unless @deprecated_interp_equivalent
|
125
|
+
|
126
|
+
@@interpolation_deprecation.warn(source_range.file, source_range.start_pos.line, <<WARNING)
|
127
|
+
\#{} interpolation near operators will be simplified in a future version of Sass.
|
128
|
+
To preserve the current behavior, use quotes:
|
129
|
+
|
130
|
+
#{@deprecated_interp_equivalent}
|
131
|
+
WARNING
|
132
|
+
end
|
133
|
+
|
134
|
+
def inspect
|
135
|
+
String.quote(value)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Sass::Script::Value; end
|
2
|
+
|
3
|
+
require 'sass/script/value/base'
|
4
|
+
require 'sass/script/value/string'
|
5
|
+
require 'sass/script/value/number'
|
6
|
+
require 'sass/script/value/color'
|
7
|
+
require 'sass/script/value/bool'
|
8
|
+
require 'sass/script/value/null'
|
9
|
+
require 'sass/script/value/list'
|
10
|
+
require 'sass/script/value/arg_list'
|
11
|
+
require 'sass/script/value/map'
|