sass 3.2.7 → 3.3.0.rc.1

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.
Files changed (184) hide show
  1. data/MIT-LICENSE +2 -2
  2. data/README.md +14 -2
  3. data/Rakefile +25 -1
  4. data/VERSION +1 -1
  5. data/VERSION_DATE +1 -1
  6. data/VERSION_NAME +1 -1
  7. data/lib/sass/cache_stores/base.rb +4 -2
  8. data/lib/sass/cache_stores/chain.rb +2 -1
  9. data/lib/sass/cache_stores/filesystem.rb +2 -6
  10. data/lib/sass/cache_stores/memory.rb +1 -1
  11. data/lib/sass/cache_stores/null.rb +2 -2
  12. data/lib/sass/callbacks.rb +1 -0
  13. data/lib/sass/css.rb +10 -10
  14. data/lib/sass/engine.rb +403 -150
  15. data/lib/sass/environment.rb +136 -57
  16. data/lib/sass/error.rb +7 -7
  17. data/lib/sass/exec.rb +123 -39
  18. data/lib/sass/features.rb +41 -0
  19. data/lib/sass/importers/base.rb +33 -2
  20. data/lib/sass/importers/deprecated_path.rb +45 -0
  21. data/lib/sass/importers/filesystem.rb +25 -14
  22. data/lib/sass/importers.rb +1 -0
  23. data/lib/sass/logger/base.rb +3 -3
  24. data/lib/sass/logger/log_level.rb +4 -6
  25. data/lib/sass/media.rb +19 -19
  26. data/lib/sass/plugin/compiler.rb +141 -101
  27. data/lib/sass/plugin/configuration.rb +18 -22
  28. data/lib/sass/plugin/merb.rb +1 -1
  29. data/lib/sass/plugin/staleness_checker.rb +24 -8
  30. data/lib/sass/plugin.rb +4 -2
  31. data/lib/sass/repl.rb +3 -3
  32. data/lib/sass/script/css_lexer.rb +9 -4
  33. data/lib/sass/script/css_parser.rb +6 -2
  34. data/lib/sass/script/functions.rb +1343 -590
  35. data/lib/sass/script/lexer.rb +84 -52
  36. data/lib/sass/script/parser.rb +217 -97
  37. data/lib/sass/script/tree/funcall.rb +290 -0
  38. data/lib/sass/script/{interpolation.rb → tree/interpolation.rb} +34 -13
  39. data/lib/sass/script/tree/list_literal.rb +80 -0
  40. data/lib/sass/script/tree/literal.rb +47 -0
  41. data/lib/sass/script/tree/map_literal.rb +64 -0
  42. data/lib/sass/script/{node.rb → tree/node.rb} +22 -12
  43. data/lib/sass/script/{operation.rb → tree/operation.rb} +17 -25
  44. data/lib/sass/script/tree/selector.rb +30 -0
  45. data/lib/sass/script/{string_interpolation.rb → tree/string_interpolation.rb} +5 -4
  46. data/lib/sass/script/{unary_operation.rb → tree/unary_operation.rb} +14 -9
  47. data/lib/sass/script/tree/variable.rb +57 -0
  48. data/lib/sass/script/tree.rb +16 -0
  49. data/lib/sass/script/{arg_list.rb → value/arg_list.rb} +4 -24
  50. data/lib/sass/script/value/base.rb +248 -0
  51. data/lib/sass/script/value/bool.rb +36 -0
  52. data/lib/sass/script/{color.rb → value/color.rb} +239 -195
  53. data/lib/sass/script/value/helpers.rb +155 -0
  54. data/lib/sass/script/value/list.rb +119 -0
  55. data/lib/sass/script/value/map.rb +70 -0
  56. data/lib/sass/script/value/null.rb +45 -0
  57. data/lib/sass/script/{number.rb → value/number.rb} +91 -65
  58. data/lib/sass/script/{string.rb → value/string.rb} +9 -11
  59. data/lib/sass/script/value.rb +11 -0
  60. data/lib/sass/script.rb +35 -8
  61. data/lib/sass/scss/css_parser.rb +2 -1
  62. data/lib/sass/scss/parser.rb +338 -170
  63. data/lib/sass/scss/rx.rb +5 -6
  64. data/lib/sass/scss/script_lexer.rb +1 -0
  65. data/lib/sass/scss/script_parser.rb +1 -0
  66. data/lib/sass/scss/static_parser.rb +23 -6
  67. data/lib/sass/selector/abstract_sequence.rb +2 -2
  68. data/lib/sass/selector/comma_sequence.rb +21 -16
  69. data/lib/sass/selector/sequence.rb +60 -34
  70. data/lib/sass/selector/simple.rb +11 -12
  71. data/lib/sass/selector/simple_sequence.rb +55 -33
  72. data/lib/sass/selector.rb +52 -48
  73. data/lib/sass/source/map.rb +211 -0
  74. data/lib/sass/source/position.rb +39 -0
  75. data/lib/sass/source/range.rb +41 -0
  76. data/lib/sass/stack.rb +120 -0
  77. data/lib/sass/supports.rb +12 -13
  78. data/lib/sass/tree/at_root_node.rb +82 -0
  79. data/lib/sass/tree/comment_node.rb +3 -3
  80. data/lib/sass/tree/css_import_node.rb +11 -11
  81. data/lib/sass/tree/debug_node.rb +2 -2
  82. data/lib/sass/tree/directive_node.rb +13 -2
  83. data/lib/sass/tree/each_node.rb +8 -8
  84. data/lib/sass/tree/extend_node.rb +13 -6
  85. data/lib/sass/tree/for_node.rb +4 -4
  86. data/lib/sass/tree/function_node.rb +5 -4
  87. data/lib/sass/tree/if_node.rb +1 -1
  88. data/lib/sass/tree/import_node.rb +4 -5
  89. data/lib/sass/tree/media_node.rb +4 -14
  90. data/lib/sass/tree/mixin_def_node.rb +4 -4
  91. data/lib/sass/tree/mixin_node.rb +21 -8
  92. data/lib/sass/tree/node.rb +29 -12
  93. data/lib/sass/tree/prop_node.rb +38 -18
  94. data/lib/sass/tree/return_node.rb +3 -2
  95. data/lib/sass/tree/root_node.rb +19 -3
  96. data/lib/sass/tree/rule_node.rb +25 -17
  97. data/lib/sass/tree/supports_node.rb +0 -13
  98. data/lib/sass/tree/trace_node.rb +2 -1
  99. data/lib/sass/tree/variable_node.rb +9 -3
  100. data/lib/sass/tree/visitors/base.rb +6 -6
  101. data/lib/sass/tree/visitors/check_nesting.rb +12 -9
  102. data/lib/sass/tree/visitors/convert.rb +63 -38
  103. data/lib/sass/tree/visitors/cssize.rb +63 -23
  104. data/lib/sass/tree/visitors/deep_copy.rb +6 -5
  105. data/lib/sass/tree/visitors/extend.rb +7 -7
  106. data/lib/sass/tree/visitors/perform.rb +256 -151
  107. data/lib/sass/tree/visitors/set_options.rb +6 -6
  108. data/lib/sass/tree/visitors/to_css.rb +231 -81
  109. data/lib/sass/tree/warn_node.rb +2 -2
  110. data/lib/sass/tree/while_node.rb +2 -2
  111. data/lib/sass/util/multibyte_string_scanner.rb +2 -0
  112. data/lib/sass/util/normalized_map.rb +65 -0
  113. data/lib/sass/util/ordered_hash.rb +188 -0
  114. data/lib/sass/util/subset_map.rb +3 -2
  115. data/lib/sass/util/test.rb +9 -0
  116. data/lib/sass/util.rb +220 -34
  117. data/lib/sass/version.rb +9 -9
  118. data/lib/sass.rb +14 -7
  119. data/test/sass/compiler_test.rb +213 -0
  120. data/test/sass/conversion_test.rb +235 -9
  121. data/test/sass/engine_test.rb +230 -60
  122. data/test/sass/exec_test.rb +86 -0
  123. data/test/sass/extend_test.rb +215 -147
  124. data/test/sass/functions_test.rb +584 -99
  125. data/test/sass/importer_test.rb +165 -17
  126. data/test/sass/plugin_test.rb +19 -13
  127. data/test/sass/script_conversion_test.rb +40 -0
  128. data/test/sass/script_test.rb +231 -21
  129. data/test/sass/scss/css_test.rb +14 -5
  130. data/test/sass/scss/scss_test.rb +1266 -66
  131. data/test/sass/source_map_test.rb +879 -0
  132. data/test/sass/templates/bork5.sass +3 -0
  133. data/test/sass/util/normalized_map_test.rb +30 -0
  134. data/test/sass/util_test.rb +90 -0
  135. data/test/sass/value_helpers_test.rb +181 -0
  136. data/test/test_helper.rb +7 -2
  137. metadata +316 -291
  138. data/lib/sass/script/bool.rb +0 -18
  139. data/lib/sass/script/funcall.rb +0 -231
  140. data/lib/sass/script/list.rb +0 -84
  141. data/lib/sass/script/literal.rb +0 -239
  142. data/lib/sass/script/null.rb +0 -34
  143. data/lib/sass/script/variable.rb +0 -58
  144. data/test/Gemfile +0 -3
  145. data/vendor/listen/CHANGELOG.md +0 -221
  146. data/vendor/listen/CONTRIBUTING.md +0 -38
  147. data/vendor/listen/Gemfile +0 -30
  148. data/vendor/listen/Guardfile +0 -8
  149. data/vendor/listen/LICENSE +0 -20
  150. data/vendor/listen/README.md +0 -315
  151. data/vendor/listen/Rakefile +0 -47
  152. data/vendor/listen/Vagrantfile +0 -96
  153. data/vendor/listen/lib/listen/adapter.rb +0 -214
  154. data/vendor/listen/lib/listen/adapters/bsd.rb +0 -112
  155. data/vendor/listen/lib/listen/adapters/darwin.rb +0 -85
  156. data/vendor/listen/lib/listen/adapters/linux.rb +0 -113
  157. data/vendor/listen/lib/listen/adapters/polling.rb +0 -67
  158. data/vendor/listen/lib/listen/adapters/windows.rb +0 -87
  159. data/vendor/listen/lib/listen/dependency_manager.rb +0 -126
  160. data/vendor/listen/lib/listen/directory_record.rb +0 -371
  161. data/vendor/listen/lib/listen/listener.rb +0 -225
  162. data/vendor/listen/lib/listen/multi_listener.rb +0 -143
  163. data/vendor/listen/lib/listen/turnstile.rb +0 -28
  164. data/vendor/listen/lib/listen/version.rb +0 -3
  165. data/vendor/listen/lib/listen.rb +0 -40
  166. data/vendor/listen/listen.gemspec +0 -22
  167. data/vendor/listen/spec/listen/adapter_spec.rb +0 -183
  168. data/vendor/listen/spec/listen/adapters/bsd_spec.rb +0 -36
  169. data/vendor/listen/spec/listen/adapters/darwin_spec.rb +0 -37
  170. data/vendor/listen/spec/listen/adapters/linux_spec.rb +0 -47
  171. data/vendor/listen/spec/listen/adapters/polling_spec.rb +0 -68
  172. data/vendor/listen/spec/listen/adapters/windows_spec.rb +0 -30
  173. data/vendor/listen/spec/listen/dependency_manager_spec.rb +0 -107
  174. data/vendor/listen/spec/listen/directory_record_spec.rb +0 -1225
  175. data/vendor/listen/spec/listen/listener_spec.rb +0 -169
  176. data/vendor/listen/spec/listen/multi_listener_spec.rb +0 -174
  177. data/vendor/listen/spec/listen/turnstile_spec.rb +0 -56
  178. data/vendor/listen/spec/listen_spec.rb +0 -73
  179. data/vendor/listen/spec/spec_helper.rb +0 -21
  180. data/vendor/listen/spec/support/adapter_helper.rb +0 -629
  181. data/vendor/listen/spec/support/directory_record_helper.rb +0 -55
  182. data/vendor/listen/spec/support/fixtures_helper.rb +0 -29
  183. data/vendor/listen/spec/support/listeners_helper.rb +0 -156
  184. data/vendor/listen/spec/support/platform_helper.rb +0 -15
@@ -1,10 +1,12 @@
1
+ require 'sass/script/value/helpers'
2
+
1
3
  module Sass::Script
2
4
  # Methods in this module are accessible from the SassScript context.
3
5
  # For example, you can write
4
6
  #
5
- # $color = hsl(120deg, 100%, 50%)
7
+ # $color: hsl(120deg, 100%, 50%)
6
8
  #
7
- # and it will call {Sass::Script::Functions#hsl}.
9
+ # and it will call {Functions#hsl}.
8
10
  #
9
11
  # The following functions are provided:
10
12
  #
@@ -13,13 +15,12 @@ module Sass::Script
13
15
  # ## RGB Functions
14
16
  #
15
17
  # \{#rgb rgb($red, $green, $blue)}
16
- # : Converts an `rgb(red, green, blue)` triplet into a color.
18
+ # : Creates a {Sass::Script::Value::Color Color} from red, green, and blue
19
+ # values.
17
20
  #
18
21
  # \{#rgba rgba($red, $green, $blue, $alpha)}
19
- # : Converts an `rgba(red, green, blue, alpha)` quadruplet into a color.
20
- #
21
- # \{#rgba rgba($color, $alpha)}
22
- # : Adds an alpha layer to any color value.
22
+ # : Creates a {Sass::Script::Value::Color Color} from red, green, blue, and
23
+ # alpha values.
23
24
  #
24
25
  # \{#red red($color)}
25
26
  # : Gets the red component of a color.
@@ -36,10 +37,12 @@ module Sass::Script
36
37
  # ## HSL Functions
37
38
  #
38
39
  # \{#hsl hsl($hue, $saturation, $lightness)}
39
- # : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
40
+ # : Creates a {Sass::Script::Value::Color Color} from hue, saturation, and
41
+ # lightness values.
40
42
  #
41
43
  # \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
42
- # : Converts an `hsla(hue, saturation, lightness, alpha)` quadruplet into a color.
44
+ # : Creates a {Sass::Script::Value::Color Color} from hue, saturation,
45
+ # lightness, and alpha values.
43
46
  #
44
47
  # \{#hue hue($color)}
45
48
  # : Gets the hue component of a color.
@@ -80,7 +83,7 @@ module Sass::Script
80
83
  # : Gets the alpha component (opacity) of a color.
81
84
  #
82
85
  # \{#rgba rgba($color, $alpha)}
83
- # : Add or change an alpha layer for any color value.
86
+ # : Changes the alpha component for a color.
84
87
  #
85
88
  # \{#opacify opacify($color, $amount)} / \{#fade_in fade-in($color, $amount)}
86
89
  # : Makes a color more opaque.
@@ -90,13 +93,16 @@ module Sass::Script
90
93
  #
91
94
  # ## Other Color Functions
92
95
  #
93
- # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
94
- # : Increase or decrease any of the components of a color.
96
+ # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\],
97
+ # \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
98
+ # : Increases or decreases one or more components of a color.
95
99
  #
96
- # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\])}
97
- # : Fluidly scale one or more components of a color.
100
+ # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\],
101
+ # \[$saturation\], \[$lightness\], \[$alpha\])}
102
+ # : Fluidly scales one or more properties of a color.
98
103
  #
99
- # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
104
+ # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\],
105
+ # \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
100
106
  # : Changes one or more properties of a color.
101
107
  #
102
108
  # \{#ie_hex_str ie-hex-str($color)}
@@ -105,11 +111,29 @@ module Sass::Script
105
111
  # ## String Functions
106
112
  #
107
113
  # \{#unquote unquote($string)}
108
- # : Removes the quotes from a string.
114
+ # : Removes quotes from a string.
109
115
  #
110
116
  # \{#quote quote($string)}
111
117
  # : Adds quotes to a string.
112
118
  #
119
+ # \{#str_length str-length($string)}
120
+ # : Returns the number of characters in a string.
121
+ #
122
+ # \{#str_insert str-insert($string, $insert, $index)}
123
+ # : Inserts `$insert` into `$string` at `$index`.
124
+ #
125
+ # \{#str_index str-index($string, $substring)}
126
+ # : Returns the index of the first occurance of `$substring` in `$string`.
127
+ #
128
+ # \{#str_slice str-slice($string, $start-at, [$end-at])}
129
+ # : Extracts a substring from `$string`.
130
+ #
131
+ # \{#to_upper_case to-upper-case($string)}
132
+ # : Converts a string to upper case.
133
+ #
134
+ # \{#to_lower_case to-lower-case($string)}
135
+ # : Converts a string to lower case.
136
+ #
113
137
  # ## Number Functions
114
138
  #
115
139
  # \{#percentage percentage($value)}
@@ -119,22 +143,24 @@ module Sass::Script
119
143
  # : Rounds a number to the nearest whole number.
120
144
  #
121
145
  # \{#ceil ceil($value)}
122
- # : Rounds a number up to the nearest whole number.
146
+ # : Rounds a number up to the next whole number.
123
147
  #
124
148
  # \{#floor floor($value)}
125
- # : Rounds a number down to the nearest whole number.
149
+ # : Rounds a number down to the previous whole number.
126
150
  #
127
151
  # \{#abs abs($value)}
128
152
  # : Returns the absolute value of a number.
129
153
  #
130
- # \{#min min($x1, $x2, ...)\}
131
- # : Finds the minimum of several values.
154
+ # \{#min min($numbers...)\}
155
+ # : Finds the minimum of several numbers.
132
156
  #
133
- # \{#max max($x1, $x2, ...)\}
134
- # : Finds the maximum of several values.
157
+ # \{#max max($numbers...)\}
158
+ # : Finds the maximum of several numbers.
135
159
  #
136
160
  # ## List Functions {#list-functions}
137
161
  #
162
+ # All list functions work for maps as well, treating them as lists of pairs.
163
+ #
138
164
  # \{#length length($list)}
139
165
  # : Returns the length of a list.
140
166
  #
@@ -147,24 +173,75 @@ module Sass::Script
147
173
  # \{#append append($list1, $val, \[$separator\])}
148
174
  # : Appends a single value onto the end of a list.
149
175
  #
176
+ # \{#zip zip($lists...)}
177
+ # : Combines several lists into a single multidimensional list.
178
+ #
179
+ # \{#index index($list, $value)}
180
+ # : Returns the position of a value within a list.
181
+ #
182
+ # \{#list_separator list-separator(#list)}
183
+ # : Returns the separator of a list.
184
+ #
185
+ # ## Map Functions {#map-functions}
186
+ #
187
+ # \{#map_get map-get($map, $key)}
188
+ # : Returns the value in a map associated with a given key.
189
+ #
190
+ # \{#map_merge map-merge($map1, $map2)}
191
+ # : Merges two maps together into a new map.
192
+ #
193
+ # \{#map_keys map-keys($map)}
194
+ # : Returns a list of all keys in a map.
195
+ #
196
+ # \{#map_values map-values($map)}
197
+ # : Returns a list of all values in a map.
198
+ #
199
+ # \{#map_has_key map-has-key($key)}
200
+ # : Returns whether a map has a value associated with a given key.
201
+ #
202
+ # \{#keywords keywords($args)}
203
+ # : Returns the keywords passed to a function that takes variable arguments.
204
+ #
150
205
  # ## Introspection Functions
151
206
  #
207
+ # \{#feature_exists feature-exists($feature)}
208
+ # : Returns whether a feature exists in the current Sass runtime.
209
+ #
210
+ # \{#variable_exists variable-exists($name)}
211
+ # : Returns whether a variable with the given name exists in the current scope.
212
+ #
213
+ # \{#global_variable_exists global-variable-exists($name)}
214
+ # : Returns whether a variable with the given name exists in the global scope.
215
+ #
216
+ # \{#function_exists function-exists($name)}
217
+ # : Returns whether a function with the given name exists.
218
+ #
219
+ # \{#mixin_exists mixin-exists($name)}
220
+ # : Returns whether a mixin with the given name exists.
221
+ #
152
222
  # \{#type_of type-of($value)}
153
223
  # : Returns the type of a value.
154
224
  #
155
225
  # \{#unit unit($number)}
156
- # : Returns the units associated with a number.
226
+ # : Returns the unit(s) associated with a number.
157
227
  #
158
228
  # \{#unitless unitless($number)}
159
- # : Returns whether a number has units or not.
229
+ # : Returns whether a number has units.
160
230
  #
161
231
  # \{#comparable comparable($number-1, $number-2)}
162
- # : Returns whether two numbers can be added or compared.
232
+ # : Returns whether two numbers can be added, subtracted, or compared.
233
+ #
234
+ # \{#call call($name, $args...)}
235
+ # : Dynamically calls a Sass function.
163
236
  #
164
237
  # ## Miscellaneous Functions
165
238
  #
166
239
  # \{#if if($condition, $if-true, $if-false)}
167
- # : Returns one of two values, depending on whether or not a condition is true.
240
+ # : Returns one of two values, depending on whether or not `$condition` is
241
+ # true.
242
+ #
243
+ # \{#unique_id unique-id()}
244
+ # : Returns a unique CSS identifier.
168
245
  #
169
246
  # ## Adding Custom Functions
170
247
  #
@@ -174,9 +251,9 @@ module Sass::Script
174
251
  # module Sass::Script::Functions
175
252
  # def reverse(string)
176
253
  # assert_type string, :String
177
- # Sass::Script::String.new(string.value.reverse)
254
+ # Sass::Script::Value::String.new(string.value.reverse)
178
255
  # end
179
- # declare :reverse, :args => [:string]
256
+ # declare :reverse, [:string]
180
257
  # end
181
258
  #
182
259
  # Calling {declare} tells Sass the argument names for your function.
@@ -184,14 +261,15 @@ module Sass::Script
184
261
  # {declare} can also allow your function to take arbitrary keyword arguments.
185
262
  #
186
263
  # There are a few things to keep in mind when modifying this module.
187
- # First of all, the arguments passed are {Sass::Script::Literal} objects.
188
- # Literal objects are also expected to be returned.
264
+ # First of all, the arguments passed are {Value} objects.
265
+ # Value objects are also expected to be returned.
189
266
  # This means that Ruby values must be unwrapped and wrapped.
190
267
  #
191
- # Most Literal objects support the {Sass::Script::Literal#value value} accessor
192
- # for getting their Ruby values.
193
- # Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
194
- # {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
268
+ # Most Value objects support the {Value::Base#value value} accessor for getting
269
+ # their Ruby values. Color objects, though, must be accessed using
270
+ # {Sass::Script::Value::Color#rgb rgb}, {Sass::Script::Value::Color#red red},
271
+ # {Sass::Script::Value::Color#blue green}, or {Sass::Script::Value::Color#blue
272
+ # blue}.
195
273
  #
196
274
  # Second, making Ruby functions accessible from Sass introduces the temptation
197
275
  # to do things like database access within stylesheets.
@@ -211,19 +289,21 @@ module Sass::Script
211
289
  #
212
290
  # ### Caveats
213
291
  #
214
- # When creating new {Literal} objects within functions,
215
- # be aware that it's not safe to call {Literal#to_s #to_s}
216
- # (or other methods that use the string representation)
217
- # on those objects without first setting {Node#options= the #options attribute}.
292
+ # When creating new {Value} objects within functions, be aware that it's not
293
+ # safe to call {Value::Base#to_s #to_s} (or other methods that use the string
294
+ # representation) on those objects without first setting {Tree::Node#options=
295
+ # the #options attribute}.
218
296
  module Functions
219
297
  @signatures = {}
220
298
 
221
299
  # A class representing a Sass function signature.
222
300
  #
223
- # @attr args [Array<Symbol>] The names of the arguments to the function.
301
+ # @attr args [Array<String>] The names of the arguments to the function.
302
+ # @attr delayed_args [Array<String>] The names of the arguments whose evaluation should be
303
+ # delayed.
224
304
  # @attr var_args [Boolean] Whether the function takes a variable number of arguments.
225
305
  # @attr var_kwargs [Boolean] Whether the function takes an arbitrary set of keyword arguments.
226
- Signature = Struct.new(:args, :var_args, :var_kwargs)
306
+ Signature = Struct.new(:args, :delayed_args, :var_args, :var_kwargs)
227
307
 
228
308
  # Declare a Sass signature for a Ruby-defined function.
229
309
  # This includes the names of the arguments,
@@ -240,6 +320,12 @@ module Sass::Script
240
320
  # but none of them but the first will be used
241
321
  # unless the user uses keyword arguments.
242
322
  #
323
+ # @example
324
+ # declare :rgba, [:hex, :alpha]
325
+ # declare :rgba, [:red, :green, :blue, :alpha]
326
+ # declare :accepts_anything, [], :var_args => true, :var_kwargs => true
327
+ # declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
328
+ #
243
329
  # @param method_name [Symbol] The name of the method
244
330
  # whose signature is being declared.
245
331
  # @param args [Array<Symbol>] The names of the arguments for the function signature.
@@ -250,19 +336,27 @@ module Sass::Script
250
336
  # Whether the function accepts other keyword arguments
251
337
  # in addition to those in `:args`.
252
338
  # If this is true, the Ruby function will be passed a hash from strings
253
- # to {Sass::Script::Literal}s as the last argument.
339
+ # to {Value}s as the last argument.
254
340
  # In addition, if this is true and `:var_args` is not,
255
341
  # Sass will ensure that the last argument passed is a hash.
256
- #
257
- # @example
258
- # declare :rgba, [:hex, :alpha]
259
- # declare :rgba, [:red, :green, :blue, :alpha]
260
- # declare :accepts_anything, [], :var_args => true, :var_kwargs => true
261
- # declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
262
342
  def self.declare(method_name, args, options = {})
343
+ delayed_args = []
344
+ args = args.map do |a|
345
+ a = a.to_s
346
+ if a[0] == ?&
347
+ a = a[1..-1]
348
+ delayed_args << a
349
+ end
350
+ a
351
+ end
352
+ # We don't expose this functionality except to certain builtin methods.
353
+ if delayed_args.any? && method_name != :if
354
+ raise ArgumentError.new("Delayed arguments are not allowed for method #{method_name}")
355
+ end
263
356
  @signatures[method_name] ||= []
264
357
  @signatures[method_name] << Signature.new(
265
- args.map {|s| s.to_s},
358
+ args,
359
+ delayed_args,
266
360
  options[:var_args],
267
361
  options[:var_kwargs])
268
362
  end
@@ -272,8 +366,8 @@ module Sass::Script
272
366
  # If no signatures match, the first signature is returned for error messaging.
273
367
  #
274
368
  # @param method_name [Symbol] The name of the Ruby function to be called.
275
- # @param arg_arity [Number] The number of unnamed arguments the function was passed.
276
- # @param kwarg_arity [Number] The number of keyword arguments the function was passed.
369
+ # @param arg_arity [Fixnum] The number of unnamed arguments the function was passed.
370
+ # @param kwarg_arity [Fixnum] The number of keyword arguments the function was passed.
277
371
  #
278
372
  # @return [{Symbol => Object}, nil]
279
373
  # The signature options for the matching signature,
@@ -281,22 +375,23 @@ module Sass::Script
281
375
  def self.signature(method_name, arg_arity, kwarg_arity)
282
376
  return unless @signatures[method_name]
283
377
  @signatures[method_name].each do |signature|
284
- return signature if signature.args.size == arg_arity + kwarg_arity
285
- next unless signature.args.size < arg_arity + kwarg_arity
378
+ sig_arity = signature.args.size
379
+ return signature if sig_arity == arg_arity + kwarg_arity
380
+ next unless sig_arity < arg_arity + kwarg_arity
286
381
 
287
382
  # We have enough args.
288
383
  # Now we need to figure out which args are varargs
289
384
  # and if the signature allows them.
290
385
  t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
291
- if signature.args.size > t_arg_arity
386
+ if sig_arity > t_arg_arity
292
387
  # we transfer some kwargs arity to args arity
293
388
  # if it does not have enough args -- assuming the names will work out.
294
- t_kwarg_arity -= (signature.args.size - t_arg_arity)
295
- t_arg_arity = signature.args.size
389
+ t_kwarg_arity -= (sig_arity - t_arg_arity)
390
+ t_arg_arity = sig_arity
296
391
  end
297
392
 
298
- if ( t_arg_arity == signature.args.size || t_arg_arity > signature.args.size && signature.var_args ) &&
299
- (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
393
+ if (t_arg_arity == sig_arity || t_arg_arity > sig_arity && signature.var_args) &&
394
+ (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
300
395
  return signature
301
396
  end
302
397
  end
@@ -308,15 +403,30 @@ module Sass::Script
308
403
  # are available to use in functions.
309
404
  class EvaluationContext
310
405
  include Functions
406
+ include Value::Helpers
407
+
408
+ # The human-readable names for [Sass::Script::Value::Base]. The default is
409
+ # just the downcased name of the type. The default is the downcased type
410
+ # name.
411
+ TYPE_NAMES = {:ArgList => 'variable argument list'}
412
+
413
+ # The environment for this function. This environment's
414
+ # {Environment#parent} is the global environment, and its
415
+ # {Environment#caller} is a read-only view of the local environment of the
416
+ # caller of this function.
417
+ #
418
+ # @return [Environment]
419
+ attr_reader :environment
311
420
 
312
421
  # The options hash for the {Sass::Engine} that is processing the function call
313
422
  #
314
423
  # @return [{Symbol => Object}]
315
424
  attr_reader :options
316
425
 
317
- # @param options [{Symbol => Object}] See \{#options}
318
- def initialize(options)
319
- @options = options
426
+ # @param environment [Environment] See \{#environment}
427
+ def initialize(environment)
428
+ @environment = environment
429
+ @options = environment.options
320
430
  end
321
431
 
322
432
  # Asserts that the type of a given SassScript value
@@ -329,15 +439,79 @@ module Sass::Script
329
439
  # @example
330
440
  # assert_type value, :String
331
441
  # assert_type value, :Number
332
- # @param value [Sass::Script::Literal] A SassScript value
442
+ # @param value [Sass::Script::Value::Base] A SassScript value
333
443
  # @param type [Symbol] The name of the type the value is expected to be
334
- # @param name [String, nil] The name of the argument.
444
+ # @param name [String, Symbol, nil] The name of the argument.
445
+ # @raise [ArgumentError] if value is not of the correct type.
335
446
  def assert_type(value, type, name = nil)
336
- return if value.is_a?(Sass::Script.const_get(type))
337
- err = "#{value.inspect} is not a #{type.to_s.downcase}"
338
- err = "$#{name}: " + err if name
447
+ klass = Sass::Script::Value.const_get(type)
448
+ return if value.is_a?(klass)
449
+ return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.is_pseudo_map?
450
+ err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
451
+ err = "$#{name.to_s.gsub('_', '-')}: " + err if name
339
452
  raise ArgumentError.new(err)
340
453
  end
454
+
455
+ # Asserts that the unit of the number is as expected.
456
+ #
457
+ # @example
458
+ # assert_unit number, "px"
459
+ # assert_unit number, nil
460
+ # @param number [Sass::Script::Value::Number] The number to be validated.
461
+ # @param unit [::String]
462
+ # The unit that the number must have.
463
+ # If nil, the number must be unitless.
464
+ # @param name [::String] The name of the parameter being validated.
465
+ # @raise [ArgumentError] if number is not of the correct unit or is not a number.
466
+ def assert_unit(number, unit, name = nil)
467
+ assert_type number, :Number, name
468
+ return if number.is_unit?(unit)
469
+ expectation = unit ? "have a unit of #{unit}" : "be unitless"
470
+ if name
471
+ raise ArgumentError.new("Expected $#{name} to #{expectation} but got #{number}")
472
+ else
473
+ raise ArgumentError.new("Expected #{number} to #{expectation}")
474
+ end
475
+ end
476
+
477
+ # Asserts that the value is an integer.
478
+ #
479
+ # @example
480
+ # assert_integer 2px
481
+ # assert_integer 2.5px
482
+ # => SyntaxError: "Expected 2.5px to be an integer"
483
+ # assert_integer 2.5px, "width"
484
+ # => SyntaxError: "Expected width to be an integer but got 2.5px"
485
+ # @param number [Sass::Script::Value::Base] The value to be validated.
486
+ # @param name [::String] The name of the parameter being validated.
487
+ # @raise [ArgumentError] if number is not an integer or is not a number.
488
+ def assert_integer(number, name = nil)
489
+ assert_type number, :Number, name
490
+ return if number.int?
491
+ if name
492
+ raise ArgumentError.new("Expected $#{name} to be an integer but got #{number}")
493
+ else
494
+ raise ArgumentError.new("Expected #{number} to be an integer")
495
+ end
496
+ end
497
+
498
+ # Performs a node that has been delayed for execution.
499
+ #
500
+ # @private
501
+ # @param node [Sass::Script::Tree::Node,
502
+ # Sass::Script::Value::Base] When this is a tree node, it's
503
+ # performed in the caller's environment. When it's a value
504
+ # (which can happen when the value had to be performed already
505
+ # -- like for a splat), it's returned as-is.
506
+ # @param env [Sass::Environment] The environment within which to perform the node.
507
+ # Defaults to the (read-only) environment of the caller.
508
+ def perform(node, env = environment.caller)
509
+ if node.is_a?(Sass::Script::Value::Base)
510
+ node
511
+ else
512
+ node.perform(env)
513
+ end
514
+ end
341
515
  end
342
516
 
343
517
  class << self
@@ -348,6 +522,7 @@ module Sass::Script
348
522
  alias_method :callable?, :public_method_defined?
349
523
 
350
524
  private
525
+
351
526
  def include(*args)
352
527
  r = super
353
528
  # We have to re-include ourselves into EvaluationContext to work around
@@ -357,69 +532,79 @@ module Sass::Script
357
532
  end
358
533
  end
359
534
 
360
- # Creates a {Color} object from red, green, and blue values.
535
+ # Creates a {Sass::Script::Value::Color Color} object from red, green, and
536
+ # blue values.
361
537
  #
362
- # @param red [Number]
363
- # A number between 0 and 255 inclusive,
364
- # or between 0% and 100% inclusive
365
- # @param green [Number]
366
- # A number between 0 and 255 inclusive,
367
- # or between 0% and 100% inclusive
368
- # @param blue [Number]
369
- # A number between 0 and 255 inclusive,
370
- # or between 0% and 100% inclusive
371
538
  # @see #rgba
372
- # @return [Color]
539
+ # @overload rgb($red, $green, $blue)
540
+ # @param $red [Sass::Script::Value::Number] The amount of red in the color.
541
+ # Must be between 0 and 255 inclusive, or between `0%` and `100%`
542
+ # inclusive
543
+ # @param $green [Sass::Script::Value::Number] The amount of green in the
544
+ # color. Must be between 0 and 255 inclusive, or between `0%` and `100%`
545
+ # inclusive
546
+ # @param $blue [Sass::Script::Value::Number] The amount of blue in the
547
+ # color. Must be between 0 and 255 inclusive, or between `0%` and `100%`
548
+ # inclusive
549
+ # @return [Sass::Script::Value::Color]
550
+ # @raise [ArgumentError] if any parameter is the wrong type or out of bounds
373
551
  def rgb(red, green, blue)
374
- assert_type red, :Number
375
- assert_type green, :Number
376
- assert_type blue, :Number
377
-
378
- Color.new([red, green, blue].map do |c|
379
- v = c.value
380
- if c.numerator_units == ["%"] && c.denominator_units.empty?
381
- v = Sass::Util.check_range("Color value", 0..100, c, '%')
382
- v * 255 / 100.0
383
- else
384
- Sass::Util.check_range("Color value", 0..255, c)
385
- end
386
- end)
552
+ assert_type red, :Number, :red
553
+ assert_type green, :Number, :green
554
+ assert_type blue, :Number, :blue
555
+
556
+ color_attrs = [[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
557
+ if c.is_unit?("%")
558
+ v = Sass::Util.check_range("$#{name}: Color value", 0..100, c, '%')
559
+ v * 255 / 100.0
560
+ elsif c.unitless?
561
+ Sass::Util.check_range("$#{name}: Color value", 0..255, c)
562
+ else
563
+ raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
564
+ end
565
+ end
566
+ Sass::Script::Value::Color.new(color_attrs)
387
567
  end
388
568
  declare :rgb, [:red, :green, :blue]
389
569
 
570
+ # Creates a {Sass::Script::Value::Color Color} from red, green, blue, and
571
+ # alpha values.
390
572
  # @see #rgb
391
- # @overload rgba(red, green, blue, alpha)
392
- # Creates a {Color} object from red, green, and blue values,
393
- # as well as an alpha channel indicating opacity.
394
- #
395
- # @param red [Number]
396
- # A number between 0 and 255 inclusive
397
- # @param green [Number]
398
- # A number between 0 and 255 inclusive
399
- # @param blue [Number]
400
- # A number between 0 and 255 inclusive
401
- # @param alpha [Number]
402
- # A number between 0 and 1
403
- # @return [Color]
404
- #
405
- # @overload rgba(color, alpha)
406
- # Sets the opacity of a color.
573
+ #
574
+ # @overload rgba($red, $green, $blue, $alpha)
575
+ # @param $red [Sass::Script::Value::Number] The amount of red in the
576
+ # color. Must be between 0 and 255 inclusive
577
+ # @param $green [Sass::Script::Value::Number] The amount of green in the
578
+ # color. Must be between 0 and 255 inclusive
579
+ # @param $blue [Sass::Script::Value::Number] The amount of blue in the
580
+ # color. Must be between 0 and 255 inclusive
581
+ # @param $alpha [Sass::Script::Value::Number] The opacity of the color.
582
+ # Must be between 0 and 1 inclusive
583
+ # @return [Sass::Script::Value::Color]
584
+ # @raise [ArgumentError] if any parameter is the wrong type or out of
585
+ # bounds
586
+ #
587
+ # @overload rgba($color, $alpha)
588
+ # Sets the opacity of an existing color.
407
589
  #
408
590
  # @example
409
591
  # rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
410
592
  # rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
411
593
  #
412
- # @param color [Color]
413
- # @param alpha [Number]
414
- # A number between 0 and 1
415
- # @return [Color]
594
+ # @param $color [Sass::Script::Value::Color] The color whose opacity will
595
+ # be changed.
596
+ # @param $alpha [Sass::Script::Value::Number] The new opacity of the
597
+ # color. Must be between 0 and 1 inclusive
598
+ # @return [Sass::Script::Value::Color]
599
+ # @raise [ArgumentError] if `$alpha` is out of bounds or either parameter
600
+ # is the wrong type
416
601
  def rgba(*args)
417
602
  case args.size
418
603
  when 2
419
604
  color, alpha = args
420
605
 
421
- assert_type color, :Color
422
- assert_type alpha, :Number
606
+ assert_type color, :Color, :color
607
+ assert_type alpha, :Number, :alpha
423
608
 
424
609
  Sass::Util.check_range('Alpha channel', 0..1, alpha)
425
610
  color.with(:alpha => alpha.value)
@@ -433,43 +618,51 @@ module Sass::Script
433
618
  declare :rgba, [:red, :green, :blue, :alpha]
434
619
  declare :rgba, [:color, :alpha]
435
620
 
436
- # Creates a {Color} object from hue, saturation, and lightness.
437
- # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
621
+ # Creates a {Sass::Script::Value::Color Color} from hue, saturation, and
622
+ # lightness values. Uses the algorithm from the [CSS3 spec][].
623
+ #
624
+ # [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
438
625
  #
439
- # @param hue [Number] The hue of the color.
440
- # Should be between 0 and 360 degrees, inclusive
441
- # @param saturation [Number] The saturation of the color.
442
- # Must be between `0%` and `100%`, inclusive
443
- # @param lightness [Number] The lightness of the color.
444
- # Must be between `0%` and `100%`, inclusive
445
- # @return [Color] The resulting color
446
626
  # @see #hsla
447
- # @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
627
+ # @overload hsl($hue, $saturation, $lightness)
628
+ # @param $hue [Sass::Script::Value::Number] The hue of the color. Should be
629
+ # between 0 and 360 degrees, inclusive
630
+ # @param $saturation [Sass::Script::Value::Number] The saturation of the
631
+ # color. Must be between `0%` and `100%`, inclusive
632
+ # @param $lightness [Sass::Script::Value::Number] The lightness of the
633
+ # color. Must be between `0%` and `100%`, inclusive
634
+ # @return [Sass::Script::Value::Color]
635
+ # @raise [ArgumentError] if `$saturation` or `$lightness` are out of bounds
636
+ # or any parameter is the wrong type
448
637
  def hsl(hue, saturation, lightness)
449
- hsla(hue, saturation, lightness, Number.new(1))
638
+ hsla(hue, saturation, lightness, number(1))
450
639
  end
451
640
  declare :hsl, [:hue, :saturation, :lightness]
452
641
 
453
- # Creates a {Color} object from hue, saturation, and lightness,
454
- # as well as an alpha channel indicating opacity.
455
- # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
456
- #
457
- # @param hue [Number] The hue of the color.
458
- # Should be between 0 and 360 degrees, inclusive
459
- # @param saturation [Number] The saturation of the color.
460
- # Must be between `0%` and `100%`, inclusive
461
- # @param lightness [Number] The lightness of the color.
462
- # Must be between `0%` and `100%`, inclusive
463
- # @param alpha [Number] The opacity of the color.
464
- # Must be between 0 and 1, inclusive
465
- # @return [Color] The resulting color
642
+ # Creates a {Sass::Script::Value::Color Color} from hue,
643
+ # saturation, lightness, and alpha values. Uses the algorithm from
644
+ # the [CSS3 spec][].
645
+ #
646
+ # [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
647
+ #
466
648
  # @see #hsl
467
- # @raise [ArgumentError] if `saturation`, `lightness`, or `alpha` are out of bounds
649
+ # @overload hsla($hue, $saturation, $lightness, $alpha)
650
+ # @param $hue [Sass::Script::Value::Number] The hue of the color. Should be
651
+ # between 0 and 360 degrees, inclusive
652
+ # @param $saturation [Sass::Script::Value::Number] The saturation of the
653
+ # color. Must be between `0%` and `100%`, inclusive
654
+ # @param $lightness [Sass::Script::Value::Number] The lightness of the
655
+ # color. Must be between `0%` and `100%`, inclusive
656
+ # @param $alpha [Sass::Script::Value::Number] The opacity of the color. Must
657
+ # be between 0 and 1, inclusive
658
+ # @return [Sass::Script::Value::Color]
659
+ # @raise [ArgumentError] if `$saturation`, `$lightness`, or `$alpha` are out
660
+ # of bounds or any parameter is the wrong type
468
661
  def hsla(hue, saturation, lightness, alpha)
469
- assert_type hue, :Number
470
- assert_type saturation, :Number
471
- assert_type lightness, :Number
472
- assert_type alpha, :Number
662
+ assert_type hue, :Number, :hue
663
+ assert_type saturation, :Number, :saturation
664
+ assert_type lightness, :Number, :lightness
665
+ assert_type alpha, :Number, :alpha
473
666
 
474
667
  Sass::Util.check_range('Alpha channel', 0..1, alpha)
475
668
 
@@ -477,149 +670,170 @@ module Sass::Script
477
670
  s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
478
671
  l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
479
672
 
480
- Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
673
+ Sass::Script::Value::Color.new(
674
+ :hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
481
675
  end
482
676
  declare :hsla, [:hue, :saturation, :lightness, :alpha]
483
677
 
484
- # Returns the red component of a color.
678
+ # Gets the red component of a color. Calculated from HSL where necessary via
679
+ # [this algorithm][hsl-to-rgb].
680
+ #
681
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
485
682
  #
486
- # @param color [Color]
487
- # @return [Number]
488
- # @raise [ArgumentError] If `color` isn't a color
683
+ # @overload red($color)
684
+ # @param $color [Sass::Script::Value::Color]
685
+ # @return [Sass::Script::Value::Number] The red component, between 0 and 255
686
+ # inclusive
687
+ # @raise [ArgumentError] if `$color` isn't a color
489
688
  def red(color)
490
- assert_type color, :Color
491
- Sass::Script::Number.new(color.red)
689
+ assert_type color, :Color, :color
690
+ number(color.red)
492
691
  end
493
692
  declare :red, [:color]
494
693
 
495
- # Returns the green component of a color.
694
+ # Gets the green component of a color. Calculated from HSL where necessary
695
+ # via [this algorithm][hsl-to-rgb].
696
+ #
697
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
496
698
  #
497
- # @param color [Color]
498
- # @return [Number]
499
- # @raise [ArgumentError] If `color` isn't a color
699
+ # @overload green($color)
700
+ # @param $color [Sass::Script::Value::Color]
701
+ # @return [Sass::Script::Value::Number] The green component, between 0 and
702
+ # 255 inclusive
703
+ # @raise [ArgumentError] if `$color` isn't a color
500
704
  def green(color)
501
- assert_type color, :Color
502
- Sass::Script::Number.new(color.green)
705
+ assert_type color, :Color, :color
706
+ number(color.green)
503
707
  end
504
708
  declare :green, [:color]
505
709
 
506
- # Returns the blue component of a color.
710
+ # Gets the blue component of a color. Calculated from HSL where necessary
711
+ # via [this algorithm][hsl-to-rgb].
507
712
  #
508
- # @param color [Color]
509
- # @return [Number]
510
- # @raise [ArgumentError] If `color` isn't a color
713
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
714
+ #
715
+ # @overload blue($color)
716
+ # @param $color [Sass::Script::Value::Color]
717
+ # @return [Sass::Script::Value::Number] The blue component, between 0 and
718
+ # 255 inclusive
719
+ # @raise [ArgumentError] if `$color` isn't a color
511
720
  def blue(color)
512
- assert_type color, :Color
513
- Sass::Script::Number.new(color.blue)
721
+ assert_type color, :Color, :color
722
+ number(color.blue)
514
723
  end
515
724
  declare :blue, [:color]
516
725
 
517
- # Returns the hue component of a color.
726
+ # Returns the hue component of a color. See [the CSS3 HSL
727
+ # specification][hsl]. Calculated from RGB where necessary via [this
728
+ # algorithm][rgb-to-hsl].
518
729
  #
519
- # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
730
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
731
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
520
732
  #
521
- # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
522
- #
523
- # @param color [Color]
524
- # @return [Number] between 0deg and 360deg
525
- # @see #adjust_hue
526
- # @raise [ArgumentError] if `color` isn't a color
733
+ # @overload hue($color)
734
+ # @param $color [Sass::Script::Value::Color]
735
+ # @return [Sass::Script::Value::Number] The hue component, between 0deg and
736
+ # 360deg
737
+ # @raise [ArgumentError] if `$color` isn't a color
527
738
  def hue(color)
739
+ assert_type color, :Color, :color
528
740
  assert_type color, :Color
529
- Sass::Script::Number.new(color.hue, ["deg"])
741
+ number(color.hue, "deg")
530
742
  end
531
743
  declare :hue, [:color]
532
744
 
533
- # Returns the saturation component of a color.
534
- #
535
- # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
745
+ # Returns the saturation component of a color. See [the CSS3 HSL
746
+ # specification][hsl]. Calculated from RGB where necessary via [this
747
+ # algorithm][rgb-to-hsl].
536
748
  #
537
- # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
749
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
750
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
538
751
  #
539
- # @param color [Color]
540
- # @return [Number] between 0% and 100%
541
- # @see #saturate
542
- # @see #desaturate
543
- # @raise [ArgumentError] if `color` isn't a color
752
+ # @overload saturation($color)
753
+ # @param $color [Sass::Script::Value::Color]
754
+ # @return [Sass::Script::Value::Number] The saturation component, between 0%
755
+ # and 100%
756
+ # @raise [ArgumentError] if `$color` isn't a color
544
757
  def saturation(color)
545
- assert_type color, :Color
546
- Sass::Script::Number.new(color.saturation, ["%"])
758
+ assert_type color, :Color, :color
759
+ number(color.saturation, "%")
547
760
  end
548
761
  declare :saturation, [:color]
549
762
 
550
- # Returns the hue component of a color.
551
- #
552
- # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
763
+ # Returns the lightness component of a color. See [the CSS3 HSL
764
+ # specification][hsl]. Calculated from RGB where necessary via [this
765
+ # algorithm][rgb-to-hsl].
553
766
  #
554
- # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
767
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
768
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
555
769
  #
556
- # @param color [Color]
557
- # @return [Number] between 0% and 100%
558
- # @see #lighten
559
- # @see #darken
560
- # @raise [ArgumentError] if `color` isn't a color
770
+ # @overload lightness($color)
771
+ # @param $color [Sass::Script::Value::Color]
772
+ # @return [Sass::Script::Value::Number] The lightness component, between 0%
773
+ # and 100%
774
+ # @raise [ArgumentError] if `$color` isn't a color
561
775
  def lightness(color)
562
- assert_type color, :Color
563
- Sass::Script::Number.new(color.lightness, ["%"])
776
+ assert_type color, :Color, :color
777
+ number(color.lightness, "%")
564
778
  end
565
779
  declare :lightness, [:color]
566
780
 
567
- # Returns the alpha component (opacity) of a color.
568
- # This is 1 unless otherwise specified.
781
+ # Returns the alpha component (opacity) of a color. This is 1 unless
782
+ # otherwise specified.
569
783
  #
570
- # This function also supports the proprietary Microsoft
571
- # `alpha(opacity=20)` syntax.
784
+ # This function also supports the proprietary Microsoft `alpha(opacity=20)`
785
+ # syntax as a special case.
572
786
  #
573
- # @overload def alpha(color)
574
- # @param color [Color]
575
- # @return [Number]
576
- # @see #opacify
577
- # @see #transparentize
578
- # @raise [ArgumentError] If `color` isn't a color
787
+ # @overload alpha($color)
788
+ # @param $color [Sass::Script::Value::Color]
789
+ # @return [Sass::Script::Value::Number] The alpha component, between 0 and 1
790
+ # @raise [ArgumentError] if `$color` isn't a color
579
791
  def alpha(*args)
580
792
  if args.all? do |a|
581
- a.is_a?(Sass::Script::String) && a.type == :identifier &&
582
- a.value =~ /^[a-zA-Z]+\s*=/
583
- end
793
+ a.is_a?(Sass::Script::Value::String) && a.type == :identifier &&
794
+ a.value =~ /^[a-zA-Z]+\s*=/
795
+ end
584
796
  # Support the proprietary MS alpha() function
585
- return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
797
+ return identifier("alpha(#{args.map {|a| a.to_s}.join(", ")})")
586
798
  end
587
799
 
588
800
  raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)") if args.size != 1
589
801
 
590
- assert_type args.first, :Color
591
- Sass::Script::Number.new(args.first.alpha)
802
+ assert_type args.first, :Color, :color
803
+ number(args.first.alpha)
592
804
  end
593
805
  declare :alpha, [:color]
594
806
 
595
- # Returns the alpha component (opacity) of a color.
596
- # This is 1 unless otherwise specified.
807
+ # Returns the alpha component (opacity) of a color. This is 1 unless
808
+ # otherwise specified.
597
809
  #
598
- # @param color [Color]
599
- # @return [Number]
600
- # @see #opacify
601
- # @see #transparentize
602
- # @raise [ArgumentError] If `color` isn't a color
810
+ # @overload opacity($color)
811
+ # @param $color [Sass::Script::Value::Color]
812
+ # @return [Sass::Script::Value::Number] The alpha component, between 0 and 1
813
+ # @raise [ArgumentError] if `$color` isn't a color
603
814
  def opacity(color)
604
- return Sass::Script::String.new("opacity(#{color})") if color.is_a?(Sass::Script::Number)
605
- assert_type color, :Color
606
- Sass::Script::Number.new(color.alpha)
815
+ if color.is_a?(Sass::Script::Value::Number)
816
+ return identifier("opacity(#{color})")
817
+ end
818
+ assert_type color, :Color, :color
819
+ number(color.alpha)
607
820
  end
608
821
  declare :opacity, [:color]
609
822
 
610
- # Makes a color more opaque.
611
- # Takes a color and an amount between 0 and 1,
612
- # and returns a color with the opacity increased by that value.
823
+ # Makes a color more opaque. Takes a color and a number between 0 and 1, and
824
+ # returns a color with the opacity increased by that amount.
613
825
  #
826
+ # @see #transparentize
614
827
  # @example
615
828
  # opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
616
829
  # opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
617
- # @param color [Color]
618
- # @param amount [Number]
619
- # @return [Color]
620
- # @see #transparentize
621
- # @raise [ArgumentError] If `color` isn't a color,
622
- # or `number` isn't a number between 0 and 1
830
+ # @overload opacify($color, $amount)
831
+ # @param $color [Sass::Script::Value::Color]
832
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
833
+ # opacity by, between 0 and 1
834
+ # @return [Sass::Script::Value::Color]
835
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
836
+ # is the wrong type
623
837
  def opacify(color, amount)
624
838
  _adjust(color, amount, :alpha, 0..1, :+)
625
839
  end
@@ -628,19 +842,20 @@ module Sass::Script
628
842
  alias_method :fade_in, :opacify
629
843
  declare :fade_in, [:color, :amount]
630
844
 
631
- # Makes a color more transparent.
632
- # Takes a color and an amount between 0 and 1,
633
- # and returns a color with the opacity decreased by that value.
845
+ # Makes a color more transparent. Takes a color and a number between 0 and
846
+ # 1, and returns a color with the opacity decreased by that amount.
634
847
  #
848
+ # @see #opacify
635
849
  # @example
636
850
  # transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
637
851
  # transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
638
- # @param color [Color]
639
- # @param amount [Number]
640
- # @return [Color]
641
- # @see #opacify
642
- # @raise [ArgumentError] If `color` isn't a color,
643
- # or `number` isn't a number between 0 and 1
852
+ # @overload transparentize($color, $amount)
853
+ # @param $color [Sass::Script::Value::Color]
854
+ # @param $amount [Sass::Script::Value::Number] The amount to decrease the
855
+ # opacity by, between 0 and 1
856
+ # @return [Sass::Script::Value::Color]
857
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
858
+ # is the wrong type
644
859
  def transparentize(color, amount)
645
860
  _adjust(color, amount, :alpha, 0..1, :-)
646
861
  end
@@ -649,153 +864,162 @@ module Sass::Script
649
864
  alias_method :fade_out, :transparentize
650
865
  declare :fade_out, [:color, :amount]
651
866
 
652
- # Makes a color lighter.
653
- # Takes a color and an amount between 0% and 100%,
654
- # and returns a color with the lightness increased by that value.
867
+ # Makes a color lighter. Takes a color and a number between `0%` and `100%`,
868
+ # and returns a color with the lightness increased by that amount.
655
869
  #
870
+ # @see #darken
656
871
  # @example
657
872
  # lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
658
873
  # lighten(#800, 20%) => #e00
659
- # @param color [Color]
660
- # @param amount [Number]
661
- # @return [Color]
662
- # @see #darken
663
- # @raise [ArgumentError] If `color` isn't a color,
664
- # or `number` isn't a number between 0% and 100%
874
+ # @overload lighten($color, $amount)
875
+ # @param $color [Sass::Script::Value::Color]
876
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
877
+ # lightness by, between `0%` and `100%`
878
+ # @return [Sass::Script::Value::Color]
879
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
880
+ # is the wrong type
665
881
  def lighten(color, amount)
666
882
  _adjust(color, amount, :lightness, 0..100, :+, "%")
667
883
  end
668
884
  declare :lighten, [:color, :amount]
669
885
 
670
- # Makes a color darker.
671
- # Takes a color and an amount between 0% and 100%,
672
- # and returns a color with the lightness decreased by that value.
886
+ # Makes a color darker. Takes a color and a number between 0% and 100%, and
887
+ # returns a color with the lightness decreased by that amount.
673
888
  #
889
+ # @see #lighten
674
890
  # @example
675
891
  # darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
676
892
  # darken(#800, 20%) => #200
677
- # @param color [Color]
678
- # @param amount [Number]
679
- # @return [Color]
680
- # @see #lighten
681
- # @raise [ArgumentError] If `color` isn't a color,
682
- # or `number` isn't a number between 0% and 100%
893
+ # @overload darken($color, $amount)
894
+ # @param $color [Sass::Script::Value::Color]
895
+ # @param $amount [Sass::Script::Value::Number] The amount to dencrease the
896
+ # lightness by, between `0%` and `100%`
897
+ # @return [Sass::Script::Value::Color]
898
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
899
+ # is the wrong type
683
900
  def darken(color, amount)
684
901
  _adjust(color, amount, :lightness, 0..100, :-, "%")
685
902
  end
686
903
  declare :darken, [:color, :amount]
687
904
 
688
- # Makes a color more saturated.
689
- # Takes a color and an amount between 0% and 100%,
690
- # and returns a color with the saturation increased by that value.
905
+ # Makes a color more saturated. Takes a color and a number between 0% and
906
+ # 100%, and returns a color with the saturation increased by that amount.
691
907
  #
908
+ # @see #desaturate
692
909
  # @example
693
910
  # saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
694
911
  # saturate(#855, 20%) => #9e3f3f
695
- # @overload saturate(color, amount)
696
- # @param color [Color]
697
- # @param amount [Number]
698
- # @return [Color]
699
- # @see #desaturate
700
- # @raise [ArgumentError] If `color` isn't a color,
701
- # or `number` isn't a number between 0% and 100%
912
+ # @overload saturate($color, $amount)
913
+ # @param $color [Sass::Script::Value::Color]
914
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
915
+ # saturation by, between `0%` and `100%`
916
+ # @return [Sass::Script::Value::Color]
917
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
918
+ # is the wrong type
702
919
  def saturate(color, amount = nil)
703
920
  # Support the filter effects definition of saturate.
704
921
  # https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
705
- return Sass::Script::String.new("saturate(#{color})") if amount.nil?
922
+ return identifier("saturate(#{color})") if amount.nil?
706
923
  _adjust(color, amount, :saturation, 0..100, :+, "%")
707
924
  end
708
925
  declare :saturate, [:color, :amount]
709
926
  declare :saturate, [:amount]
710
927
 
711
- # Makes a color less saturated.
712
- # Takes a color and an amount between 0% and 100%,
713
- # and returns a color with the saturation decreased by that value.
928
+ # Makes a color less saturated. Takes a color and a number between 0% and
929
+ # 100%, and returns a color with the saturation decreased by that value.
714
930
  #
931
+ # @see #saturate
715
932
  # @example
716
933
  # desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
717
934
  # desaturate(#855, 20%) => #726b6b
718
- # @param color [Color]
719
- # @param amount [Number]
720
- # @return [Color]
721
- # @see #saturate
722
- # @raise [ArgumentError] If `color` isn't a color,
723
- # or `number` isn't a number between 0% and 100%
935
+ # @overload desaturate($color, $amount)
936
+ # @param $color [Sass::Script::Value::Color]
937
+ # @param $amount [Sass::Script::Value::Number] The amount to decrease the
938
+ # saturation by, between `0%` and `100%`
939
+ # @return [Sass::Script::Value::Color]
940
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
941
+ # is the wrong type
724
942
  def desaturate(color, amount)
725
943
  _adjust(color, amount, :saturation, 0..100, :-, "%")
726
944
  end
727
945
  declare :desaturate, [:color, :amount]
728
946
 
729
- # Changes the hue of a color while retaining the lightness and saturation.
730
- # Takes a color and a number of degrees (usually between -360deg and 360deg),
731
- # and returns a color with the hue rotated by that value.
947
+ # Changes the hue of a color. Takes a color and a number of degrees (usually
948
+ # between `-360deg` and `360deg`), and returns a color with the hue rotated
949
+ # along the color wheel by that amount.
732
950
  #
733
951
  # @example
734
952
  # adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
735
953
  # adjust-hue(hsl(120, 30%, 90%), 060deg) => hsl(60, 30%, 90%)
736
954
  # adjust-hue(#811, 45deg) => #886a11
737
- # @param color [Color]
738
- # @param amount [Number]
739
- # @return [Color]
740
- # @raise [ArgumentError] If `color` isn't a color, or `number` isn't a number
955
+ # @overload adjust_hue($color, $degrees)
956
+ # @param $color [Sass::Script::Value::Color]
957
+ # @param $degrees [Sass::Script::Value::Number] The number of degrees to
958
+ # rotate the hue
959
+ # @return [Sass::Script::Value::Color]
960
+ # @raise [ArgumentError] if either parameter is the wrong type
741
961
  def adjust_hue(color, degrees)
742
- assert_type color, :Color
743
- assert_type degrees, :Number
962
+ assert_type color, :Color, :color
963
+ assert_type degrees, :Number, :degrees
744
964
  color.with(:hue => color.hue + degrees.value)
745
965
  end
746
966
  declare :adjust_hue, [:color, :degrees]
747
967
 
748
- # Returns an IE hex string for a color with an alpha channel
749
- # suitable for passing to IE filters.
968
+ # Converts a color into the format understood by IE filters.
750
969
  #
751
970
  # @example
752
971
  # ie-hex-str(#abc) => #FFAABBCC
753
972
  # ie-hex-str(#3322BB) => #FF3322BB
754
973
  # ie-hex-str(rgba(0, 255, 0, 0.5)) => #8000FF00
755
- # @param color [Color]
756
- # @return [String]
757
- # @raise [ArgumentError] If `color` isn't a color
974
+ # @overload ie_hex_str($color)
975
+ # @param $color [Sass::Script::Value::Color]
976
+ # @return [Sass::Script::Value::String] The IE-formatted string
977
+ # representation of the color
978
+ # @raise [ArgumentError] if `$color` isn't a color
758
979
  def ie_hex_str(color)
759
- assert_type color, :Color
980
+ assert_type color, :Color, :color
760
981
  alpha = (color.alpha * 255).round.to_s(16).rjust(2, '0')
761
- Sass::Script::String.new("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
982
+ identifier("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
762
983
  end
763
984
  declare :ie_hex_str, [:color]
764
985
 
765
- # Adjusts one or more properties of a color.
766
- # This can change the red, green, blue, hue, saturation, value, and alpha properties.
767
- # The properties are specified as keyword arguments,
768
- # and are added to or subtracted from the color's current value for that property.
986
+ # Increases or decreases one or more properties of a color. This can change
987
+ # the red, green, blue, hue, saturation, value, and alpha properties. The
988
+ # properties are specified as keyword arguments, and are added to or
989
+ # subtracted from the color's current value for that property.
769
990
  #
770
- # `$red`, `$green`, and `$blue` properties should be between 0 and 255.
771
- # `$saturation` and `$lightness` should be between 0% and 100%.
772
- # `$alpha` should be between 0 and 1.
773
- #
774
- # All properties are optional.
775
- # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
776
- # and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
991
+ # All properties are optional. You can't specify both RGB properties
992
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
993
+ # `$value`) at the same time.
777
994
  #
778
995
  # @example
779
996
  # adjust-color(#102030, $blue: 5) => #102035
780
997
  # adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
781
998
  # adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
782
- # @param color [Color]
783
- # @param red [Number]
784
- # @param green [Number]
785
- # @param blue [Number]
786
- # @param hue [Number]
787
- # @param saturation [Number]
788
- # @param lightness [Number]
789
- # @param alpha [Number]
790
- # @return [Color]
791
- # @raise [ArgumentError] if `color` is not a color,
792
- # if any keyword argument is not a number,
793
- # if any keyword argument is not in the legal range,
794
- # if an unexpected keyword argument is given,
795
- # or if both HSL and RGB properties are given.
999
+ # @overload adjust_color($color, [$red], [$green], [$blue],
1000
+ # [$hue], [$saturation], [$lightness], [$alpha])
1001
+ # @param $color [Sass::Script::Value::Color]
1002
+ # @param $red [Sass::Script::Value::Number] The adjustment to make on the
1003
+ # red component, between -255 and 255 inclusive
1004
+ # @param $green [Sass::Script::Value::Number] The adjustment to make on the
1005
+ # green component, between -255 and 255 inclusive
1006
+ # @param $blue [Sass::Script::Value::Number] The adjustment to make on the
1007
+ # blue component, between -255 and 255 inclusive
1008
+ # @param $hue [Sass::Script::Value::Number] The adjustment to make on the
1009
+ # hue component, in degrees
1010
+ # @param $saturation [Sass::Script::Value::Number] The adjustment to make on
1011
+ # the saturation component, between `-100%` and `100%` inclusive
1012
+ # @param $lightness [Sass::Script::Value::Number] The adjustment to make on
1013
+ # the lightness component, between `-100%` and `100%` inclusive
1014
+ # @param $alpha [Sass::Script::Value::Number] The adjustment to make on the
1015
+ # alpha component, between -1 and 1 inclusive
1016
+ # @return [Sass::Script::Value::Color]
1017
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1018
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1019
+ # same time
796
1020
  def adjust_color(color, kwargs)
797
- assert_type color, :Color
798
- with = Sass::Util.map_hash({
1021
+ assert_type color, :Color, :color
1022
+ with = Sass::Util.map_hash(
799
1023
  "red" => [-255..255, ""],
800
1024
  "green" => [-255..255, ""],
801
1025
  "blue" => [-255..255, ""],
@@ -803,9 +1027,10 @@ module Sass::Script
803
1027
  "saturation" => [-100..100, "%"],
804
1028
  "lightness" => [-100..100, "%"],
805
1029
  "alpha" => [-1..1, ""]
806
- }) do |name, (range, units)|
1030
+ ) do |name, (range, units)|
807
1031
 
808
- next unless val = kwargs.delete(name)
1032
+ val = kwargs.delete(name)
1033
+ next unless val
809
1034
  assert_type val, :Number, name
810
1035
  Sass::Util.check_range("$#{name}: Amount", range, val, units) if range
811
1036
  adjusted = color.send(name) + val.value
@@ -822,68 +1047,67 @@ module Sass::Script
822
1047
  end
823
1048
  declare :adjust_color, [:color], :var_kwargs => true
824
1049
 
825
- # Scales one or more properties of a color by a percentage value.
826
- # Unlike \{#adjust_color adjust-color}, which changes a color's properties by fixed amounts,
827
- # \{#scale_color scale-color} fluidly changes them based on how high or low they already are.
828
- # That means that lightening an already-light color with \{#scale_color scale-color}
829
- # won't change the lightness much,
830
- # but lightening a dark color by the same amount will change it more dramatically.
831
- # This has the benefit of making `scale-color($color, ...)` have a similar effect
832
- # regardless of what `$color` is.
833
- #
834
- # For example, the lightness of a color can be anywhere between 0 and 100.
835
- # If `scale-color($color, $lightness: 40%)` is called, the resulting color's lightness
836
- # will be 40% of the way between its original lightness and 100.
837
- # If `scale-color($color, $lightness: -40%)` is called instead,
838
- # the lightness will be 40% of the way between the original and 0.
839
- #
840
- # This can change the red, green, blue, saturation, value, and alpha properties.
841
- # The properties are specified as keyword arguments.
842
- # All arguments should be percentages between 0% and 100%.
843
- #
844
- # All properties are optional.
845
- # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
846
- # and HSL properties (`$saturation`, `$value`) at the same time.
1050
+ # Fluidly scales one or more properties of a color. Unlike
1051
+ # \{#adjust_color adjust-color}, which changes a color's properties by fixed
1052
+ # amounts, \{#scale_color scale-color} fluidly changes them based on how
1053
+ # high or low they already are. That means that lightening an already-light
1054
+ # color with \{#scale_color scale-color} won't change the lightness much,
1055
+ # but lightening a dark color by the same amount will change it more
1056
+ # dramatically. This has the benefit of making `scale-color($color, ...)`
1057
+ # have a similar effect regardless of what `$color` is.
1058
+ #
1059
+ # For example, the lightness of a color can be anywhere between `0%` and
1060
+ # `100%`. If `scale-color($color, $lightness: 40%)` is called, the resulting
1061
+ # color's lightness will be 40% of the way between its original lightness
1062
+ # and 100. If `scale-color($color, $lightness: -40%)` is called instead, the
1063
+ # lightness will be 40% of the way between the original and 0.
1064
+ #
1065
+ # This can change the red, green, blue, saturation, value, and alpha
1066
+ # properties. The properties are specified as keyword arguments. All
1067
+ # arguments should be percentages between `0%` and `100%`.
1068
+ #
1069
+ # All properties are optional. You can't specify both RGB properties
1070
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$saturation`, `$value`)
1071
+ # at the same time.
847
1072
  #
848
1073
  # @example
849
- # scale-color(hsl(120, 70, 80), $lightness: 50%) => hsl(120, 70, 90)
850
- # scale-color(rgb(200, 150, 170), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
851
- # scale-color(hsl(200, 70, 80), $saturation: -90%, $alpha: -30%) => hsla(200, 7, 80, 0.7)
852
- # @param color [Color]
853
- # @param red [Number]
854
- # @param green [Number]
855
- # @param blue [Number]
856
- # @param saturation [Number]
857
- # @param lightness [Number]
858
- # @param alpha [Number]
859
- # @return [Color]
860
- # @raise [ArgumentError] if `color` is not a color,
861
- # if any keyword argument is not a percentage between 0% and 100%,
862
- # if an unexpected keyword argument is given,
863
- # or if both HSL and RGB properties are given.
1074
+ # scale-color(hsl(120, 70%, 80%), $lightness: 50%) => hsl(120, 70%, 90%)
1075
+ # scale-color(rgb(200, 150%, 170%), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
1076
+ # scale-color(hsl(200, 70%, 80%), $saturation: -90%, $alpha: -30%) => hsla(200, 7%, 80%, 0.7)
1077
+ # @overload scale_color($color, [$red], [$green], [$blue],
1078
+ # [$saturation], [$lightness], [$alpha])
1079
+ # @param $color [Sass::Script::Value::Color]
1080
+ # @param $red [Sass::Script::Value::Number]
1081
+ # @param $green [Sass::Script::Value::Number]
1082
+ # @param $blue [Sass::Script::Value::Number]
1083
+ # @param $saturation [Sass::Script::Value::Number]
1084
+ # @param $lightness [Sass::Script::Value::Number]
1085
+ # @param $alpha [Sass::Script::Value::Number]
1086
+ # @return [Sass::Script::Value::Color]
1087
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1088
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1089
+ # same time
864
1090
  def scale_color(color, kwargs)
865
- assert_type color, :Color
866
- with = Sass::Util.map_hash({
1091
+ assert_type color, :Color, :color
1092
+ with = Sass::Util.map_hash(
867
1093
  "red" => 255,
868
1094
  "green" => 255,
869
1095
  "blue" => 255,
870
1096
  "saturation" => 100,
871
1097
  "lightness" => 100,
872
1098
  "alpha" => 1
873
- }) do |name, max|
1099
+ ) do |name, max|
874
1100
 
875
- next unless val = kwargs.delete(name)
1101
+ val = kwargs.delete(name)
1102
+ next unless val
876
1103
  assert_type val, :Number, name
877
- if !(val.numerator_units == ['%'] && val.denominator_units.empty?)
878
- raise ArgumentError.new("$#{name}: Amount #{val} must be a % (e.g. #{val.value}%)")
879
- else
880
- Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
881
- end
1104
+ assert_unit val, '%', name
1105
+ Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
882
1106
 
883
1107
  current = color.send(name)
884
- scale = val.value/100.0
1108
+ scale = val.value / 100.0
885
1109
  diff = scale > 0 ? max - current : current
886
- [name.to_sym, current + diff*scale]
1110
+ [name.to_sym, current + diff * scale]
887
1111
  end
888
1112
 
889
1113
  unless kwargs.empty?
@@ -895,41 +1119,45 @@ module Sass::Script
895
1119
  end
896
1120
  declare :scale_color, [:color], :var_kwargs => true
897
1121
 
898
- # Changes one or more properties of a color.
899
- # This can change the red, green, blue, hue, saturation, value, and alpha properties.
900
- # The properties are specified as keyword arguments,
901
- # and replace the color's current value for that property.
1122
+ # Changes one or more properties of a color. This can change the red, green,
1123
+ # blue, hue, saturation, value, and alpha properties. The properties are
1124
+ # specified as keyword arguments, and replace the color's current value for
1125
+ # that property.
902
1126
  #
903
- # `$red`, `$green`, and `$blue` properties should be between 0 and 255.
904
- # `$saturation` and `$lightness` should be between 0% and 100%.
905
- # `$alpha` should be between 0 and 1.
906
- #
907
- # All properties are optional.
908
- # You can't specify both RGB properties (`$red`, `$green`, `$blue`)
909
- # and HSL properties (`$hue`, `$saturation`, `$value`) at the same time.
1127
+ # All properties are optional. You can't specify both RGB properties
1128
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
1129
+ # `$value`) at the same time.
910
1130
  #
911
1131
  # @example
912
1132
  # change-color(#102030, $blue: 5) => #102005
913
1133
  # change-color(#102030, $red: 120, $blue: 5) => #782005
914
1134
  # change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
915
- # @param color [Color]
916
- # @param red [Number]
917
- # @param green [Number]
918
- # @param blue [Number]
919
- # @param hue [Number]
920
- # @param saturation [Number]
921
- # @param lightness [Number]
922
- # @param alpha [Number]
923
- # @return [Color]
924
- # @raise [ArgumentError] if `color` is not a color,
925
- # if any keyword argument is not a number,
926
- # if any keyword argument is not in the legal range,
927
- # if an unexpected keyword argument is given,
928
- # or if both HSL and RGB properties are given.
1135
+ # @overload change_color($color, [$red], [$green], [$blue], [$hue],
1136
+ # [$saturation], [$lightness], [$alpha])
1137
+ # @param $color [Sass::Script::Value::Color]
1138
+ # @param $red [Sass::Script::Value::Number] The new red component for the
1139
+ # color, within 0 and 255 inclusive
1140
+ # @param $green [Sass::Script::Value::Number] The new green component for
1141
+ # the color, within 0 and 255 inclusive
1142
+ # @param $blue [Sass::Script::Value::Number] The new blue component for the
1143
+ # color, within 0 and 255 inclusive
1144
+ # @param $hue [Sass::Script::Value::Number] The new hue component for the
1145
+ # color, in degrees
1146
+ # @param $saturation [Sass::Script::Value::Number] The new saturation
1147
+ # component for the color, between `0%` and `100%` inclusive
1148
+ # @param $lightness [Sass::Script::Value::Number] The new lightness
1149
+ # component for the color, within `0%` and `100%` inclusive
1150
+ # @param $alpha [Sass::Script::Value::Number] The new alpha component for
1151
+ # the color, within 0 and 1 inclusive
1152
+ # @return [Sass::Script::Value::Color]
1153
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1154
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1155
+ # same time
929
1156
  def change_color(color, kwargs)
930
- assert_type color, :Color
1157
+ assert_type color, :Color, :color
931
1158
  with = Sass::Util.map_hash(%w[red green blue hue saturation lightness alpha]) do |name, max|
932
- next unless val = kwargs.delete(name)
1159
+ val = kwargs.delete(name)
1160
+ next unless val
933
1161
  assert_type val, :Number, name
934
1162
  [name.to_sym, val.value]
935
1163
  end
@@ -943,33 +1171,32 @@ module Sass::Script
943
1171
  end
944
1172
  declare :change_color, [:color], :var_kwargs => true
945
1173
 
946
- # Mixes together two colors.
947
- # Specifically, takes the average of each of the RGB components,
948
- # optionally weighted by the given percentage.
949
- # The opacity of the colors is also considered when weighting the components.
1174
+ # Mixes two colors together. Specifically, takes the average of each of the
1175
+ # RGB components, optionally weighted by the given percentage. The opacity
1176
+ # of the colors is also considered when weighting the components.
950
1177
  #
951
1178
  # The weight specifies the amount of the first color that should be included
952
- # in the returned color.
953
- # The default, 50%, means that half the first color
954
- # and half the second color should be used.
955
- # 25% means that a quarter of the first color
956
- # and three quarters of the second color should be used.
1179
+ # in the returned color. The default, `50%`, means that half the first color
1180
+ # and half the second color should be used. `25%` means that a quarter of
1181
+ # the first color and three quarters of the second color should be used.
957
1182
  #
958
1183
  # @example
959
1184
  # mix(#f00, #00f) => #7f007f
960
1185
  # mix(#f00, #00f, 25%) => #3f00bf
961
1186
  # mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
962
- # @overload mix(color1, color2, weight: 50%)
963
- # @param color1 [Color]
964
- # @param color2 [Color]
965
- # @param weight [Number] between 0% and 100%
966
- # @return [Color]
967
- # @raise [ArgumentError] if `color1` or `color2` aren't colors,
968
- # or `weight` isn't a number between 0% and 100%
969
- def mix(color1, color2, weight = Number.new(50))
970
- assert_type color1, :Color
971
- assert_type color2, :Color
972
- assert_type weight, :Number
1187
+ # @overload mix($color-1, $color-2, $weight: 50%)
1188
+ # @param $color-1 [Sass::Script::Value::Color]
1189
+ # @param $color-2 [Sass::Script::Value::Color]
1190
+ # @param $weight [Sass::Script::Value::Number] The relative weight of each
1191
+ # color. Closer to `0%` gives more weight to `$color`, closer to `100%`
1192
+ # gives more weight to `$color2`
1193
+ # @return [Sass::Script::Value::Color]
1194
+ # @raise [ArgumentError] if `$weight` is out of bounds or any parameter is
1195
+ # the wrong type
1196
+ def mix(color_1, color_2, weight = number(50))
1197
+ assert_type color_1, :Color, :color_1
1198
+ assert_type color_2, :Color, :color_2
1199
+ assert_type weight, :Number, :weight
973
1200
 
974
1201
  Sass::Util.check_range("Weight", 0..100, weight, '%')
975
1202
 
@@ -978,11 +1205,11 @@ module Sass::Script
978
1205
  # to perform the weighted average of the two RGB values.
979
1206
  #
980
1207
  # It works by first normalizing both parameters to be within [-1, 1],
981
- # where 1 indicates "only use color1", -1 indicates "only use color2", and
1208
+ # where 1 indicates "only use color_1", -1 indicates "only use color_2", and
982
1209
  # all values in between indicated a proportionately weighted average.
983
1210
  #
984
1211
  # Once we have the normalized variables w and a, we apply the formula
985
- # (w + a)/(1 + w*a) to get the combined weight (in [-1, 1]) of color1.
1212
+ # (w + a)/(1 + w*a) to get the combined weight (in [-1, 1]) of color_1.
986
1213
  # This formula has two especially nice properties:
987
1214
  #
988
1215
  # * When either w or a are -1 or 1, the combined weight is also that number
@@ -990,57 +1217,64 @@ module Sass::Script
990
1217
  #
991
1218
  # * When a is 0, the combined weight is w, and vice versa.
992
1219
  #
993
- # Finally, the weight of color1 is renormalized to be within [0, 1]
994
- # and the weight of color2 is given by 1 minus the weight of color1.
995
- p = (weight.value/100.0).to_f
996
- w = p*2 - 1
997
- a = color1.alpha - color2.alpha
1220
+ # Finally, the weight of color_1 is renormalized to be within [0, 1]
1221
+ # and the weight of color_2 is given by 1 minus the weight of color_1.
1222
+ p = (weight.value / 100.0).to_f
1223
+ w = p * 2 - 1
1224
+ a = color_1.alpha - color_2.alpha
998
1225
 
999
- w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
1226
+ w1 = ((w * a == -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0
1000
1227
  w2 = 1 - w1
1001
1228
 
1002
- rgb = color1.rgb.zip(color2.rgb).map {|v1, v2| v1*w1 + v2*w2}
1003
- alpha = color1.alpha*p + color2.alpha*(1-p)
1004
- Color.new(rgb + [alpha])
1229
+ rgba = color_1.rgb.zip(color_2.rgb).map {|v1, v2| v1 * w1 + v2 * w2}
1230
+ rgba << color_1.alpha * p + color_2.alpha * (1 - p)
1231
+ rgb_color(*rgba)
1005
1232
  end
1006
1233
  declare :mix, [:color_1, :color_2]
1007
1234
  declare :mix, [:color_1, :color_2, :weight]
1008
1235
 
1009
- # Converts a color to grayscale.
1010
- # This is identical to `desaturate(color, 100%)`.
1236
+ # Converts a color to grayscale. This is identical to `desaturate(color,
1237
+ # 100%)`.
1011
1238
  #
1012
- # @param color [Color]
1013
- # @return [Color]
1014
- # @raise [ArgumentError] if `color` isn't a color
1015
1239
  # @see #desaturate
1240
+ # @overload grayscale($color)
1241
+ # @param $color [Sass::Script::Value::Color]
1242
+ # @return [Sass::Script::Value::Color]
1243
+ # @raise [ArgumentError] if `$color` isn't a color
1016
1244
  def grayscale(color)
1017
- return Sass::Script::String.new("grayscale(#{color})") if color.is_a?(Sass::Script::Number)
1018
- desaturate color, Number.new(100)
1245
+ if color.is_a?(Sass::Script::Value::Number)
1246
+ return identifier("grayscale(#{color})")
1247
+ end
1248
+ desaturate color, number(100)
1019
1249
  end
1020
1250
  declare :grayscale, [:color]
1021
1251
 
1022
- # Returns the complement of a color.
1023
- # This is identical to `adjust-hue(color, 180deg)`.
1252
+ # Returns the complement of a color. This is identical to `adjust-hue(color,
1253
+ # 180deg)`.
1024
1254
  #
1025
- # @param color [Color]
1026
- # @return [Color]
1027
- # @raise [ArgumentError] if `color` isn't a color
1028
1255
  # @see #adjust_hue #adjust-hue
1256
+ # @overload complement($color)
1257
+ # @param $color [Sass::Script::Value::Color]
1258
+ # @return [Sass::Script::Value::Color]
1259
+ # @raise [ArgumentError] if `$color` isn't a color
1029
1260
  def complement(color)
1030
- adjust_hue color, Number.new(180)
1261
+ adjust_hue color, number(180)
1031
1262
  end
1032
1263
  declare :complement, [:color]
1033
1264
 
1034
- # Returns the inverse (negative) of a color.
1035
- # The red, green, and blue values are inverted, while the opacity is left alone.
1265
+ # Returns the inverse (negative) of a color. The red, green, and blue values
1266
+ # are inverted, while the opacity is left alone.
1036
1267
  #
1037
- # @param color [Color]
1038
- # @return [Color]
1039
- # @raise [ArgumentError] if `color` isn't a color
1268
+ # @overload invert($color)
1269
+ # @param $color [Sass::Script::Value::Color]
1270
+ # @return [Sass::Script::Value::Color]
1271
+ # @raise [ArgumentError] if `$color` isn't a color
1040
1272
  def invert(color)
1041
- return Sass::Script::String.new("invert(#{color})") if color.is_a?(Sass::Script::Number)
1273
+ if color.is_a?(Sass::Script::Value::Number)
1274
+ return identifier("invert(#{color})")
1275
+ end
1042
1276
 
1043
- assert_type color, :Color
1277
+ assert_type color, :Color, :color
1044
1278
  color.with(
1045
1279
  :red => (255 - color.red),
1046
1280
  :green => (255 - color.green),
@@ -1048,19 +1282,20 @@ module Sass::Script
1048
1282
  end
1049
1283
  declare :invert, [:color]
1050
1284
 
1051
- # Removes quotes from a string if the string is quoted,
1052
- # or returns the same string if it's not.
1285
+ # Removes quotes from a string. If the string is already unquoted, this will
1286
+ # return it unmodified.
1053
1287
  #
1054
- # @param string [String]
1055
- # @return [String]
1056
- # @raise [ArgumentError] if `string` isn't a string
1057
1288
  # @see #quote
1058
1289
  # @example
1059
1290
  # unquote("foo") => foo
1060
1291
  # unquote(foo) => foo
1292
+ # @overload unquote($string)
1293
+ # @param $string [Sass::Script::Value::String]
1294
+ # @return [Sass::Script::Value::String]
1295
+ # @raise [ArgumentError] if `$string` isn't a string
1061
1296
  def unquote(string)
1062
- if string.is_a?(Sass::Script::String)
1063
- Sass::Script::String.new(string.value, :identifier)
1297
+ if string.is_a?(Sass::Script::Value::String) && string.type != :identifier
1298
+ identifier(string.value)
1064
1299
  else
1065
1300
  string
1066
1301
  end
@@ -1070,20 +1305,170 @@ module Sass::Script
1070
1305
  # Add quotes to a string if the string isn't quoted,
1071
1306
  # or returns the same string if it is.
1072
1307
  #
1073
- # @param string [String]
1074
- # @return [String]
1075
- # @raise [ArgumentError] if `string` isn't a string
1076
1308
  # @see #unquote
1077
1309
  # @example
1078
1310
  # quote("foo") => "foo"
1079
1311
  # quote(foo) => "foo"
1312
+ # @overload quote($string)
1313
+ # @param $string [Sass::Script::Value::String]
1314
+ # @return [Sass::Script::Value::String]
1315
+ # @raise [ArgumentError] if `$string` isn't a string
1080
1316
  def quote(string)
1081
- assert_type string, :String
1082
- Sass::Script::String.new(string.value, :string)
1317
+ assert_type string, :String, :string
1318
+ if string.type != :string
1319
+ quoted_string(string.value)
1320
+ else
1321
+ string
1322
+ end
1083
1323
  end
1084
1324
  declare :quote, [:string]
1085
1325
 
1086
- # Inspects the type of the argument, returning it as an unquoted string.
1326
+ # Returns the number of characters in a string.
1327
+ #
1328
+ # @example
1329
+ # str-length("foo") => 3
1330
+ # @overload str_length($string)
1331
+ # @param $string [Sass::Script::Value::String]
1332
+ # @return [Sass::Script::Value::Number]
1333
+ # @raise [ArgumentError] if `$string` isn't a string
1334
+ def str_length(string)
1335
+ assert_type string, :String, :string
1336
+ number(string.value.size)
1337
+ end
1338
+ declare :str_length, [:string]
1339
+
1340
+ # Inserts `$insert` into `$string` at `$index`.
1341
+ #
1342
+ # Note that unlike some languages, the first character in a Sass string is
1343
+ # number 1, the second number 2, and so forth.
1344
+ #
1345
+ # @example
1346
+ # str-insert("abcd", "X", 1) => "Xabcd"
1347
+ # str-insert("abcd", "X", 4) => "abcXd"
1348
+ # str-insert("abcd", "X", 5) => "abcdX"
1349
+ #
1350
+ # @overload str_insert($string, $insert, $index)
1351
+ # @param $string [Sass::Script::Value::String]
1352
+ # @param $insert [Sass::Script::Value::String]
1353
+ # @param $index [Sass::Script::Value::Number] The position at which
1354
+ # `$insert` will be inserted. Negative indices count from the end of
1355
+ # `$string`. An index that's outside the bounds of the string will insert
1356
+ # `$insert` at the front or back of the string
1357
+ # @return [Sass::Script::Value::String] The result string. This will be
1358
+ # quoted if and only if `$string` was quoted
1359
+ # @raise [ArgumentError] if any parameter is the wrong type
1360
+ def str_insert(original, insert, index)
1361
+ assert_type original, :String, :string
1362
+ assert_type insert, :String, :insert
1363
+ assert_integer index, :index
1364
+ assert_unit index, nil, :index
1365
+ insertion_point = if index.value > 0
1366
+ [index.value - 1, original.value.size].min
1367
+ else
1368
+ [index.value, -original.value.size - 1].max
1369
+ end
1370
+ result = original.value.dup.insert(insertion_point, insert.value)
1371
+ Sass::Script::Value::String.new(result, original.type)
1372
+ end
1373
+ declare :str_insert, [:string, :insert, :index]
1374
+
1375
+ # Returns the index of the first occurance of `$substring` in `$string`. If
1376
+ # there is no such occurance, returns 0.
1377
+ #
1378
+ # Note that unlike some languages, the first character in a Sass string is
1379
+ # number 1, the second number 2, and so forth.
1380
+ #
1381
+ # @example
1382
+ # str-index(abcd, a) => 1
1383
+ # str-index(abcd, ab) => 1
1384
+ # str-index(abcd, X) => 0
1385
+ # str-index(abcd, c) => 3
1386
+ #
1387
+ # @overload str_index($string, $substring)
1388
+ # @param $string [Sass::Script::Value::String]
1389
+ # @param $substring [Sass::Script::Value::String]
1390
+ # @return [Sass::Script::Value::Number]
1391
+ # @raise [ArgumentError] if any parameter is the wrong type
1392
+ def str_index(string, substring)
1393
+ assert_type string, :String, :string
1394
+ assert_type substring, :String, :substring
1395
+ index = string.value.index(substring.value) || -1
1396
+ number(index + 1)
1397
+ end
1398
+ declare :str_index, [:string, :substring]
1399
+
1400
+ # Extracts a substring from `$string`. The substring will begin at index
1401
+ # `$start-at` and ends at index `$end-at`.
1402
+ #
1403
+ # Note that unlike some languages, the first character in a Sass string is
1404
+ # number 1, the second number 2, and so forth.
1405
+ #
1406
+ # @example
1407
+ # str-slice("abcd", 2, 3) => "bc"
1408
+ # str-slice("abcd", 2) => "bcd"
1409
+ # str-slice("abcd", -3, -2) => "bc"
1410
+ # str-slice("abcd", 2, -2) => "bc"
1411
+ #
1412
+ # @overload str_slice($string, $start-at, $end-at: -1)
1413
+ # @param $start-at [Sass::Script::Value::Number] The index of the first
1414
+ # character of the substring. If this is negative, it counts from the end
1415
+ # of `$string`
1416
+ # @param $end-before [Sass::Script::Value::Number] The index of the last
1417
+ # character of the substring. If this is negative, it counts from the end
1418
+ # of `$string`. Defaults to -1
1419
+ # @return [Sass::Script::Value::String] The substring. This will be quoted
1420
+ # if and only if `$string` was quoted
1421
+ # @raise [ArgumentError] if any parameter is the wrong type
1422
+ def str_slice(string, start_at, end_at = nil)
1423
+ assert_type string, :String, :string
1424
+ assert_unit start_at, nil, "start-at"
1425
+
1426
+ end_at = number(-1) if end_at.nil?
1427
+ assert_unit end_at, nil, "end-at"
1428
+
1429
+ s = start_at.value > 0 ? start_at.value - 1 : start_at.value
1430
+ e = end_at.value > 0 ? end_at.value - 1 : end_at.value
1431
+ s = string.value.length + s if s < 0
1432
+ s = 0 if s < 0
1433
+ e = string.value.length + e if e < 0
1434
+ e = 0 if s < 0
1435
+ extracted = string.value.slice(s..e)
1436
+ Sass::Script::Value::String.new(extracted || "", string.type)
1437
+ end
1438
+ declare :str_slice, [:string, :start_at]
1439
+ declare :str_slice, [:string, :start_at, :end_at]
1440
+
1441
+ # Converts a string to upper case.
1442
+ #
1443
+ # @example
1444
+ # to-upper-case(abcd) => ABCD
1445
+ #
1446
+ # @overload to_upper_case($string)
1447
+ # @param $string [Sass::Script::Value::String]
1448
+ # @return [Sass::Script::Value::String]
1449
+ # @raise [ArgumentError] if `$string` isn't a string
1450
+ def to_upper_case(string)
1451
+ assert_type string, :String, :string
1452
+ Sass::Script::Value::String.new(string.value.upcase, string.type)
1453
+ end
1454
+ declare :to_upper_case, [:string]
1455
+
1456
+ # Convert a string to lower case,
1457
+ #
1458
+ # @example
1459
+ # to-lower-case(ABCD) => abcd
1460
+ #
1461
+ # @overload to_lower_case($string)
1462
+ # @param $string [Sass::Script::Value::String]
1463
+ # @return [Sass::Script::Value::String]
1464
+ # @raise [ArgumentError] if `$string` isn't a string
1465
+ def to_lower_case(string)
1466
+ assert_type string, :String, :string
1467
+ Sass::Script::Value::String.new(string.value.downcase, string.type)
1468
+ end
1469
+ declare :to_lower_case, [:string]
1470
+
1471
+ # Returns the type of a value.
1087
1472
  #
1088
1473
  # @example
1089
1474
  # type-of(100px) => number
@@ -1092,15 +1477,33 @@ module Sass::Script
1092
1477
  # type-of(true) => bool
1093
1478
  # type-of(#fff) => color
1094
1479
  # type-of(blue) => color
1095
- # @param value [Literal] The object to inspect
1096
- # @return [String] The unquoted string name of the literal's type
1480
+ # @overload type_of($value)
1481
+ # @param $value [Sass::Script::Value::Base] The value to inspect
1482
+ # @return [Sass::Script::Value::String] The unquoted string name of the
1483
+ # value's type
1097
1484
  def type_of(value)
1098
- Sass::Script::String.new(value.class.name.gsub(/Sass::Script::/,'').downcase)
1485
+ identifier(value.class.name.gsub(/Sass::Script::Value::/, '').downcase)
1099
1486
  end
1100
1487
  declare :type_of, [:value]
1101
1488
 
1102
- # Inspects the unit of the number, returning it as a quoted string.
1103
- # Complex units are sorted in alphabetical order by numerator and denominator.
1489
+ # Returns whether a feature exists in the current Sass runtime.
1490
+ #
1491
+ # @example
1492
+ # feature-exists(some-feature-that-exists) => true
1493
+ # feature-exists(what-is-this-i-dont-know) => false
1494
+ #
1495
+ # @overload feature_exists($feature)
1496
+ # @param $feature [Sass::Script::Value::String] The name of the feature
1497
+ # @return [Sass::Script::Value::Bool] Whether the feature is supported in this version of Sass
1498
+ # @raise [ArgumentError] if `$feature` isn't a string
1499
+ def feature_exists(feature)
1500
+ assert_type feature, :String, :feature
1501
+ bool(Sass.has_feature?(feature.value))
1502
+ end
1503
+ declare :feature_exists, [:feature]
1504
+
1505
+ # Returns the unit(s) associated with a number. Complex units are sorted in
1506
+ # alphabetical order by numerator and denominator.
1104
1507
  #
1105
1508
  # @example
1106
1509
  # unit(100) => ""
@@ -1108,58 +1511,64 @@ module Sass::Script
1108
1511
  # unit(3em) => "em"
1109
1512
  # unit(10px * 5em) => "em*px"
1110
1513
  # unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
1111
- # @param number [Literal] The number to inspect
1112
- # @return [String] The unit(s) of the number
1113
- # @raise [ArgumentError] if `number` isn't a number
1514
+ # @overload unit($number)
1515
+ # @param $number [Sass::Script::Value::Number]
1516
+ # @return [Sass::Script::Value::String] The unit(s) of the number, as a
1517
+ # quoted string
1518
+ # @raise [ArgumentError] if `$number` isn't a number
1114
1519
  def unit(number)
1115
- assert_type number, :Number
1116
- Sass::Script::String.new(number.unit_str, :string)
1520
+ assert_type number, :Number, :number
1521
+ quoted_string(number.unit_str)
1117
1522
  end
1118
1523
  declare :unit, [:number]
1119
1524
 
1120
- # Inspects the unit of the number, returning a boolean indicating if it is unitless.
1525
+ # Returns whether a number has units.
1121
1526
  #
1122
1527
  # @example
1123
1528
  # unitless(100) => true
1124
1529
  # unitless(100px) => false
1125
- # @param number [Literal] The number to inspect
1126
- # @return [Bool] Whether or not the number is unitless
1127
- # @raise [ArgumentError] if `number` isn't a number
1530
+ # @overload unitless($number)
1531
+ # @param $number [Sass::Script::Value::Number]
1532
+ # @return [Sass::Script::Value::Bool]
1533
+ # @raise [ArgumentError] if `$number` isn't a number
1128
1534
  def unitless(number)
1129
- assert_type number, :Number
1130
- Sass::Script::Bool.new(number.unitless?)
1535
+ assert_type number, :Number, :number
1536
+ bool(number.unitless?)
1131
1537
  end
1132
1538
  declare :unitless, [:number]
1133
1539
 
1134
- # Returns true if two numbers are similar enough to be added, subtracted, or compared.
1540
+ # Returns whether two numbers can added, subtracted, or compared.
1135
1541
  #
1136
1542
  # @example
1137
1543
  # comparable(2px, 1px) => true
1138
1544
  # comparable(100px, 3em) => false
1139
1545
  # comparable(10cm, 3mm) => true
1140
- # @param number_1 [Number]
1141
- # @param number_2 [Number]
1142
- # @return [Bool] indicating if the numbers can be compared.
1143
- # @raise [ArgumentError] if `number_1` or `number_2` aren't numbers
1546
+ # @overload comparable($number-1, $number-2)
1547
+ # @param $number-1 [Sass::Script::Value::Number]
1548
+ # @param $number-2 [Sass::Script::Value::Number]
1549
+ # @return [Sass::Script::Value::Bool]
1550
+ # @raise [ArgumentError] if either parameter is the wrong type
1144
1551
  def comparable(number_1, number_2)
1145
- assert_type number_1, :Number
1146
- assert_type number_2, :Number
1147
- Sass::Script::Bool.new(number_1.comparable_to?(number_2))
1552
+ assert_type number_1, :Number, :number_1
1553
+ assert_type number_2, :Number, :number_2
1554
+ bool(number_1.comparable_to?(number_2))
1148
1555
  end
1149
1556
  declare :comparable, [:number_1, :number_2]
1150
1557
 
1151
- # Converts a decimal number to a percentage.
1558
+ # Converts a unitless number to a percentage.
1152
1559
  #
1153
1560
  # @example
1561
+ # percentage(0.2) => 20%
1154
1562
  # percentage(100px / 50px) => 200%
1155
- # @param value [Number] The decimal number to convert to a percentage
1156
- # @return [Number] The percentage
1157
- # @raise [ArgumentError] If `value` isn't a unitless number
1563
+ # @overload percentage($value)
1564
+ # @param $value [Sass::Script::Value::Number]
1565
+ # @return [Sass::Script::Value::Number]
1566
+ # @raise [ArgumentError] if `$value` isn't a unitless number
1158
1567
  def percentage(value)
1159
- unless value.is_a?(Sass::Script::Number) && value.unitless?
1160
- raise ArgumentError.new("#{value.inspect} is not a unitless number")
1568
+ unless value.is_a?(Sass::Script::Value::Number) && value.unitless?
1569
+ raise ArgumentError.new("$value: #{value.inspect} is not a unitless number")
1161
1570
  end
1162
- Sass::Script::Number.new(value.value * 100, ['%'])
1571
+ number(value.value * 100, '%')
1163
1572
  end
1164
1573
  declare :percentage, [:value]
1165
1574
 
@@ -1168,76 +1577,83 @@ module Sass::Script
1168
1577
  # @example
1169
1578
  # round(10.4px) => 10px
1170
1579
  # round(10.6px) => 11px
1171
- # @param value [Number] The number
1172
- # @return [Number] The rounded number
1173
- # @raise [ArgumentError] if `value` isn't a number
1580
+ # @overload round($value)
1581
+ # @param $value [Sass::Script::Value::Number]
1582
+ # @return [Sass::Script::Value::Number]
1583
+ # @raise [ArgumentError] if `$value` isn't a number
1174
1584
  def round(value)
1175
1585
  numeric_transformation(value) {|n| n.round}
1176
1586
  end
1177
1587
  declare :round, [:value]
1178
1588
 
1179
- # Rounds a number up to the nearest whole number.
1589
+ # Rounds a number up to the next whole number.
1180
1590
  #
1181
1591
  # @example
1182
1592
  # ceil(10.4px) => 11px
1183
1593
  # ceil(10.6px) => 11px
1184
- # @param value [Number] The number
1185
- # @return [Number] The rounded number
1186
- # @raise [ArgumentError] if `value` isn't a number
1594
+ # @overload ceil($value)
1595
+ # @param $value [Sass::Script::Value::Number]
1596
+ # @return [Sass::Script::Value::Number]
1597
+ # @raise [ArgumentError] if `$value` isn't a number
1187
1598
  def ceil(value)
1188
1599
  numeric_transformation(value) {|n| n.ceil}
1189
1600
  end
1190
1601
  declare :ceil, [:value]
1191
1602
 
1192
- # Rounds down to the nearest whole number.
1603
+ # Rounds a number down to the previous whole number.
1193
1604
  #
1194
1605
  # @example
1195
1606
  # floor(10.4px) => 10px
1196
1607
  # floor(10.6px) => 10px
1197
- # @param value [Number] The number
1198
- # @return [Number] The rounded number
1199
- # @raise [ArgumentError] if `value` isn't a number
1608
+ # @overload floor($value)
1609
+ # @param $value [Sass::Script::Value::Number]
1610
+ # @return [Sass::Script::Value::Number]
1611
+ # @raise [ArgumentError] if `$value` isn't a number
1200
1612
  def floor(value)
1201
1613
  numeric_transformation(value) {|n| n.floor}
1202
1614
  end
1203
1615
  declare :floor, [:value]
1204
1616
 
1205
- # Finds the absolute value of a number.
1617
+ # Returns the absolute value of a number.
1206
1618
  #
1207
1619
  # @example
1208
1620
  # abs(10px) => 10px
1209
1621
  # abs(-10px) => 10px
1210
- # @param value [Number] The number
1211
- # @return [Number] The absolute value
1212
- # @raise [ArgumentError] if `value` isn't a number
1622
+ # @overload abs($value)
1623
+ # @param $value [Sass::Script::Value::Number]
1624
+ # @return [Sass::Script::Value::Number]
1625
+ # @raise [ArgumentError] if `$value` isn't a number
1213
1626
  def abs(value)
1214
1627
  numeric_transformation(value) {|n| n.abs}
1215
1628
  end
1216
1629
  declare :abs, [:value]
1217
1630
 
1218
- # Finds the minimum of several values. This function takes any number of
1631
+ # Finds the minimum of several numbers. This function takes any number of
1219
1632
  # arguments.
1220
1633
  #
1221
1634
  # @example
1222
1635
  # min(1px, 4px) => 1px
1223
1636
  # min(5em, 3em, 4em) => 3em
1224
- # @param values [[Number]] The numbers
1225
- # @return [Number] The minimum value
1637
+ # @overload min($numbers...)
1638
+ # @param $numbers [[Sass::Script::Value::Number]]
1639
+ # @return [Sass::Script::Value::Number]
1226
1640
  # @raise [ArgumentError] if any argument isn't a number, or if not all of
1227
1641
  # the arguments have comparable units
1228
- def min(*values)
1229
- values.each {|v| assert_type v, :Number}
1230
- values.inject {|min, val| min.lt(val).to_bool ? min : val}
1642
+ def min(*numbers)
1643
+ numbers.each {|n| assert_type n, :Number}
1644
+ numbers.inject {|min, num| min.lt(num).to_bool ? min : num}
1231
1645
  end
1232
1646
  declare :min, [], :var_args => :true
1233
1647
 
1234
- # Finds the maximum of several values. This function takes any number of
1648
+ # Finds the maximum of several numbers. This function takes any number of
1235
1649
  # arguments.
1236
1650
  #
1237
1651
  # @example
1238
1652
  # max(1px, 4px) => 4px
1239
1653
  # max(5em, 3em, 4em) => 5em
1240
- # @return [Number] The maximum value
1654
+ # @overload max($numbers...)
1655
+ # @param $numbers [[Sass::Script::Value::Number]]
1656
+ # @return [Sass::Script::Value::Number]
1241
1657
  # @raise [ArgumentError] if any argument isn't a number, or if not all of
1242
1658
  # the arguments have comparable units
1243
1659
  def max(*values)
@@ -1248,50 +1664,86 @@ module Sass::Script
1248
1664
 
1249
1665
  # Return the length of a list.
1250
1666
  #
1667
+ # This can return the number of pairs in a map as well.
1668
+ #
1251
1669
  # @example
1252
1670
  # length(10px) => 1
1253
1671
  # length(10px 20px 30px) => 3
1254
- # @param list [Literal] The list
1255
- # @return [Number] The length
1672
+ # length((width: 10px, height: 20px)) => 2
1673
+ # @overload length($list)
1674
+ # @param $list [Sass::Script::Value::Base]
1675
+ # @return [Sass::Script::Value::Number]
1256
1676
  def length(list)
1257
- Sass::Script::Number.new(list.to_a.size)
1677
+ number(list.to_a.size)
1258
1678
  end
1259
1679
  declare :length, [:list]
1260
1680
 
1681
+ # Return a new list, based on the list provided, but with the nth
1682
+ # element changed to the value given.
1683
+ #
1684
+ # Note that unlike some languages, the first item in a Sass list is number
1685
+ # 1, the second number 2, and so forth.
1686
+ #
1687
+ # Negative index values address elements in reverse order, starting with the last element
1688
+ # in the list.
1689
+ #
1690
+ # @example
1691
+ # set-nth($list: 10px 20px 30px, $n: 2, $value: -20px) => 10px -20px 30px
1692
+ # @overload set-nth($list, $n, $value)
1693
+ # @param $list [Sass::Script::Value::Base] The list that will be copied, having the element
1694
+ # at index `$n` changed.
1695
+ # @param $n [Sass::Script::Value::Number] The index of the item to set.
1696
+ # Negative indices count from the end of the list.
1697
+ # @param $value [Sass::Script::Value::Base] The new value at index `$n`.
1698
+ # @return [Sass::Script::Value::List]
1699
+ # @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
1700
+ # of `$list`
1701
+ def set_nth(list, n, value)
1702
+ assert_type n, :Number, :n
1703
+ Sass::Script::Value::List.assert_valid_index(list, n)
1704
+ index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1705
+ new_list = list.to_a.dup
1706
+ new_list[index] = value
1707
+ Sass::Script::Value::List.new(new_list, list.separator)
1708
+ end
1709
+ declare :set_nth, [:list, :n, :value]
1710
+
1261
1711
  # Gets the nth item in a list.
1262
1712
  #
1263
- # Note that unlike some languages, the first item in a Sass list is number 1,
1264
- # the second number 2, and so forth.
1713
+ # Note that unlike some languages, the first item in a Sass list is number
1714
+ # 1, the second number 2, and so forth.
1715
+ #
1716
+ # This can return the nth pair in a map as well.
1717
+ #
1718
+ # Negative index values address elements in reverse order, starting with the last element in
1719
+ # the list.
1265
1720
  #
1266
1721
  # @example
1267
1722
  # nth(10px 20px 30px, 1) => 10px
1268
1723
  # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
1269
- # @param list [Literal] The list
1270
- # @param n [Number] The index into the list
1271
- # @return [Literal] The nth item in the list
1272
- # @raise [ArgumentError] If `n` isn't an integer between 1 and the list's length.
1724
+ # nth((width: 10px, length: 20px), 2) => length, 20px
1725
+ # @overload nth($list, $n)
1726
+ # @param $list [Sass::Script::Value::Base]
1727
+ # @param $n [Sass::Script::Value::Number] The index of the item to get.
1728
+ # Negative indices count from the end of the list.
1729
+ # @return [Sass::Script::Value::Base]
1730
+ # @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
1731
+ # of `$list`
1273
1732
  def nth(list, n)
1274
- assert_type n, :Number
1275
- if !n.int?
1276
- raise ArgumentError.new("List index #{n} must be an integer")
1277
- elsif n.to_i < 1
1278
- raise ArgumentError.new("List index #{n} must be greater than or equal to 1")
1279
- elsif list.to_a.size == 0
1280
- raise ArgumentError.new("List index is #{n} but list has no items")
1281
- elsif n.to_i > (size = list.to_a.size)
1282
- raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
1283
- end
1733
+ assert_type n, :Number, :n
1734
+ Sass::Script::Value::List.assert_valid_index(list, n)
1284
1735
 
1285
- list.to_a[n.to_i - 1]
1736
+ index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1737
+ list.to_a[index]
1286
1738
  end
1287
1739
  declare :nth, [:list, :n]
1288
1740
 
1289
- # Joins together two lists into a new list.
1741
+ # Joins together two lists into one.
1290
1742
  #
1291
- # Unless the `$separator` argument is passed,
1292
- # if one list is comma-separated and one is space-separated,
1293
- # the first parameter's separator is used for the resulting list.
1294
- # If the lists have only one item each, spaces are used for the resulting list.
1743
+ # Unless `$separator` is passed, if one list is comma-separated and one is
1744
+ # space-separated, the first parameter's separator is used for the resulting
1745
+ # list. If both lists have fewer than two items, spaces are used for the
1746
+ # resulting list.
1295
1747
  #
1296
1748
  # @example
1297
1749
  # join(10px 20px, 30px 40px) => 10px 20px 30px 40px
@@ -1299,34 +1751,31 @@ module Sass::Script
1299
1751
  # join(10px, 20px) => 10px 20px
1300
1752
  # join(10px, 20px, comma) => 10px, 20px
1301
1753
  # join((blue, red), (#abc, #def), space) => blue red #abc #def
1302
- # @overload join(list1, list2, separator: auto)
1303
- # @param list1 [Literal] The first list to join
1304
- # @param list2 [Literal] The second list to join
1305
- # @param separator [String] How the list separator (comma or space) should be determined.
1306
- # If this is `comma` or `space`, that is always the separator;
1307
- # if this is `auto` (the default), the separator is determined as explained above.
1308
- def join(list1, list2, separator = Sass::Script::String.new("auto"))
1309
- assert_type separator, :String
1754
+ # @overload join($list1, $list2, $separator: auto)
1755
+ # @param $list1 [Sass::Script::Value::Base]
1756
+ # @param $list2 [Sass::Script::Value::Base]
1757
+ # @param $separator [Sass::Script::Value::String] The list separator to use.
1758
+ # If this is `comma` or `space`, that separator will be used. If this is
1759
+ # `auto` (the default), the separator is determined as explained above.
1760
+ # @return [Sass::Script::Value::List]
1761
+ def join(list1, list2, separator = identifier("auto"))
1762
+ assert_type separator, :String, :separator
1310
1763
  unless %w[auto space comma].include?(separator.value)
1311
1764
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1312
1765
  end
1313
- sep1 = list1.separator if list1.is_a?(Sass::Script::List) && !list1.value.empty?
1314
- sep2 = list2.separator if list2.is_a?(Sass::Script::List) && !list2.value.empty?
1315
- Sass::Script::List.new(
1316
- list1.to_a + list2.to_a,
1317
- if separator.value == 'auto'
1318
- sep1 || sep2 || :space
1319
- else
1320
- separator.value.to_sym
1321
- end)
1766
+ sep = if separator.value == 'auto'
1767
+ list1.separator || list2.separator || :space
1768
+ else
1769
+ separator.value.to_sym
1770
+ end
1771
+ list(list1.to_a + list2.to_a, sep)
1322
1772
  end
1323
1773
  declare :join, [:list1, :list2]
1324
1774
  declare :join, [:list1, :list2, :separator]
1325
1775
 
1326
1776
  # Appends a single value onto the end of a list.
1327
1777
  #
1328
- # Unless the `$separator` argument is passed,
1329
- # if the list has only one item,
1778
+ # Unless the `$separator` argument is passed, if the list had only one item,
1330
1779
  # the resulting list will be space-separated.
1331
1780
  #
1332
1781
  # @example
@@ -1335,31 +1784,31 @@ module Sass::Script
1335
1784
  # append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
1336
1785
  # append(10px, 20px, comma) => 10px, 20px
1337
1786
  # append((blue, red), green, space) => blue red green
1338
- # @overload append(list, val, separator: auto)
1339
- # @param list [Literal] The list to add the value to
1340
- # @param val [Literal] The value to add to the end of the list
1341
- # @param separator [String] How the list separator (comma or space) should be determined.
1342
- # If this is `comma` or `space`, that is always the separator;
1343
- # if this is `auto` (the default), the separator is the same as that used by the list.
1344
- def append(list, val, separator = Sass::Script::String.new("auto"))
1345
- assert_type separator, :String
1787
+ # @overload append($list, $val, $separator: auto)
1788
+ # @param $list [Sass::Script::Value::Base]
1789
+ # @param $val [Sass::Script::Value::Base]
1790
+ # @param $separator [Sass::Script::Value::String] The list separator to use.
1791
+ # If this is `comma` or `space`, that separator will be used. If this is
1792
+ # `auto` (the default), the separator is determined as explained above.
1793
+ # @return [Sass::Script::Value::List]
1794
+ def append(list, val, separator = identifier("auto"))
1795
+ assert_type separator, :String, :separator
1346
1796
  unless %w[auto space comma].include?(separator.value)
1347
1797
  raise ArgumentError.new("Separator name must be space, comma, or auto")
1348
1798
  end
1349
- sep = list.separator if list.is_a?(Sass::Script::List)
1350
- Sass::Script::List.new(
1351
- list.to_a + [val],
1352
- if separator.value == 'auto'
1353
- sep || :space
1354
- else
1355
- separator.value.to_sym
1356
- end)
1799
+ sep = if separator.value == 'auto'
1800
+ list.separator || :space
1801
+ else
1802
+ separator.value.to_sym
1803
+ end
1804
+ list(list.to_a + [val], sep)
1357
1805
  end
1358
1806
  declare :append, [:list, :val]
1359
1807
  declare :append, [:list, :val, :separator]
1360
1808
 
1361
- # Combines several lists into a single comma separated list
1362
- # space separated lists.
1809
+ # Combines several lists into a single multidimensional list. The nth value
1810
+ # of the resulting list is a space separated list of the source lists' nth
1811
+ # values.
1363
1812
  #
1364
1813
  # The length of the resulting list is the length of the
1365
1814
  # shortest list.
@@ -1367,6 +1816,9 @@ module Sass::Script
1367
1816
  # @example
1368
1817
  # zip(1px 1px 3px, solid dashed solid, red green blue)
1369
1818
  # => 1px solid red, 1px dashed green, 3px solid blue
1819
+ # @overload zip($lists...)
1820
+ # @param $lists [[Sass::Script::Value::Base]]
1821
+ # @return [Sass::Script::Value::List]
1370
1822
  def zip(*lists)
1371
1823
  length = nil
1372
1824
  values = []
@@ -1379,43 +1831,328 @@ module Sass::Script
1379
1831
  value.slice!(length)
1380
1832
  end
1381
1833
  new_list_value = values.first.zip(*values[1..-1])
1382
- List.new(new_list_value.map{|list| List.new(list, :space)}, :comma)
1834
+ list(new_list_value.map {|list| list(list, :space)}, :comma)
1383
1835
  end
1384
1836
  declare :zip, [], :var_args => true
1385
1837
 
1386
-
1387
- # Returns the position of the given value within the given
1388
- # list. If not found, returns false.
1838
+ # Returns the position of a value within a list. If the value isn't found,
1839
+ # returns false instead.
1840
+ #
1841
+ # Note that unlike some languages, the first item in a Sass list is number
1842
+ # 1, the second number 2, and so forth.
1843
+ #
1844
+ # This can return the position of a pair in a map as well.
1389
1845
  #
1390
1846
  # @example
1391
1847
  # index(1px solid red, solid) => 2
1392
1848
  # index(1px solid red, dashed) => false
1849
+ # index((width: 10px, height: 20px), (height, 20px)) => 2
1850
+ # @overload index($list, $value)
1851
+ # @param $list [Sass::Script::Value::Base]
1852
+ # @param $value [Sass::Script::Value::Base]
1853
+ # @return [Sass::Script::Value::Number, Sass::Script::Value::Bool] The
1854
+ # 1-based index of `$value` in `$list`, or `false`
1393
1855
  def index(list, value)
1394
- index = list.to_a.index {|e| e.eq(value).to_bool }
1856
+ index = list.to_a.index {|e| e.eq(value).to_bool}
1395
1857
  if index
1396
- Number.new(index + 1)
1858
+ number(index + 1)
1397
1859
  else
1398
- Bool.new(false)
1860
+ bool(false)
1399
1861
  end
1400
1862
  end
1401
1863
  declare :index, [:list, :value]
1402
1864
 
1403
- # Returns one of two values based on the truth value of the first argument.
1865
+ # Returns the separator of a list. If the list doesn't have a separator due
1866
+ # to having fewer than two elements, returns `space`.
1867
+ #
1868
+ # @example
1869
+ # list-separator(1px 2px 3px) => space
1870
+ # list-separator(1px, 2px, 3px) => comma
1871
+ # list-separator('foo') => space
1872
+ # @overload list_separator($list)
1873
+ # @param $list [Sass::Script::Value::Base]
1874
+ # @return [Sass::Script::Value::String] `comma` or `space`
1875
+ def list_separator(list)
1876
+ identifier((list.separator || :space).to_s)
1877
+ end
1878
+ declare :separator, [:list]
1879
+
1880
+ # Returns the value in a map associated with the given key. If the map
1881
+ # doesn't have such a key, returns `null`.
1882
+ #
1883
+ # @example
1884
+ # map-get(("foo": 1, "bar": 2), "foo") => 1
1885
+ # map-get(("foo": 1, "bar": 2), "bar") => 2
1886
+ # map-get(("foo": 1, "bar": 2), "baz") => null
1887
+ # @overload map_get($map, $key)
1888
+ # @param $map [Sass::Script::Value::Map]
1889
+ # @param $key [Sass::Script::Value::Base]
1890
+ # @return [Sass::Script::Value::Base] The value indexed by `$key`, or `null`
1891
+ # if the map doesn't contain the given key
1892
+ # @raise [ArgumentError] if `$map` is not a map
1893
+ def map_get(map, key)
1894
+ assert_type map, :Map
1895
+ to_h(map)[key] || null
1896
+ end
1897
+ declare :map_get, [:map, :key]
1898
+
1899
+ # Merges two maps together into a new map. Keys in `$map2` will take
1900
+ # precedence over keys in `$map1`.
1901
+ #
1902
+ # This is the best way to add new values to a map.
1903
+ #
1904
+ # All keys in the returned map that also appear in `$map1` will have the
1905
+ # same order as in `$map1`. New keys from `$map2` will be placed at the end
1906
+ # of the map.
1907
+ #
1908
+ # @example
1909
+ # map-merge(("foo": 1), ("bar": 2)) => ("foo": 1, "bar": 2)
1910
+ # map-merge(("foo": 1, "bar": 2), ("bar": 3)) => ("foo": 1, "bar": 3)
1911
+ # @overload map_merge($map1, $map2)
1912
+ # @param $map1 [Sass::Script::Value::Map]
1913
+ # @param $map2 [Sass::Script::Value::Map]
1914
+ # @return [Sass::Script::Value::Map]
1915
+ # @raise [ArgumentError] if either parameter is not a map
1916
+ def map_merge(map1, map2)
1917
+ assert_type map1, :Map
1918
+ assert_type map2, :Map
1919
+ map(to_h(map1).merge(to_h(map2)))
1920
+ end
1921
+ declare :map_get, [:map1, :map2]
1922
+
1923
+ # Returns a list of all keys in a map.
1924
+ #
1925
+ # @example
1926
+ # map-keys(("foo": 1, "bar": 2)) => "foo", "bar"
1927
+ # @overload map_keys($map)
1928
+ # @param $map [Map]
1929
+ # @return [List] the list of keys, comma-separated
1930
+ # @raise [ArgumentError] if `$map` is not a map
1931
+ def map_keys(map)
1932
+ assert_type map, :Map
1933
+ list(to_h(map).keys, :comma)
1934
+ end
1935
+ declare :map_keys, [:map]
1936
+
1937
+ # Returns a list of all values in a map. This list may include duplicate
1938
+ # values, if multiple keys have the same value.
1939
+ #
1940
+ # @example
1941
+ # map-values(("foo": 1, "bar": 2)) => 1, 2
1942
+ # map-values(("foo": 1, "bar": 2, "baz": 1)) => 1, 2, 1
1943
+ # @overload map_values($map)
1944
+ # @param $map [Map]
1945
+ # @return [List] the list of values, comma-separated
1946
+ # @raise [ArgumentError] if `$map` is not a map
1947
+ def map_values(map)
1948
+ assert_type map, :Map
1949
+ list(to_h(map).values, :comma)
1950
+ end
1951
+ declare :map_values, [:map]
1952
+
1953
+ # Returns whether a map has a value associated with a given key.
1954
+ #
1955
+ # @example
1956
+ # map-has-key(("foo": 1, "bar": 2), "foo") => true
1957
+ # map-has-key(("foo": 1, "bar": 2), "baz") => false
1958
+ # @overload map_has_key($map, $key)
1959
+ # @param $map [Sass::Script::Value::Map]
1960
+ # @param $key [Sass::Script::Value::Base]
1961
+ # @return [Sass::Script::Value::Bool]
1962
+ # @raise [ArgumentError] if `$map` is not a map
1963
+ def map_has_key(map, key)
1964
+ assert_type map, :Map
1965
+ bool(to_h(map).has_key?(key))
1966
+ end
1967
+ declare :map_has_key, [:map, :key]
1968
+
1969
+ # Returns the map of named arguments passed to a function or mixin that
1970
+ # takes a variable argument list. The argument names are strings, and they
1971
+ # do not contain the leading `$`.
1972
+ #
1973
+ # @example
1974
+ # @mixin foo($args...) {
1975
+ # @debug keywords($args); //=> (arg1: val, arg2: val)
1976
+ # }
1977
+ #
1978
+ # @include foo($arg1: val, $arg2: val);
1979
+ # @overload keywords($args)
1980
+ # @param $args [Sass::Script::Value::ArgList]
1981
+ # @return [Sass::Script::Value::Map]
1982
+ # @raise [ArgumentError] if `$args` isn't a variable argument list
1983
+ def keywords(args)
1984
+ assert_type args, :ArgList
1985
+ map(Sass::Util.map_keys(args.keywords) {|k| Sass::Script::String.new(k)})
1986
+ end
1987
+ declare :keywords, [:args]
1988
+
1989
+ # Returns one of two values, depending on whether or not `$condition` is
1990
+ # true. Just like in `@if`, all values other than `false` and `null` are
1991
+ # considered to be true.
1404
1992
  #
1405
1993
  # @example
1406
1994
  # if(true, 1px, 2px) => 1px
1407
1995
  # if(false, 1px, 2px) => 2px
1408
- # @param condition [Bool] Whether the first or second value will be returned.
1409
- # @param if_true [Literal] The value that will be returned if `$condition` is true.
1410
- # @param if_false [Literal] The value that will be returned if `$condition` is false.
1996
+ # @overload if($condition, $if-true, $if-false)
1997
+ # @param $condition [Sass::Script::Value::Base] Whether the `$if-true` or
1998
+ # `$if-false` will be returned
1999
+ # @param $if-true [Sass::Script::Tree::Node]
2000
+ # @param $if-false [Sass::Script::Tree::Node]
2001
+ # @return [Sass::Script::Value::Base] `$if-true` or `$if-false`
1411
2002
  def if(condition, if_true, if_false)
1412
2003
  if condition.to_bool
1413
- if_true
2004
+ perform(if_true)
1414
2005
  else
1415
- if_false
2006
+ perform(if_false)
1416
2007
  end
1417
2008
  end
1418
- declare :if, [:condition, :if_true, :if_false]
2009
+ declare :if, [:condition, :"&if_true", :"&if_false"]
2010
+
2011
+ # Returns a unique CSS identifier. The identifier is returned as an unquoted
2012
+ # string. The identifier returned is only guaranteed to be unique within the
2013
+ # scope of a single Sass run.
2014
+ #
2015
+ # @overload unique_id()
2016
+ # @return [Sass::Script::Value::String]
2017
+ def unique_id
2018
+ Thread.current[:sass_last_unique_id] ||= rand(36**8)
2019
+ # avoid the temptation of trying to guess the next unique value.
2020
+ value = (Thread.current[:sass_last_unique_id] += (rand(10) + 1))
2021
+ # the u makes this a legal identifier if it would otherwise start with a number.
2022
+ identifier("u" + value.to_s(36).rjust(8, '0'))
2023
+ end
2024
+ declare :unique_id, []
2025
+
2026
+ # Dynamically calls a function. This can call user-defined
2027
+ # functions, built-in functions, or plain CSS functions. It will
2028
+ # pass along all arguments, including keyword arguments, to the
2029
+ # called function.
2030
+ #
2031
+ # @example
2032
+ # call(rgb, 10, 100, 255) => #0a64ff
2033
+ # call(scale-color, #0a64ff, $lightness: -10%) => #0058ef
2034
+ #
2035
+ # $fn: nth;
2036
+ # call($fn, 2, (a b c)) => b
2037
+ #
2038
+ # @overload call($name, $args...)
2039
+ # @param $name [String] The name of the function to call.
2040
+ def call(name, *args)
2041
+ assert_type name, :String, :name
2042
+ kwargs = args.last.is_a?(Hash) ? args.pop : {}
2043
+ funcall = Sass::Script::Tree::Funcall.new(
2044
+ name.value,
2045
+ args.map {|a| Sass::Script::Tree::Literal.new(a)},
2046
+ Sass::Util.map_vals(kwargs) {|v| Sass::Script::Tree::Literal.new(v)},
2047
+ nil,
2048
+ nil)
2049
+ funcall.options = options
2050
+ funcall.perform(environment)
2051
+ end
2052
+ declare :call, [:name], :var_args => true, :var_kwargs => true
2053
+
2054
+ # This function only exists as a workaround for IE7's [`content: counter`
2055
+ # bug][bug]. It works identically to any other plain-CSS function, except it
2056
+ # avoids adding spaces between the argument commas.
2057
+ #
2058
+ # [bug]: http://jes.st/2013/ie7s-css-breaking-content-counter-bug/
2059
+ #
2060
+ # @example
2061
+ # counter(item, ".") => counter(item,".")
2062
+ # @overload counter($args...)
2063
+ # @return [String]
2064
+ def counter(*args)
2065
+ identifier("counter(#{args.map {|a| a.to_s(options)}.join(',')})")
2066
+ end
2067
+ declare :counter, [], :var_args => true
2068
+
2069
+ # This function only exists as a workaround for IE7's [`content: counters`
2070
+ # bug][bug]. It works identically to any other plain-CSS function, except it
2071
+ # avoids adding spaces between the argument commas.
2072
+ #
2073
+ # [bug]: http://jes.st/2013/ie7s-css-breaking-content-counter-bug/
2074
+ #
2075
+ # @example
2076
+ # counters(item, ".") => counters(item,".")
2077
+ # @overload counters($args...)
2078
+ # @return [String]
2079
+ def counters(*args)
2080
+ identifier("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
2081
+ end
2082
+ declare :counters, [], :var_args => true
2083
+
2084
+ # Check whether a variable with the given name exists in the current
2085
+ # scope or in the global scope.
2086
+ #
2087
+ # @example
2088
+ # $a-false-value: false;
2089
+ # variable-exists(a-false-value) => true
2090
+ #
2091
+ # variable-exists(nonexistent) => false
2092
+ # @param name [Sass::Script::String] The name of the variable to
2093
+ # check. The name should not include the `$`.
2094
+ # @return [Sass::Script::Bool] Whether the variable is defined in
2095
+ # the current scope.
2096
+ def variable_exists(name)
2097
+ assert_type name, :String
2098
+ bool(environment.caller.var(name.value))
2099
+ end
2100
+ declare :variable_exists, [:name]
2101
+
2102
+ # Check whether a variable with the given name exists in the global
2103
+ # scope (at the top level of the file).
2104
+ #
2105
+ # @example
2106
+ # $a-false-value: false;
2107
+ # global-variable-exists(a-false-value) => true
2108
+ #
2109
+ # .foo {
2110
+ # $some-var: false;
2111
+ # @if global-variable-exists(some-var) { /* false, doesn't run */ }
2112
+ # }
2113
+ # @param name [Sass::Script::String] The name of the variable to
2114
+ # check. The name should not include the `$`.
2115
+ # @return [Sass::Script::Bool] Whether the variable is defined in
2116
+ # the global scope.
2117
+ def global_variable_exists(name)
2118
+ assert_type name, :String
2119
+ bool(environment.global_env.var(name.value))
2120
+ end
2121
+ declare :global_variable_exists, [:name]
2122
+
2123
+ # Check whether a function with the given name exists.
2124
+ #
2125
+ # @example
2126
+ # function-exists(lighten) => true
2127
+ #
2128
+ # @function myfunc { @return "something"; }
2129
+ # function-exists(myfunc) => true
2130
+ # @param name [Sass::Script::String] The name of the function to
2131
+ # check.
2132
+ # @return [Sass::Script::Bool] Whether the function is defined.
2133
+ def function_exists(name)
2134
+ assert_type name, :String
2135
+ exists = Sass::Script::Functions.callable?(name.value.tr("-", "_"))
2136
+ exists ||= environment.function(name.value)
2137
+ bool(exists)
2138
+ end
2139
+ declare :function_exists, [:name]
2140
+
2141
+ # Check whether a mixin with the given name exists.
2142
+ #
2143
+ # @example
2144
+ # mixin-exists(nonexistent) => false
2145
+ #
2146
+ # @mixin red-text { color: red; }
2147
+ # mixin-exists(red-text) => true
2148
+ # @param name [Sass::Script::String] The name of the mixin to
2149
+ # check.
2150
+ # @return [Sass::Script::Bool] Whether the mixin is defined.
2151
+ def mixin_exists(name)
2152
+ assert_type name, :String
2153
+ bool(environment.mixin(name.value))
2154
+ end
2155
+ declare :mixin_exists, [:name]
1419
2156
 
1420
2157
  private
1421
2158
 
@@ -1423,13 +2160,17 @@ module Sass::Script
1423
2160
  # another numeric value with the same units.
1424
2161
  # It yields a number to a block to perform the operation and return a number
1425
2162
  def numeric_transformation(value)
1426
- assert_type value, :Number
1427
- Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
2163
+ assert_type value, :Number, :value
2164
+ Sass::Script::Value::Number.new(
2165
+ yield(value.value), value.numerator_units, value.denominator_units)
1428
2166
  end
1429
2167
 
2168
+ # @comment
2169
+ # rubocop:disable ParameterLists
1430
2170
  def _adjust(color, amount, attr, range, op, units = "")
1431
- assert_type color, :Color
1432
- assert_type amount, :Number
2171
+ # rubocop:enable ParameterLists
2172
+ assert_type color, :Color, :color
2173
+ assert_type amount, :Number, :amount
1433
2174
  Sass::Util.check_range('Amount', range, amount, units)
1434
2175
 
1435
2176
  # TODO: is it worth restricting here,
@@ -1438,5 +2179,17 @@ module Sass::Script
1438
2179
  color.with(attr => Sass::Util.restrict(
1439
2180
  color.send(attr).send(op, amount.value), range))
1440
2181
  end
2182
+
2183
+ def to_h(obj)
2184
+ return obj.to_h unless obj.is_a?(Sass::Script::Value::List) && obj.needs_map_warning?
2185
+
2186
+ fn_name = Sass::Util.caller_info.last.gsub('_', '-')
2187
+ Sass::Util.sass_warn <<WARNING + environment.stack.to_s.gsub(/^/, ' ')
2188
+ DEPRECATION WARNING: Passing lists of pairs to #{fn_name} is deprecated and will
2189
+ be removed in future versions of Sass. Use Sass maps instead. For details, see
2190
+ http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#maps.
2191
+ WARNING
2192
+ obj.to_h
2193
+ end
1441
2194
  end
1442
2195
  end