oreorenasass 3.4.0

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 (268) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +11 -0
  3. data/CONTRIBUTING +3 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +221 -0
  6. data/Rakefile +370 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/sass +13 -0
  10. data/bin/sass-convert +12 -0
  11. data/bin/scss +13 -0
  12. data/extra/update_watch.rb +13 -0
  13. data/init.rb +18 -0
  14. data/lib/sass/cache_stores/base.rb +88 -0
  15. data/lib/sass/cache_stores/chain.rb +34 -0
  16. data/lib/sass/cache_stores/filesystem.rb +60 -0
  17. data/lib/sass/cache_stores/memory.rb +47 -0
  18. data/lib/sass/cache_stores/null.rb +25 -0
  19. data/lib/sass/cache_stores.rb +15 -0
  20. data/lib/sass/callbacks.rb +67 -0
  21. data/lib/sass/css.rb +407 -0
  22. data/lib/sass/engine.rb +1181 -0
  23. data/lib/sass/environment.rb +191 -0
  24. data/lib/sass/error.rb +198 -0
  25. data/lib/sass/exec/base.rb +187 -0
  26. data/lib/sass/exec/sass_convert.rb +264 -0
  27. data/lib/sass/exec/sass_scss.rb +424 -0
  28. data/lib/sass/exec.rb +9 -0
  29. data/lib/sass/features.rb +47 -0
  30. data/lib/sass/importers/base.rb +182 -0
  31. data/lib/sass/importers/filesystem.rb +211 -0
  32. data/lib/sass/importers.rb +22 -0
  33. data/lib/sass/logger/base.rb +30 -0
  34. data/lib/sass/logger/log_level.rb +45 -0
  35. data/lib/sass/logger.rb +12 -0
  36. data/lib/sass/media.rb +210 -0
  37. data/lib/sass/plugin/compiler.rb +565 -0
  38. data/lib/sass/plugin/configuration.rb +118 -0
  39. data/lib/sass/plugin/generic.rb +15 -0
  40. data/lib/sass/plugin/merb.rb +48 -0
  41. data/lib/sass/plugin/rack.rb +60 -0
  42. data/lib/sass/plugin/rails.rb +47 -0
  43. data/lib/sass/plugin/staleness_checker.rb +199 -0
  44. data/lib/sass/plugin.rb +133 -0
  45. data/lib/sass/railtie.rb +10 -0
  46. data/lib/sass/repl.rb +57 -0
  47. data/lib/sass/root.rb +7 -0
  48. data/lib/sass/script/css_lexer.rb +33 -0
  49. data/lib/sass/script/css_parser.rb +34 -0
  50. data/lib/sass/script/functions.rb +2626 -0
  51. data/lib/sass/script/lexer.rb +449 -0
  52. data/lib/sass/script/parser.rb +637 -0
  53. data/lib/sass/script/tree/funcall.rb +306 -0
  54. data/lib/sass/script/tree/interpolation.rb +118 -0
  55. data/lib/sass/script/tree/list_literal.rb +77 -0
  56. data/lib/sass/script/tree/literal.rb +45 -0
  57. data/lib/sass/script/tree/map_literal.rb +64 -0
  58. data/lib/sass/script/tree/node.rb +109 -0
  59. data/lib/sass/script/tree/operation.rb +103 -0
  60. data/lib/sass/script/tree/selector.rb +26 -0
  61. data/lib/sass/script/tree/string_interpolation.rb +104 -0
  62. data/lib/sass/script/tree/unary_operation.rb +69 -0
  63. data/lib/sass/script/tree/variable.rb +57 -0
  64. data/lib/sass/script/tree.rb +16 -0
  65. data/lib/sass/script/value/arg_list.rb +36 -0
  66. data/lib/sass/script/value/base.rb +240 -0
  67. data/lib/sass/script/value/bool.rb +35 -0
  68. data/lib/sass/script/value/color.rb +680 -0
  69. data/lib/sass/script/value/helpers.rb +262 -0
  70. data/lib/sass/script/value/list.rb +113 -0
  71. data/lib/sass/script/value/map.rb +70 -0
  72. data/lib/sass/script/value/null.rb +44 -0
  73. data/lib/sass/script/value/number.rb +530 -0
  74. data/lib/sass/script/value/string.rb +97 -0
  75. data/lib/sass/script/value.rb +11 -0
  76. data/lib/sass/script.rb +66 -0
  77. data/lib/sass/scss/css_parser.rb +42 -0
  78. data/lib/sass/scss/parser.rb +1209 -0
  79. data/lib/sass/scss/rx.rb +141 -0
  80. data/lib/sass/scss/script_lexer.rb +15 -0
  81. data/lib/sass/scss/script_parser.rb +25 -0
  82. data/lib/sass/scss/static_parser.rb +368 -0
  83. data/lib/sass/scss.rb +16 -0
  84. data/lib/sass/selector/abstract_sequence.rb +109 -0
  85. data/lib/sass/selector/comma_sequence.rb +175 -0
  86. data/lib/sass/selector/pseudo.rb +256 -0
  87. data/lib/sass/selector/sequence.rb +600 -0
  88. data/lib/sass/selector/simple.rb +117 -0
  89. data/lib/sass/selector/simple_sequence.rb +325 -0
  90. data/lib/sass/selector.rb +326 -0
  91. data/lib/sass/shared.rb +76 -0
  92. data/lib/sass/source/map.rb +210 -0
  93. data/lib/sass/source/position.rb +39 -0
  94. data/lib/sass/source/range.rb +41 -0
  95. data/lib/sass/stack.rb +120 -0
  96. data/lib/sass/supports.rb +227 -0
  97. data/lib/sass/tree/at_root_node.rb +83 -0
  98. data/lib/sass/tree/charset_node.rb +22 -0
  99. data/lib/sass/tree/comment_node.rb +82 -0
  100. data/lib/sass/tree/content_node.rb +9 -0
  101. data/lib/sass/tree/css_import_node.rb +60 -0
  102. data/lib/sass/tree/debug_node.rb +18 -0
  103. data/lib/sass/tree/directive_node.rb +59 -0
  104. data/lib/sass/tree/each_node.rb +24 -0
  105. data/lib/sass/tree/error_node.rb +18 -0
  106. data/lib/sass/tree/extend_node.rb +43 -0
  107. data/lib/sass/tree/for_node.rb +36 -0
  108. data/lib/sass/tree/function_node.rb +39 -0
  109. data/lib/sass/tree/if_node.rb +52 -0
  110. data/lib/sass/tree/import_node.rb +74 -0
  111. data/lib/sass/tree/keyframe_rule_node.rb +15 -0
  112. data/lib/sass/tree/media_node.rb +48 -0
  113. data/lib/sass/tree/mixin_def_node.rb +38 -0
  114. data/lib/sass/tree/mixin_node.rb +52 -0
  115. data/lib/sass/tree/node.rb +238 -0
  116. data/lib/sass/tree/prop_node.rb +171 -0
  117. data/lib/sass/tree/return_node.rb +19 -0
  118. data/lib/sass/tree/root_node.rb +44 -0
  119. data/lib/sass/tree/rule_node.rb +145 -0
  120. data/lib/sass/tree/supports_node.rb +38 -0
  121. data/lib/sass/tree/trace_node.rb +33 -0
  122. data/lib/sass/tree/variable_node.rb +36 -0
  123. data/lib/sass/tree/visitors/base.rb +72 -0
  124. data/lib/sass/tree/visitors/check_nesting.rb +177 -0
  125. data/lib/sass/tree/visitors/convert.rb +334 -0
  126. data/lib/sass/tree/visitors/cssize.rb +369 -0
  127. data/lib/sass/tree/visitors/deep_copy.rb +107 -0
  128. data/lib/sass/tree/visitors/extend.rb +68 -0
  129. data/lib/sass/tree/visitors/perform.rb +539 -0
  130. data/lib/sass/tree/visitors/set_options.rb +139 -0
  131. data/lib/sass/tree/visitors/to_css.rb +381 -0
  132. data/lib/sass/tree/warn_node.rb +18 -0
  133. data/lib/sass/tree/while_node.rb +18 -0
  134. data/lib/sass/util/cross_platform_random.rb +19 -0
  135. data/lib/sass/util/multibyte_string_scanner.rb +157 -0
  136. data/lib/sass/util/normalized_map.rb +130 -0
  137. data/lib/sass/util/ordered_hash.rb +192 -0
  138. data/lib/sass/util/subset_map.rb +110 -0
  139. data/lib/sass/util/test.rb +9 -0
  140. data/lib/sass/util.rb +1318 -0
  141. data/lib/sass/version.rb +124 -0
  142. data/lib/sass.rb +102 -0
  143. data/rails/init.rb +1 -0
  144. data/test/sass/cache_test.rb +131 -0
  145. data/test/sass/callbacks_test.rb +61 -0
  146. data/test/sass/compiler_test.rb +232 -0
  147. data/test/sass/conversion_test.rb +2054 -0
  148. data/test/sass/css2sass_test.rb +477 -0
  149. data/test/sass/data/hsl-rgb.txt +319 -0
  150. data/test/sass/encoding_test.rb +219 -0
  151. data/test/sass/engine_test.rb +3301 -0
  152. data/test/sass/exec_test.rb +86 -0
  153. data/test/sass/extend_test.rb +1661 -0
  154. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  155. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  156. data/test/sass/functions_test.rb +1926 -0
  157. data/test/sass/importer_test.rb +412 -0
  158. data/test/sass/logger_test.rb +58 -0
  159. data/test/sass/mock_importer.rb +49 -0
  160. data/test/sass/more_results/more1.css +9 -0
  161. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  162. data/test/sass/more_results/more_import.css +29 -0
  163. data/test/sass/more_templates/_more_partial.sass +2 -0
  164. data/test/sass/more_templates/more1.sass +23 -0
  165. data/test/sass/more_templates/more_import.sass +11 -0
  166. data/test/sass/plugin_test.rb +554 -0
  167. data/test/sass/results/alt.css +4 -0
  168. data/test/sass/results/basic.css +9 -0
  169. data/test/sass/results/cached_import_option.css +3 -0
  170. data/test/sass/results/compact.css +5 -0
  171. data/test/sass/results/complex.css +86 -0
  172. data/test/sass/results/compressed.css +1 -0
  173. data/test/sass/results/expanded.css +19 -0
  174. data/test/sass/results/filename_fn.css +3 -0
  175. data/test/sass/results/if.css +3 -0
  176. data/test/sass/results/import.css +31 -0
  177. data/test/sass/results/import_charset.css +5 -0
  178. data/test/sass/results/import_charset_1_8.css +5 -0
  179. data/test/sass/results/import_charset_ibm866.css +5 -0
  180. data/test/sass/results/import_content.css +1 -0
  181. data/test/sass/results/line_numbers.css +49 -0
  182. data/test/sass/results/mixins.css +95 -0
  183. data/test/sass/results/multiline.css +24 -0
  184. data/test/sass/results/nested.css +22 -0
  185. data/test/sass/results/options.css +1 -0
  186. data/test/sass/results/parent_ref.css +13 -0
  187. data/test/sass/results/script.css +16 -0
  188. data/test/sass/results/scss_import.css +31 -0
  189. data/test/sass/results/scss_importee.css +2 -0
  190. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  191. data/test/sass/results/subdir/subdir.css +3 -0
  192. data/test/sass/results/units.css +11 -0
  193. data/test/sass/results/warn.css +0 -0
  194. data/test/sass/results/warn_imported.css +0 -0
  195. data/test/sass/script_conversion_test.rb +328 -0
  196. data/test/sass/script_test.rb +1054 -0
  197. data/test/sass/scss/css_test.rb +1215 -0
  198. data/test/sass/scss/rx_test.rb +156 -0
  199. data/test/sass/scss/scss_test.rb +3900 -0
  200. data/test/sass/scss/test_helper.rb +37 -0
  201. data/test/sass/source_map_test.rb +977 -0
  202. data/test/sass/superselector_test.rb +191 -0
  203. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  204. data/test/sass/templates/_double_import_loop2.sass +1 -0
  205. data/test/sass/templates/_filename_fn_import.scss +11 -0
  206. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  207. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  208. data/test/sass/templates/_imported_content.sass +3 -0
  209. data/test/sass/templates/_partial.sass +2 -0
  210. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  211. data/test/sass/templates/alt.sass +16 -0
  212. data/test/sass/templates/basic.sass +23 -0
  213. data/test/sass/templates/bork1.sass +2 -0
  214. data/test/sass/templates/bork2.sass +2 -0
  215. data/test/sass/templates/bork3.sass +2 -0
  216. data/test/sass/templates/bork4.sass +2 -0
  217. data/test/sass/templates/bork5.sass +3 -0
  218. data/test/sass/templates/cached_import_option.scss +3 -0
  219. data/test/sass/templates/compact.sass +17 -0
  220. data/test/sass/templates/complex.sass +305 -0
  221. data/test/sass/templates/compressed.sass +15 -0
  222. data/test/sass/templates/double_import_loop1.sass +1 -0
  223. data/test/sass/templates/expanded.sass +17 -0
  224. data/test/sass/templates/filename_fn.scss +18 -0
  225. data/test/sass/templates/if.sass +11 -0
  226. data/test/sass/templates/import.sass +12 -0
  227. data/test/sass/templates/import_charset.sass +9 -0
  228. data/test/sass/templates/import_charset_1_8.sass +6 -0
  229. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  230. data/test/sass/templates/import_content.sass +4 -0
  231. data/test/sass/templates/importee.less +2 -0
  232. data/test/sass/templates/importee.sass +19 -0
  233. data/test/sass/templates/line_numbers.sass +13 -0
  234. data/test/sass/templates/mixin_bork.sass +5 -0
  235. data/test/sass/templates/mixins.sass +76 -0
  236. data/test/sass/templates/multiline.sass +20 -0
  237. data/test/sass/templates/nested.sass +25 -0
  238. data/test/sass/templates/nested_bork1.sass +2 -0
  239. data/test/sass/templates/nested_bork2.sass +2 -0
  240. data/test/sass/templates/nested_bork3.sass +2 -0
  241. data/test/sass/templates/nested_bork4.sass +2 -0
  242. data/test/sass/templates/nested_import.sass +2 -0
  243. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  244. data/test/sass/templates/options.sass +2 -0
  245. data/test/sass/templates/parent_ref.sass +25 -0
  246. data/test/sass/templates/same_name_different_ext.sass +2 -0
  247. data/test/sass/templates/same_name_different_ext.scss +1 -0
  248. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  249. data/test/sass/templates/script.sass +101 -0
  250. data/test/sass/templates/scss_import.scss +12 -0
  251. data/test/sass/templates/scss_importee.scss +1 -0
  252. data/test/sass/templates/single_import_loop.sass +1 -0
  253. data/test/sass/templates/subdir/import_up1.scss +1 -0
  254. data/test/sass/templates/subdir/import_up2.scss +1 -0
  255. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  256. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  257. data/test/sass/templates/subdir/subdir.sass +6 -0
  258. data/test/sass/templates/units.sass +11 -0
  259. data/test/sass/templates/warn.sass +3 -0
  260. data/test/sass/templates/warn_imported.sass +4 -0
  261. data/test/sass/test_helper.rb +8 -0
  262. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  263. data/test/sass/util/normalized_map_test.rb +51 -0
  264. data/test/sass/util/subset_map_test.rb +91 -0
  265. data/test/sass/util_test.rb +467 -0
  266. data/test/sass/value_helpers_test.rb +179 -0
  267. data/test/test_helper.rb +109 -0
  268. metadata +386 -0
@@ -0,0 +1,133 @@
1
+ require 'fileutils'
2
+
3
+ require 'sass'
4
+ require 'sass/plugin/compiler'
5
+
6
+ module Sass
7
+ # This module provides a single interface to the compilation of Sass/SCSS files
8
+ # for an application. It provides global options and checks whether CSS files
9
+ # need to be updated.
10
+ #
11
+ # This module is used as the primary interface with Sass
12
+ # when it's used as a plugin for various frameworks.
13
+ # All Rack-enabled frameworks are supported out of the box.
14
+ # The plugin is
15
+ # {file:SASS_REFERENCE.md#rails_merb_plugin automatically activated for Rails and Merb}.
16
+ # Other frameworks must enable it explicitly; see {Sass::Plugin::Rack}.
17
+ #
18
+ # This module has a large set of callbacks available
19
+ # to allow users to run code (such as logging) when certain things happen.
20
+ # All callback methods are of the form `on_#{name}`,
21
+ # and they all take a block that's called when the given action occurs.
22
+ #
23
+ # Note that this class proxies almost all methods to its {Sass::Plugin::Compiler} instance.
24
+ # See \{#compiler}.
25
+ #
26
+ # @example Using a callback
27
+ # Sass::Plugin.on_updating_stylesheet do |template, css|
28
+ # puts "Compiling #{template} to #{css}"
29
+ # end
30
+ # Sass::Plugin.update_stylesheets
31
+ # #=> Compiling app/sass/screen.scss to public/stylesheets/screen.css
32
+ # #=> Compiling app/sass/print.scss to public/stylesheets/print.css
33
+ # #=> Compiling app/sass/ie.scss to public/stylesheets/ie.css
34
+ # @see Sass::Plugin::Compiler
35
+ module Plugin
36
+ extend self
37
+
38
+ @checked_for_updates = false
39
+
40
+ # Whether or not Sass has **ever** checked if the stylesheets need to be updated
41
+ # (in this Ruby instance).
42
+ #
43
+ # @return [Boolean]
44
+ attr_accessor :checked_for_updates
45
+
46
+ # Same as \{#update\_stylesheets}, but respects \{#checked\_for\_updates}
47
+ # and the {file:SASS_REFERENCE.md#always_update-option `:always_update`}
48
+ # and {file:SASS_REFERENCE.md#always_check-option `:always_check`} options.
49
+ #
50
+ # @see #update_stylesheets
51
+ def check_for_updates
52
+ return unless !Sass::Plugin.checked_for_updates ||
53
+ Sass::Plugin.options[:always_update] || Sass::Plugin.options[:always_check]
54
+ update_stylesheets
55
+ end
56
+
57
+ # Returns the singleton compiler instance.
58
+ # This compiler has been pre-configured according
59
+ # to the plugin configuration.
60
+ #
61
+ # @return [Sass::Plugin::Compiler]
62
+ def compiler
63
+ @compiler ||= Compiler.new
64
+ end
65
+
66
+ # Updates out-of-date stylesheets.
67
+ #
68
+ # Checks each Sass/SCSS file in
69
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location`}
70
+ # to see if it's been modified more recently than the corresponding CSS file
71
+ # in {file:SASS_REFERENCE.md#css_location-option `:css_location`}.
72
+ # If it has, it updates the CSS file.
73
+ #
74
+ # @param individual_files [Array<(String, String)>]
75
+ # A list of files to check for updates
76
+ # **in addition to those specified by the
77
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
78
+ # The first string in each pair is the location of the Sass/SCSS file,
79
+ # the second is the location of the CSS file that it should be compiled to.
80
+ def update_stylesheets(individual_files = [])
81
+ return if options[:never_update]
82
+ compiler.update_stylesheets(individual_files)
83
+ end
84
+
85
+ # Updates all stylesheets, even those that aren't out-of-date.
86
+ # Ignores the cache.
87
+ #
88
+ # @param individual_files [Array<(String, String)>]
89
+ # A list of files to check for updates
90
+ # **in addition to those specified by the
91
+ # {file:SASS_REFERENCE.md#template_location-option `:template_location` option}.**
92
+ # The first string in each pair is the location of the Sass/SCSS file,
93
+ # the second is the location of the CSS file that it should be compiled to.
94
+ # @see #update_stylesheets
95
+ def force_update_stylesheets(individual_files = [])
96
+ Compiler.new(options.dup.merge(
97
+ :never_update => false,
98
+ :always_update => true,
99
+ :cache => false)).update_stylesheets(individual_files)
100
+ end
101
+
102
+ # All other method invocations are proxied to the \{#compiler}.
103
+ #
104
+ # @see #compiler
105
+ # @see Sass::Plugin::Compiler
106
+ def method_missing(method, *args, &block)
107
+ if compiler.respond_to?(method)
108
+ compiler.send(method, *args, &block)
109
+ else
110
+ super
111
+ end
112
+ end
113
+
114
+ # For parity with method_missing
115
+ def respond_to?(method)
116
+ super || compiler.respond_to?(method)
117
+ end
118
+
119
+ # There's a small speedup by not using method missing for frequently delegated methods.
120
+ def options
121
+ compiler.options
122
+ end
123
+ end
124
+ end
125
+
126
+ if defined?(ActionController)
127
+ # On Rails 3+ the rails plugin is loaded at the right time in railtie.rb
128
+ require 'sass/plugin/rails' unless Sass::Util.ap_geq_3?
129
+ elsif defined?(Merb::Plugins)
130
+ require 'sass/plugin/merb'
131
+ else
132
+ require 'sass/plugin/generic'
133
+ end
@@ -0,0 +1,10 @@
1
+ # Rails 3.0.0.beta.2+, < 3.1
2
+ if defined?(ActiveSupport) && Sass::Util.has?(:public_method, ActiveSupport, :on_load) &&
3
+ !Sass::Util.ap_geq?('3.1.0.beta')
4
+ require 'sass/plugin/configuration'
5
+ ActiveSupport.on_load(:before_configuration) do
6
+ require 'sass'
7
+ require 'sass/plugin'
8
+ require 'sass/plugin/rails'
9
+ end
10
+ end
data/lib/sass/repl.rb ADDED
@@ -0,0 +1,57 @@
1
+ require 'readline'
2
+
3
+ module Sass
4
+ # Runs a SassScript read-eval-print loop.
5
+ # It presents a prompt on the terminal,
6
+ # reads in SassScript expressions,
7
+ # evaluates them,
8
+ # and prints the result.
9
+ class Repl
10
+ # @param options [{Symbol => Object}] An options hash.
11
+ def initialize(options = {})
12
+ @options = options
13
+ end
14
+
15
+ # Starts the read-eval-print loop.
16
+ def run
17
+ environment = Environment.new
18
+ @line = 0
19
+ loop do
20
+ @line += 1
21
+ unless (text = Readline.readline('>> '))
22
+ puts
23
+ return
24
+ end
25
+
26
+ Readline::HISTORY << text
27
+ parse_input(environment, text)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def parse_input(environment, text)
34
+ case text
35
+ when Script::MATCH
36
+ name = $1
37
+ guarded = !!$3
38
+ val = Script::Parser.parse($2, @line, text.size - ($3 || '').size - $2.size)
39
+
40
+ unless guarded && environment.var(name)
41
+ environment.set_var(name, val.perform(environment))
42
+ end
43
+
44
+ p environment.var(name)
45
+ else
46
+ p Script::Parser.parse(text, @line, 0).perform(environment)
47
+ end
48
+ rescue Sass::SyntaxError => e
49
+ puts "SyntaxError: #{e.message}"
50
+ if @options[:trace]
51
+ e.backtrace.each do |line|
52
+ puts "\tfrom #{line}"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/sass/root.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Sass
2
+ # The root directory of the Sass source tree.
3
+ # This may be overridden by the package manager
4
+ # if the lib directory is separated from the main source tree.
5
+ # @api public
6
+ ROOT_DIR = File.expand_path(File.join(__FILE__, "../../.."))
7
+ end
@@ -0,0 +1,33 @@
1
+ module Sass
2
+ module Script
3
+ # This is a subclass of {Lexer} for use in parsing plain CSS properties.
4
+ #
5
+ # @see Sass::SCSS::CssParser
6
+ class CssLexer < Lexer
7
+ private
8
+
9
+ def token
10
+ important || super
11
+ end
12
+
13
+ def string(re, *args)
14
+ if re == :uri
15
+ uri = scan(URI)
16
+ return unless uri
17
+ return [:string, Script::Value::String.new(uri)]
18
+ end
19
+
20
+ return unless scan(STRING)
21
+ string_value = Sass::Script::Value::String.value(@scanner[1] || @scanner[2])
22
+ value = Script::Value::String.new(string_value, :string)
23
+ [:string, value]
24
+ end
25
+
26
+ def important
27
+ s = scan(IMPORTANT)
28
+ return unless s
29
+ [:raw, s]
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'sass/script'
2
+ require 'sass/script/css_lexer'
3
+
4
+ module Sass
5
+ module Script
6
+ # This is a subclass of {Parser} for use in parsing plain CSS properties.
7
+ #
8
+ # @see Sass::SCSS::CssParser
9
+ class CssParser < Parser
10
+ private
11
+
12
+ # @private
13
+ def lexer_class; CssLexer; end
14
+
15
+ # We need a production that only does /,
16
+ # since * and % aren't allowed in plain CSS
17
+ production :div, :unary_plus, :div
18
+
19
+ def string
20
+ tok = try_tok(:string)
21
+ return number unless tok
22
+ unless @lexer.peek && @lexer.peek.type == :begin_interpolation
23
+ return literal_node(tok.value, tok.source_range)
24
+ end
25
+ end
26
+
27
+ # Short-circuit all the SassScript-only productions
28
+ alias_method :interpolation, :space
29
+ alias_method :or_expr, :div
30
+ alias_method :unary_div, :ident
31
+ alias_method :paren, :string
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,2626 @@
1
+ require 'sass/script/value/helpers'
2
+
3
+ module Sass::Script
4
+ # @comment
5
+ # YARD can't handle some multiline tags, and we need really long tags for function declarations.
6
+ # rubocop:disable LineLength
7
+ # Methods in this module are accessible from the SassScript context.
8
+ # For example, you can write
9
+ #
10
+ # $color: hsl(120deg, 100%, 50%)
11
+ #
12
+ # and it will call {Functions#hsl}.
13
+ #
14
+ # The following functions are provided:
15
+ #
16
+ # *Note: These functions are described in more detail below.*
17
+ #
18
+ # ## RGB Functions
19
+ #
20
+ # \{#rgb rgb($red, $green, $blue)}
21
+ # : Creates a {Sass::Script::Value::Color Color} from red, green, and blue
22
+ # values.
23
+ #
24
+ # \{#rgba rgba($red, $green, $blue, $alpha)}
25
+ # : Creates a {Sass::Script::Value::Color Color} from red, green, blue, and
26
+ # alpha values.
27
+ #
28
+ # \{#red red($color)}
29
+ # : Gets the red component of a color.
30
+ #
31
+ # \{#green green($color)}
32
+ # : Gets the green component of a color.
33
+ #
34
+ # \{#blue blue($color)}
35
+ # : Gets the blue component of a color.
36
+ #
37
+ # \{#mix mix($color1, $color2, \[$weight\])}
38
+ # : Mixes two colors together.
39
+ #
40
+ # ## HSL Functions
41
+ #
42
+ # \{#hsl hsl($hue, $saturation, $lightness)}
43
+ # : Creates a {Sass::Script::Value::Color Color} from hue, saturation, and
44
+ # lightness values.
45
+ #
46
+ # \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
47
+ # : Creates a {Sass::Script::Value::Color Color} from hue, saturation,
48
+ # lightness, and alpha values.
49
+ #
50
+ # \{#hue hue($color)}
51
+ # : Gets the hue component of a color.
52
+ #
53
+ # \{#saturation saturation($color)}
54
+ # : Gets the saturation component of a color.
55
+ #
56
+ # \{#lightness lightness($color)}
57
+ # : Gets the lightness component of a color.
58
+ #
59
+ # \{#adjust_hue adjust-hue($color, $degrees)}
60
+ # : Changes the hue of a color.
61
+ #
62
+ # \{#lighten lighten($color, $amount)}
63
+ # : Makes a color lighter.
64
+ #
65
+ # \{#darken darken($color, $amount)}
66
+ # : Makes a color darker.
67
+ #
68
+ # \{#saturate saturate($color, $amount)}
69
+ # : Makes a color more saturated.
70
+ #
71
+ # \{#desaturate desaturate($color, $amount)}
72
+ # : Makes a color less saturated.
73
+ #
74
+ # \{#grayscale grayscale($color)}
75
+ # : Converts a color to grayscale.
76
+ #
77
+ # \{#complement complement($color)}
78
+ # : Returns the complement of a color.
79
+ #
80
+ # \{#invert invert($color)}
81
+ # : Returns the inverse of a color.
82
+ #
83
+ # ## Opacity Functions
84
+ #
85
+ # \{#alpha alpha($color)} / \{#opacity opacity($color)}
86
+ # : Gets the alpha component (opacity) of a color.
87
+ #
88
+ # \{#rgba rgba($color, $alpha)}
89
+ # : Changes the alpha component for a color.
90
+ #
91
+ # \{#opacify opacify($color, $amount)} / \{#fade_in fade-in($color, $amount)}
92
+ # : Makes a color more opaque.
93
+ #
94
+ # \{#transparentize transparentize($color, $amount)} / \{#fade_out fade-out($color, $amount)}
95
+ # : Makes a color more transparent.
96
+ #
97
+ # ## Other Color Functions
98
+ #
99
+ # \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
100
+ # : Increases or decreases one or more components of a color.
101
+ #
102
+ # \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\])}
103
+ # : Fluidly scales one or more properties of a color.
104
+ #
105
+ # \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
106
+ # : Changes one or more properties of a color.
107
+ #
108
+ # \{#ie_hex_str ie-hex-str($color)}
109
+ # : Converts a color into the format understood by IE filters.
110
+ #
111
+ # ## String Functions
112
+ #
113
+ # \{#unquote unquote($string)}
114
+ # : Removes quotes from a string.
115
+ #
116
+ # \{#quote quote($string)}
117
+ # : Adds quotes to a string.
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
+ #
137
+ # ## Number Functions
138
+ #
139
+ # \{#percentage percentage($number)}
140
+ # : Converts a unitless number to a percentage.
141
+ #
142
+ # \{#round round($number)}
143
+ # : Rounds a number to the nearest whole number.
144
+ #
145
+ # \{#ceil ceil($number)}
146
+ # : Rounds a number up to the next whole number.
147
+ #
148
+ # \{#floor floor($number)}
149
+ # : Rounds a number down to the previous whole number.
150
+ #
151
+ # \{#abs abs($number)}
152
+ # : Returns the absolute value of a number.
153
+ #
154
+ # \{#min min($numbers...)\}
155
+ # : Finds the minimum of several numbers.
156
+ #
157
+ # \{#max max($numbers...)\}
158
+ # : Finds the maximum of several numbers.
159
+ #
160
+ # \{#random random([$limit])\}
161
+ # : Returns a random number.
162
+ #
163
+ # ## List Functions {#list-functions}
164
+ #
165
+ # All list functions work for maps as well, treating them as lists of pairs.
166
+ #
167
+ # \{#length length($list)}
168
+ # : Returns the length of a list.
169
+ #
170
+ # \{#nth nth($list, $n)}
171
+ # : Returns a specific item in a list.
172
+ #
173
+ # \{#join join($list1, $list2, \[$separator\])}
174
+ # : Joins together two lists into one.
175
+ #
176
+ # \{#append append($list1, $val, \[$separator\])}
177
+ # : Appends a single value onto the end of a list.
178
+ #
179
+ # \{#zip zip($lists...)}
180
+ # : Combines several lists into a single multidimensional list.
181
+ #
182
+ # \{#index index($list, $value)}
183
+ # : Returns the position of a value within a list.
184
+ #
185
+ # \{#list_separator list-separator(#list)}
186
+ # : Returns the separator of a list.
187
+ #
188
+ # ## Map Functions {#map-functions}
189
+ #
190
+ # \{#map_get map-get($map, $key)}
191
+ # : Returns the value in a map associated with a given key.
192
+ #
193
+ # \{#map_merge map-merge($map1, $map2)}
194
+ # : Merges two maps together into a new map.
195
+ #
196
+ # \{#map_remove map-remove($map, $keys...)}
197
+ # : Returns a new map with keys removed.
198
+ #
199
+ # \{#map_keys map-keys($map)}
200
+ # : Returns a list of all keys in a map.
201
+ #
202
+ # \{#map_values map-values($map)}
203
+ # : Returns a list of all values in a map.
204
+ #
205
+ # \{#map_has_key map-has-key($map, $key)}
206
+ # : Returns whether a map has a value associated with a given key.
207
+ #
208
+ # \{#keywords keywords($args)}
209
+ # : Returns the keywords passed to a function that takes variable arguments.
210
+ #
211
+ # ## Selector Functions
212
+ #
213
+ # Selector functions are very liberal in the formats they support
214
+ # for selector arguments. They can take a plain string, a list of
215
+ # lists as returned by `&` or anything in between:
216
+ #
217
+ # * A plain sring, such as `".foo .bar, .baz .bang"`.
218
+ # * A space-separated list of strings such as `(".foo" ".bar")`.
219
+ # * A comma-separated list of strings such as `(".foo .bar", ".baz .bang")`.
220
+ # * A comma-separated list of space-separated lists of strings such
221
+ # as `((".foo" ".bar"), (".baz" ".bang"))`.
222
+ #
223
+ # In general, selector functions allow placeholder selectors
224
+ # (`%foo`) but disallow parent-reference selectors (`&`).
225
+ #
226
+ # \{#selector_nest selector-nest($selectors...)}
227
+ # : Nests selector beneath one another like they would be nested in the
228
+ # stylesheet.
229
+ #
230
+ # \{#selector_append selector-append($selectors...)}
231
+ # : Appends selectors to one another without spaces in between.
232
+ #
233
+ # \{#selector_extend selector-extend($selector, $extendee, $extender)}
234
+ # : Extends `$extendee` with `$extender` within `$selector`.
235
+ #
236
+ # \{#selector_replace selector-replace($selector, $original, $replacement)}
237
+ # : Replaces `$original` with `$replacement` within `$selector`.
238
+ #
239
+ # \{#selector_unify selector-unify($selector1, $selector2)}
240
+ # : Unifies two selectors to produce a selector that matches
241
+ # elements matched by both.
242
+ #
243
+ # \{#is_superselector is-superselector($super, $sub)}
244
+ # : Returns whether `$super` matches all the elements `$sub` does, and
245
+ # possibly more.
246
+ #
247
+ # \{#simple_selectors simple-selectors($selector)}
248
+ # : Returns the simple selectors that comprise a compound selector.
249
+ #
250
+ # \{#selector_parse selector-parse($selector)}
251
+ # : Parses a selector into the format returned by `&`.
252
+ #
253
+ # ## Introspection Functions
254
+ #
255
+ # \{#feature_exists feature-exists($feature)}
256
+ # : Returns whether a feature exists in the current Sass runtime.
257
+ #
258
+ # \{#variable_exists variable-exists($name)}
259
+ # : Returns whether a variable with the given name exists in the current scope.
260
+ #
261
+ # \{#global_variable_exists global-variable-exists($name)}
262
+ # : Returns whether a variable with the given name exists in the global scope.
263
+ #
264
+ # \{#function_exists function-exists($name)}
265
+ # : Returns whether a function with the given name exists.
266
+ #
267
+ # \{#mixin_exists mixin-exists($name)}
268
+ # : Returns whether a mixin with the given name exists.
269
+ #
270
+ # \{#inspect inspect($value)}
271
+ # : Returns the string representation of a value as it would be represented in Sass.
272
+ #
273
+ # \{#type_of type-of($value)}
274
+ # : Returns the type of a value.
275
+ #
276
+ # \{#unit unit($number)}
277
+ # : Returns the unit(s) associated with a number.
278
+ #
279
+ # \{#unitless unitless($number)}
280
+ # : Returns whether a number has units.
281
+ #
282
+ # \{#comparable comparable($number1, $number2)}
283
+ # : Returns whether two numbers can be added, subtracted, or compared.
284
+ #
285
+ # \{#call call($name, $args...)}
286
+ # : Dynamically calls a Sass function.
287
+ #
288
+ # ## Miscellaneous Functions
289
+ #
290
+ # \{#if if($condition, $if-true, $if-false)}
291
+ # : Returns one of two values, depending on whether or not `$condition` is
292
+ # true.
293
+ #
294
+ # \{#unique_id unique-id()}
295
+ # : Returns a unique CSS identifier.
296
+ #
297
+ # ## Adding Custom Functions
298
+ #
299
+ # New Sass functions can be added by adding Ruby methods to this module.
300
+ # For example:
301
+ #
302
+ # module Sass::Script::Functions
303
+ # def reverse(string)
304
+ # assert_type string, :String
305
+ # Sass::Script::Value::String.new(string.value.reverse)
306
+ # end
307
+ # declare :reverse, [:string]
308
+ # end
309
+ #
310
+ # Calling {declare} tells Sass the argument names for your function.
311
+ # If omitted, the function will still work, but will not be able to accept keyword arguments.
312
+ # {declare} can also allow your function to take arbitrary keyword arguments.
313
+ #
314
+ # There are a few things to keep in mind when modifying this module.
315
+ # First of all, the arguments passed are {Value} objects.
316
+ # Value objects are also expected to be returned.
317
+ # This means that Ruby values must be unwrapped and wrapped.
318
+ #
319
+ # Most Value objects support the {Value::Base#value value} accessor for getting
320
+ # their Ruby values. Color objects, though, must be accessed using
321
+ # {Sass::Script::Value::Color#rgb rgb}, {Sass::Script::Value::Color#red red},
322
+ # {Sass::Script::Value::Color#blue green}, or {Sass::Script::Value::Color#blue
323
+ # blue}.
324
+ #
325
+ # Second, making Ruby functions accessible from Sass introduces the temptation
326
+ # to do things like database access within stylesheets.
327
+ # This is generally a bad idea;
328
+ # since Sass files are by default only compiled once,
329
+ # dynamic code is not a great fit.
330
+ #
331
+ # If you really, really need to compile Sass on each request,
332
+ # first make sure you have adequate caching set up.
333
+ # Then you can use {Sass::Engine} to render the code,
334
+ # using the {file:SASS_REFERENCE.md#custom-option `options` parameter}
335
+ # to pass in data that {EvaluationContext#options can be accessed}
336
+ # from your Sass functions.
337
+ #
338
+ # Within one of the functions in this module,
339
+ # methods of {EvaluationContext} can be used.
340
+ #
341
+ # ### Caveats
342
+ #
343
+ # When creating new {Value} objects within functions, be aware that it's not
344
+ # safe to call {Value::Base#to_s #to_s} (or other methods that use the string
345
+ # representation) on those objects without first setting {Tree::Node#options=
346
+ # the #options attribute}.
347
+ #
348
+ # @comment
349
+ # rubocop:enable LineLength
350
+ module Functions
351
+ @signatures = {}
352
+
353
+ # A class representing a Sass function signature.
354
+ #
355
+ # @attr args [Array<String>] The names of the arguments to the function.
356
+ # @attr delayed_args [Array<String>] The names of the arguments whose evaluation should be
357
+ # delayed.
358
+ # @attr var_args [Boolean] Whether the function takes a variable number of arguments.
359
+ # @attr var_kwargs [Boolean] Whether the function takes an arbitrary set of keyword arguments.
360
+ Signature = Struct.new(:args, :delayed_args, :var_args, :var_kwargs, :deprecated)
361
+
362
+ # Declare a Sass signature for a Ruby-defined function.
363
+ # This includes the names of the arguments,
364
+ # whether the function takes a variable number of arguments,
365
+ # and whether the function takes an arbitrary set of keyword arguments.
366
+ #
367
+ # It's not necessary to declare a signature for a function.
368
+ # However, without a signature it won't support keyword arguments.
369
+ #
370
+ # A single function can have multiple signatures declared
371
+ # as long as each one takes a different number of arguments.
372
+ # It's also possible to declare multiple signatures
373
+ # that all take the same number of arguments,
374
+ # but none of them but the first will be used
375
+ # unless the user uses keyword arguments.
376
+ #
377
+ # @example
378
+ # declare :rgba, [:hex, :alpha]
379
+ # declare :rgba, [:red, :green, :blue, :alpha]
380
+ # declare :accepts_anything, [], :var_args => true, :var_kwargs => true
381
+ # declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
382
+ #
383
+ # @param method_name [Symbol] The name of the method
384
+ # whose signature is being declared.
385
+ # @param args [Array<Symbol>] The names of the arguments for the function signature.
386
+ # @option options :var_args [Boolean] (false)
387
+ # Whether the function accepts a variable number of (unnamed) arguments
388
+ # in addition to the named arguments.
389
+ # @option options :var_kwargs [Boolean] (false)
390
+ # Whether the function accepts other keyword arguments
391
+ # in addition to those in `:args`.
392
+ # If this is true, the Ruby function will be passed a hash from strings
393
+ # to {Value}s as the last argument.
394
+ # In addition, if this is true and `:var_args` is not,
395
+ # Sass will ensure that the last argument passed is a hash.
396
+ def self.declare(method_name, args, options = {})
397
+ delayed_args = []
398
+ args = args.map do |a|
399
+ a = a.to_s
400
+ if a[0] == ?&
401
+ a = a[1..-1]
402
+ delayed_args << a
403
+ end
404
+ a
405
+ end
406
+ # We don't expose this functionality except to certain builtin methods.
407
+ if delayed_args.any? && method_name != :if
408
+ raise ArgumentError.new("Delayed arguments are not allowed for method #{method_name}")
409
+ end
410
+ @signatures[method_name] ||= []
411
+ @signatures[method_name] << Signature.new(
412
+ args,
413
+ delayed_args,
414
+ options[:var_args],
415
+ options[:var_kwargs],
416
+ options[:deprecated] && options[:deprecated].map {|a| a.to_s})
417
+ end
418
+
419
+ # Determine the correct signature for the number of arguments
420
+ # passed in for a given function.
421
+ # If no signatures match, the first signature is returned for error messaging.
422
+ #
423
+ # @param method_name [Symbol] The name of the Ruby function to be called.
424
+ # @param arg_arity [Fixnum] The number of unnamed arguments the function was passed.
425
+ # @param kwarg_arity [Fixnum] The number of keyword arguments the function was passed.
426
+ #
427
+ # @return [{Symbol => Object}, nil]
428
+ # The signature options for the matching signature,
429
+ # or nil if no signatures are declared for this function. See {declare}.
430
+ def self.signature(method_name, arg_arity, kwarg_arity)
431
+ return unless @signatures[method_name]
432
+ @signatures[method_name].each do |signature|
433
+ sig_arity = signature.args.size
434
+ return signature if sig_arity == arg_arity + kwarg_arity
435
+ next unless sig_arity < arg_arity + kwarg_arity
436
+
437
+ # We have enough args.
438
+ # Now we need to figure out which args are varargs
439
+ # and if the signature allows them.
440
+ t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
441
+ if sig_arity > t_arg_arity
442
+ # we transfer some kwargs arity to args arity
443
+ # if it does not have enough args -- assuming the names will work out.
444
+ t_kwarg_arity -= (sig_arity - t_arg_arity)
445
+ t_arg_arity = sig_arity
446
+ end
447
+
448
+ if (t_arg_arity == sig_arity || t_arg_arity > sig_arity && signature.var_args) &&
449
+ (t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
450
+ return signature
451
+ end
452
+ end
453
+ @signatures[method_name].first
454
+ end
455
+
456
+ # Sets the random seed used by Sass's internal random number generator.
457
+ #
458
+ # This can be used to ensure consistent random number sequences which
459
+ # allows for consistent results when testing, etc.
460
+ #
461
+ # @param seed [Integer]
462
+ # @return [Integer] The same seed.
463
+ def self.random_seed=(seed)
464
+ @random_number_generator = Sass::Util::CrossPlatformRandom.new(seed)
465
+ end
466
+
467
+ # Get Sass's internal random number generator.
468
+ #
469
+ # @return [Random]
470
+ def self.random_number_generator
471
+ @random_number_generator ||= Sass::Util::CrossPlatformRandom.new
472
+ end
473
+
474
+ # The context in which methods in {Script::Functions} are evaluated.
475
+ # That means that all instance methods of {EvaluationContext}
476
+ # are available to use in functions.
477
+ class EvaluationContext
478
+ include Functions
479
+ include Value::Helpers
480
+
481
+ # The human-readable names for [Sass::Script::Value::Base]. The default is
482
+ # just the downcased name of the type. The default is the downcased type
483
+ # name.
484
+ TYPE_NAMES = {:ArgList => 'variable argument list'}
485
+
486
+ # The environment for this function. This environment's
487
+ # {Environment#parent} is the global environment, and its
488
+ # {Environment#caller} is a read-only view of the local environment of the
489
+ # caller of this function.
490
+ #
491
+ # @return [Environment]
492
+ attr_reader :environment
493
+
494
+ # The options hash for the {Sass::Engine} that is processing the function call
495
+ #
496
+ # @return [{Symbol => Object}]
497
+ attr_reader :options
498
+
499
+ # @param environment [Environment] See \{#environment}
500
+ def initialize(environment)
501
+ @environment = environment
502
+ @options = environment.options
503
+ end
504
+
505
+ # Asserts that the type of a given SassScript value
506
+ # is the expected type (designated by a symbol).
507
+ #
508
+ # Valid types are `:Bool`, `:Color`, `:Number`, and `:String`.
509
+ # Note that `:String` will match both double-quoted strings
510
+ # and unquoted identifiers.
511
+ #
512
+ # @example
513
+ # assert_type value, :String
514
+ # assert_type value, :Number
515
+ # @param value [Sass::Script::Value::Base] A SassScript value
516
+ # @param type [Symbol] The name of the type the value is expected to be
517
+ # @param name [String, Symbol, nil] The name of the argument.
518
+ # @raise [ArgumentError] if value is not of the correct type.
519
+ def assert_type(value, type, name = nil)
520
+ klass = Sass::Script::Value.const_get(type)
521
+ return if value.is_a?(klass)
522
+ return if value.is_a?(Sass::Script::Value::List) && type == :Map && value.value.empty?
523
+ err = "#{value.inspect} is not a #{TYPE_NAMES[type] || type.to_s.downcase}"
524
+ err = "$#{name.to_s.gsub('_', '-')}: " + err if name
525
+ raise ArgumentError.new(err)
526
+ end
527
+
528
+ # Asserts that the unit of the number is as expected.
529
+ #
530
+ # @example
531
+ # assert_unit number, "px"
532
+ # assert_unit number, nil
533
+ # @param number [Sass::Script::Value::Number] The number to be validated.
534
+ # @param unit [::String]
535
+ # The unit that the number must have.
536
+ # If nil, the number must be unitless.
537
+ # @param name [::String] The name of the parameter being validated.
538
+ # @raise [ArgumentError] if number is not of the correct unit or is not a number.
539
+ def assert_unit(number, unit, name = nil)
540
+ assert_type number, :Number, name
541
+ return if number.is_unit?(unit)
542
+ expectation = unit ? "have a unit of #{unit}" : "be unitless"
543
+ if name
544
+ raise ArgumentError.new("Expected $#{name} to #{expectation} but got #{number}")
545
+ else
546
+ raise ArgumentError.new("Expected #{number} to #{expectation}")
547
+ end
548
+ end
549
+
550
+ # Asserts that the value is an integer.
551
+ #
552
+ # @example
553
+ # assert_integer 2px
554
+ # assert_integer 2.5px
555
+ # => SyntaxError: "Expected 2.5px to be an integer"
556
+ # assert_integer 2.5px, "width"
557
+ # => SyntaxError: "Expected width to be an integer but got 2.5px"
558
+ # @param number [Sass::Script::Value::Base] The value to be validated.
559
+ # @param name [::String] The name of the parameter being validated.
560
+ # @raise [ArgumentError] if number is not an integer or is not a number.
561
+ def assert_integer(number, name = nil)
562
+ assert_type number, :Number, name
563
+ return if number.int?
564
+ if name
565
+ raise ArgumentError.new("Expected $#{name} to be an integer but got #{number}")
566
+ else
567
+ raise ArgumentError.new("Expected #{number} to be an integer")
568
+ end
569
+ end
570
+
571
+ # Performs a node that has been delayed for execution.
572
+ #
573
+ # @private
574
+ # @param node [Sass::Script::Tree::Node,
575
+ # Sass::Script::Value::Base] When this is a tree node, it's
576
+ # performed in the caller's environment. When it's a value
577
+ # (which can happen when the value had to be performed already
578
+ # -- like for a splat), it's returned as-is.
579
+ # @param env [Sass::Environment] The environment within which to perform the node.
580
+ # Defaults to the (read-only) environment of the caller.
581
+ def perform(node, env = environment.caller)
582
+ if node.is_a?(Sass::Script::Value::Base)
583
+ node
584
+ else
585
+ node.perform(env)
586
+ end
587
+ end
588
+ end
589
+
590
+ class << self
591
+ # Returns whether user function with a given name exists.
592
+ #
593
+ # @param function_name [String]
594
+ # @return [Boolean]
595
+ alias_method :callable?, :public_method_defined?
596
+
597
+ private
598
+
599
+ def include(*args)
600
+ r = super
601
+ # We have to re-include ourselves into EvaluationContext to work around
602
+ # an icky Ruby restriction.
603
+ EvaluationContext.send :include, self
604
+ r
605
+ end
606
+ end
607
+
608
+ # Creates a {Sass::Script::Value::Color Color} object from red, green, and
609
+ # blue values.
610
+ #
611
+ # @see #rgba
612
+ # @overload rgb($red, $green, $blue)
613
+ # @param $red [Sass::Script::Value::Number] The amount of red in the color.
614
+ # Must be between 0 and 255 inclusive, or between `0%` and `100%`
615
+ # inclusive
616
+ # @param $green [Sass::Script::Value::Number] The amount of green in the
617
+ # color. Must be between 0 and 255 inclusive, or between `0%` and `100%`
618
+ # inclusive
619
+ # @param $blue [Sass::Script::Value::Number] The amount of blue in the
620
+ # color. Must be between 0 and 255 inclusive, or between `0%` and `100%`
621
+ # inclusive
622
+ # @return [Sass::Script::Value::Color]
623
+ # @raise [ArgumentError] if any parameter is the wrong type or out of bounds
624
+ def rgb(red, green, blue)
625
+ assert_type red, :Number, :red
626
+ assert_type green, :Number, :green
627
+ assert_type blue, :Number, :blue
628
+
629
+ color_attrs = [[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
630
+ if c.is_unit?("%")
631
+ c.value * 255 / 100.0
632
+ elsif c.unitless?
633
+ c.value
634
+ else
635
+ raise ArgumentError.new("Expected #{c} to be unitless or have a unit of % but got #{c}")
636
+ end
637
+ end
638
+
639
+ # Don't store the string representation for function-created colors, both
640
+ # because it's not very useful and because some functions aren't supported
641
+ # on older browsers.
642
+ Sass::Script::Value::Color.new(color_attrs)
643
+ end
644
+ declare :rgb, [:red, :green, :blue]
645
+
646
+ # Creates a {Sass::Script::Value::Color Color} from red, green, blue, and
647
+ # alpha values.
648
+ # @see #rgb
649
+ #
650
+ # @overload rgba($red, $green, $blue, $alpha)
651
+ # @param $red [Sass::Script::Value::Number] The amount of red in the
652
+ # color. Must be between 0 and 255 inclusive
653
+ # @param $green [Sass::Script::Value::Number] The amount of green in the
654
+ # color. Must be between 0 and 255 inclusive
655
+ # @param $blue [Sass::Script::Value::Number] The amount of blue in the
656
+ # color. Must be between 0 and 255 inclusive
657
+ # @param $alpha [Sass::Script::Value::Number] The opacity of the color.
658
+ # Must be between 0 and 1 inclusive
659
+ # @return [Sass::Script::Value::Color]
660
+ # @raise [ArgumentError] if any parameter is the wrong type or out of
661
+ # bounds
662
+ #
663
+ # @overload rgba($color, $alpha)
664
+ # Sets the opacity of an existing color.
665
+ #
666
+ # @example
667
+ # rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
668
+ # rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
669
+ #
670
+ # @param $color [Sass::Script::Value::Color] The color whose opacity will
671
+ # be changed.
672
+ # @param $alpha [Sass::Script::Value::Number] The new opacity of the
673
+ # color. Must be between 0 and 1 inclusive
674
+ # @return [Sass::Script::Value::Color]
675
+ # @raise [ArgumentError] if `$alpha` is out of bounds or either parameter
676
+ # is the wrong type
677
+ def rgba(*args)
678
+ case args.size
679
+ when 2
680
+ color, alpha = args
681
+
682
+ assert_type color, :Color, :color
683
+ assert_type alpha, :Number, :alpha
684
+
685
+ color.with(:alpha => alpha.value)
686
+ when 4
687
+ red, green, blue, alpha = args
688
+ rgba(rgb(red, green, blue), alpha)
689
+ else
690
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for 4)")
691
+ end
692
+ end
693
+ declare :rgba, [:red, :green, :blue, :alpha]
694
+ declare :rgba, [:color, :alpha]
695
+
696
+ # Creates a {Sass::Script::Value::Color Color} from hue, saturation, and
697
+ # lightness values. Uses the algorithm from the [CSS3 spec][].
698
+ #
699
+ # [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
700
+ #
701
+ # @see #hsla
702
+ # @overload hsl($hue, $saturation, $lightness)
703
+ # @param $hue [Sass::Script::Value::Number] The hue of the color. Should be
704
+ # between 0 and 360 degrees, inclusive
705
+ # @param $saturation [Sass::Script::Value::Number] The saturation of the
706
+ # color. Must be between `0%` and `100%`, inclusive
707
+ # @param $lightness [Sass::Script::Value::Number] The lightness of the
708
+ # color. Must be between `0%` and `100%`, inclusive
709
+ # @return [Sass::Script::Value::Color]
710
+ # @raise [ArgumentError] if `$saturation` or `$lightness` are out of bounds
711
+ # or any parameter is the wrong type
712
+ def hsl(hue, saturation, lightness)
713
+ hsla(hue, saturation, lightness, number(1))
714
+ end
715
+ declare :hsl, [:hue, :saturation, :lightness]
716
+
717
+ # Creates a {Sass::Script::Value::Color Color} from hue,
718
+ # saturation, lightness, and alpha values. Uses the algorithm from
719
+ # the [CSS3 spec][].
720
+ #
721
+ # [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
722
+ #
723
+ # @see #hsl
724
+ # @overload hsla($hue, $saturation, $lightness, $alpha)
725
+ # @param $hue [Sass::Script::Value::Number] The hue of the color. Should be
726
+ # between 0 and 360 degrees, inclusive
727
+ # @param $saturation [Sass::Script::Value::Number] The saturation of the
728
+ # color. Must be between `0%` and `100%`, inclusive
729
+ # @param $lightness [Sass::Script::Value::Number] The lightness of the
730
+ # color. Must be between `0%` and `100%`, inclusive
731
+ # @param $alpha [Sass::Script::Value::Number] The opacity of the color. Must
732
+ # be between 0 and 1, inclusive
733
+ # @return [Sass::Script::Value::Color]
734
+ # @raise [ArgumentError] if `$saturation`, `$lightness`, or `$alpha` are out
735
+ # of bounds or any parameter is the wrong type
736
+ def hsla(hue, saturation, lightness, alpha)
737
+ assert_type hue, :Number, :hue
738
+ assert_type saturation, :Number, :saturation
739
+ assert_type lightness, :Number, :lightness
740
+ assert_type alpha, :Number, :alpha
741
+
742
+ h = hue.value
743
+ s = saturation.value
744
+ l = lightness.value
745
+
746
+ # Don't store the string representation for function-created colors, both
747
+ # because it's not very useful and because some functions aren't supported
748
+ # on older browsers.
749
+ Sass::Script::Value::Color.new(
750
+ :hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
751
+ end
752
+ declare :hsla, [:hue, :saturation, :lightness, :alpha]
753
+
754
+ # Gets the red component of a color. Calculated from HSL where necessary via
755
+ # [this algorithm][hsl-to-rgb].
756
+ #
757
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
758
+ #
759
+ # @overload red($color)
760
+ # @param $color [Sass::Script::Value::Color]
761
+ # @return [Sass::Script::Value::Number] The red component, between 0 and 255
762
+ # inclusive
763
+ # @raise [ArgumentError] if `$color` isn't a color
764
+ def red(color)
765
+ assert_type color, :Color, :color
766
+ number(color.red)
767
+ end
768
+ declare :red, [:color]
769
+
770
+ # Gets the green component of a color. Calculated from HSL where necessary
771
+ # via [this algorithm][hsl-to-rgb].
772
+ #
773
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
774
+ #
775
+ # @overload green($color)
776
+ # @param $color [Sass::Script::Value::Color]
777
+ # @return [Sass::Script::Value::Number] The green component, between 0 and
778
+ # 255 inclusive
779
+ # @raise [ArgumentError] if `$color` isn't a color
780
+ def green(color)
781
+ assert_type color, :Color, :color
782
+ number(color.green)
783
+ end
784
+ declare :green, [:color]
785
+
786
+ # Gets the blue component of a color. Calculated from HSL where necessary
787
+ # via [this algorithm][hsl-to-rgb].
788
+ #
789
+ # [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
790
+ #
791
+ # @overload blue($color)
792
+ # @param $color [Sass::Script::Value::Color]
793
+ # @return [Sass::Script::Value::Number] The blue component, between 0 and
794
+ # 255 inclusive
795
+ # @raise [ArgumentError] if `$color` isn't a color
796
+ def blue(color)
797
+ assert_type color, :Color, :color
798
+ number(color.blue)
799
+ end
800
+ declare :blue, [:color]
801
+
802
+ # Returns the hue component of a color. See [the CSS3 HSL
803
+ # specification][hsl]. Calculated from RGB where necessary via [this
804
+ # algorithm][rgb-to-hsl].
805
+ #
806
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
807
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
808
+ #
809
+ # @overload hue($color)
810
+ # @param $color [Sass::Script::Value::Color]
811
+ # @return [Sass::Script::Value::Number] The hue component, between 0deg and
812
+ # 360deg
813
+ # @raise [ArgumentError] if `$color` isn't a color
814
+ def hue(color)
815
+ assert_type color, :Color, :color
816
+ number(color.hue, "deg")
817
+ end
818
+ declare :hue, [:color]
819
+
820
+ # Returns the saturation component of a color. See [the CSS3 HSL
821
+ # specification][hsl]. Calculated from RGB where necessary via [this
822
+ # algorithm][rgb-to-hsl].
823
+ #
824
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
825
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
826
+ #
827
+ # @overload saturation($color)
828
+ # @param $color [Sass::Script::Value::Color]
829
+ # @return [Sass::Script::Value::Number] The saturation component, between 0%
830
+ # and 100%
831
+ # @raise [ArgumentError] if `$color` isn't a color
832
+ def saturation(color)
833
+ assert_type color, :Color, :color
834
+ number(color.saturation, "%")
835
+ end
836
+ declare :saturation, [:color]
837
+
838
+ # Returns the lightness component of a color. See [the CSS3 HSL
839
+ # specification][hsl]. Calculated from RGB where necessary via [this
840
+ # algorithm][rgb-to-hsl].
841
+ #
842
+ # [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
843
+ # [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
844
+ #
845
+ # @overload lightness($color)
846
+ # @param $color [Sass::Script::Value::Color]
847
+ # @return [Sass::Script::Value::Number] The lightness component, between 0%
848
+ # and 100%
849
+ # @raise [ArgumentError] if `$color` isn't a color
850
+ def lightness(color)
851
+ assert_type color, :Color, :color
852
+ number(color.lightness, "%")
853
+ end
854
+ declare :lightness, [:color]
855
+
856
+ # Returns the alpha component (opacity) of a color. This is 1 unless
857
+ # otherwise specified.
858
+ #
859
+ # This function also supports the proprietary Microsoft `alpha(opacity=20)`
860
+ # syntax as a special case.
861
+ #
862
+ # @overload alpha($color)
863
+ # @param $color [Sass::Script::Value::Color]
864
+ # @return [Sass::Script::Value::Number] The alpha component, between 0 and 1
865
+ # @raise [ArgumentError] if `$color` isn't a color
866
+ def alpha(*args)
867
+ if args.all? do |a|
868
+ a.is_a?(Sass::Script::Value::String) && a.type == :identifier &&
869
+ a.value =~ /^[a-zA-Z]+\s*=/
870
+ end
871
+ # Support the proprietary MS alpha() function
872
+ return identifier("alpha(#{args.map {|a| a.to_s}.join(", ")})")
873
+ end
874
+
875
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)") if args.size != 1
876
+
877
+ assert_type args.first, :Color, :color
878
+ number(args.first.alpha)
879
+ end
880
+ declare :alpha, [:color]
881
+
882
+ # Returns the alpha component (opacity) of a color. This is 1 unless
883
+ # otherwise specified.
884
+ #
885
+ # @overload opacity($color)
886
+ # @param $color [Sass::Script::Value::Color]
887
+ # @return [Sass::Script::Value::Number] The alpha component, between 0 and 1
888
+ # @raise [ArgumentError] if `$color` isn't a color
889
+ def opacity(color)
890
+ if color.is_a?(Sass::Script::Value::Number)
891
+ return identifier("opacity(#{color})")
892
+ end
893
+ assert_type color, :Color, :color
894
+ number(color.alpha)
895
+ end
896
+ declare :opacity, [:color]
897
+
898
+ # Makes a color more opaque. Takes a color and a number between 0 and 1, and
899
+ # returns a color with the opacity increased by that amount.
900
+ #
901
+ # @see #transparentize
902
+ # @example
903
+ # opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
904
+ # opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
905
+ # @overload opacify($color, $amount)
906
+ # @param $color [Sass::Script::Value::Color]
907
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
908
+ # opacity by, between 0 and 1
909
+ # @return [Sass::Script::Value::Color]
910
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
911
+ # is the wrong type
912
+ def opacify(color, amount)
913
+ _adjust(color, amount, :alpha, 0..1, :+)
914
+ end
915
+ declare :opacify, [:color, :amount]
916
+
917
+ alias_method :fade_in, :opacify
918
+ declare :fade_in, [:color, :amount]
919
+
920
+ # Makes a color more transparent. Takes a color and a number between 0 and
921
+ # 1, and returns a color with the opacity decreased by that amount.
922
+ #
923
+ # @see #opacify
924
+ # @example
925
+ # transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
926
+ # transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
927
+ # @overload transparentize($color, $amount)
928
+ # @param $color [Sass::Script::Value::Color]
929
+ # @param $amount [Sass::Script::Value::Number] The amount to decrease the
930
+ # opacity by, between 0 and 1
931
+ # @return [Sass::Script::Value::Color]
932
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
933
+ # is the wrong type
934
+ def transparentize(color, amount)
935
+ _adjust(color, amount, :alpha, 0..1, :-)
936
+ end
937
+ declare :transparentize, [:color, :amount]
938
+
939
+ alias_method :fade_out, :transparentize
940
+ declare :fade_out, [:color, :amount]
941
+
942
+ # Makes a color lighter. Takes a color and a number between `0%` and `100%`,
943
+ # and returns a color with the lightness increased by that amount.
944
+ #
945
+ # @see #darken
946
+ # @example
947
+ # lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
948
+ # lighten(#800, 20%) => #e00
949
+ # @overload lighten($color, $amount)
950
+ # @param $color [Sass::Script::Value::Color]
951
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
952
+ # lightness by, between `0%` and `100%`
953
+ # @return [Sass::Script::Value::Color]
954
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
955
+ # is the wrong type
956
+ def lighten(color, amount)
957
+ _adjust(color, amount, :lightness, 0..100, :+, "%")
958
+ end
959
+ declare :lighten, [:color, :amount]
960
+
961
+ # Makes a color darker. Takes a color and a number between 0% and 100%, and
962
+ # returns a color with the lightness decreased by that amount.
963
+ #
964
+ # @see #lighten
965
+ # @example
966
+ # darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
967
+ # darken(#800, 20%) => #200
968
+ # @overload darken($color, $amount)
969
+ # @param $color [Sass::Script::Value::Color]
970
+ # @param $amount [Sass::Script::Value::Number] The amount to decrease the
971
+ # lightness by, between `0%` and `100%`
972
+ # @return [Sass::Script::Value::Color]
973
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
974
+ # is the wrong type
975
+ def darken(color, amount)
976
+ _adjust(color, amount, :lightness, 0..100, :-, "%")
977
+ end
978
+ declare :darken, [:color, :amount]
979
+
980
+ # Makes a color more saturated. Takes a color and a number between 0% and
981
+ # 100%, and returns a color with the saturation increased by that amount.
982
+ #
983
+ # @see #desaturate
984
+ # @example
985
+ # saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
986
+ # saturate(#855, 20%) => #9e3f3f
987
+ # @overload saturate($color, $amount)
988
+ # @param $color [Sass::Script::Value::Color]
989
+ # @param $amount [Sass::Script::Value::Number] The amount to increase the
990
+ # saturation by, between `0%` and `100%`
991
+ # @return [Sass::Script::Value::Color]
992
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
993
+ # is the wrong type
994
+ def saturate(color, amount = nil)
995
+ # Support the filter effects definition of saturate.
996
+ # https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
997
+ return identifier("saturate(#{color})") if amount.nil?
998
+ _adjust(color, amount, :saturation, 0..100, :+, "%")
999
+ end
1000
+ declare :saturate, [:color, :amount]
1001
+ declare :saturate, [:amount]
1002
+
1003
+ # Makes a color less saturated. Takes a color and a number between 0% and
1004
+ # 100%, and returns a color with the saturation decreased by that value.
1005
+ #
1006
+ # @see #saturate
1007
+ # @example
1008
+ # desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
1009
+ # desaturate(#855, 20%) => #726b6b
1010
+ # @overload desaturate($color, $amount)
1011
+ # @param $color [Sass::Script::Value::Color]
1012
+ # @param $amount [Sass::Script::Value::Number] The amount to decrease the
1013
+ # saturation by, between `0%` and `100%`
1014
+ # @return [Sass::Script::Value::Color]
1015
+ # @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
1016
+ # is the wrong type
1017
+ def desaturate(color, amount)
1018
+ _adjust(color, amount, :saturation, 0..100, :-, "%")
1019
+ end
1020
+ declare :desaturate, [:color, :amount]
1021
+
1022
+ # Changes the hue of a color. Takes a color and a number of degrees (usually
1023
+ # between `-360deg` and `360deg`), and returns a color with the hue rotated
1024
+ # along the color wheel by that amount.
1025
+ #
1026
+ # @example
1027
+ # adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
1028
+ # adjust-hue(hsl(120, 30%, 90%), -60deg) => hsl(60, 30%, 90%)
1029
+ # adjust-hue(#811, 45deg) => #886a11
1030
+ # @overload adjust_hue($color, $degrees)
1031
+ # @param $color [Sass::Script::Value::Color]
1032
+ # @param $degrees [Sass::Script::Value::Number] The number of degrees to
1033
+ # rotate the hue
1034
+ # @return [Sass::Script::Value::Color]
1035
+ # @raise [ArgumentError] if either parameter is the wrong type
1036
+ def adjust_hue(color, degrees)
1037
+ assert_type color, :Color, :color
1038
+ assert_type degrees, :Number, :degrees
1039
+ color.with(:hue => color.hue + degrees.value)
1040
+ end
1041
+ declare :adjust_hue, [:color, :degrees]
1042
+
1043
+ # Converts a color into the format understood by IE filters.
1044
+ #
1045
+ # @example
1046
+ # ie-hex-str(#abc) => #FFAABBCC
1047
+ # ie-hex-str(#3322BB) => #FF3322BB
1048
+ # ie-hex-str(rgba(0, 255, 0, 0.5)) => #8000FF00
1049
+ # @overload ie_hex_str($color)
1050
+ # @param $color [Sass::Script::Value::Color]
1051
+ # @return [Sass::Script::Value::String] The IE-formatted string
1052
+ # representation of the color
1053
+ # @raise [ArgumentError] if `$color` isn't a color
1054
+ def ie_hex_str(color)
1055
+ assert_type color, :Color, :color
1056
+ alpha = (color.alpha * 255).round.to_s(16).rjust(2, '0')
1057
+ identifier("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
1058
+ end
1059
+ declare :ie_hex_str, [:color]
1060
+
1061
+ # Increases or decreases one or more properties of a color. This can change
1062
+ # the red, green, blue, hue, saturation, value, and alpha properties. The
1063
+ # properties are specified as keyword arguments, and are added to or
1064
+ # subtracted from the color's current value for that property.
1065
+ #
1066
+ # All properties are optional. You can't specify both RGB properties
1067
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
1068
+ # `$value`) at the same time.
1069
+ #
1070
+ # @example
1071
+ # adjust-color(#102030, $blue: 5) => #102035
1072
+ # adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
1073
+ # adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
1074
+ # @comment
1075
+ # rubocop:disable LineLength
1076
+ # @overload adjust_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
1077
+ # @comment
1078
+ # rubocop:disable LineLength
1079
+ # @param $color [Sass::Script::Value::Color]
1080
+ # @param $red [Sass::Script::Value::Number] The adjustment to make on the
1081
+ # red component, between -255 and 255 inclusive
1082
+ # @param $green [Sass::Script::Value::Number] The adjustment to make on the
1083
+ # green component, between -255 and 255 inclusive
1084
+ # @param $blue [Sass::Script::Value::Number] The adjustment to make on the
1085
+ # blue component, between -255 and 255 inclusive
1086
+ # @param $hue [Sass::Script::Value::Number] The adjustment to make on the
1087
+ # hue component, in degrees
1088
+ # @param $saturation [Sass::Script::Value::Number] The adjustment to make on
1089
+ # the saturation component, between `-100%` and `100%` inclusive
1090
+ # @param $lightness [Sass::Script::Value::Number] The adjustment to make on
1091
+ # the lightness component, between `-100%` and `100%` inclusive
1092
+ # @param $alpha [Sass::Script::Value::Number] The adjustment to make on the
1093
+ # alpha component, between -1 and 1 inclusive
1094
+ # @return [Sass::Script::Value::Color]
1095
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1096
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1097
+ # same time
1098
+ def adjust_color(color, kwargs)
1099
+ assert_type color, :Color, :color
1100
+ with = Sass::Util.map_hash(
1101
+ "red" => [-255..255, ""],
1102
+ "green" => [-255..255, ""],
1103
+ "blue" => [-255..255, ""],
1104
+ "hue" => nil,
1105
+ "saturation" => [-100..100, "%"],
1106
+ "lightness" => [-100..100, "%"],
1107
+ "alpha" => [-1..1, ""]
1108
+ ) do |name, (range, units)|
1109
+
1110
+ val = kwargs.delete(name)
1111
+ next unless val
1112
+ assert_type val, :Number, name
1113
+ Sass::Util.check_range("$#{name}: Amount", range, val, units) if range
1114
+ adjusted = color.send(name) + val.value
1115
+ adjusted = [0, Sass::Util.restrict(adjusted, range)].max if range
1116
+ [name.to_sym, adjusted]
1117
+ end
1118
+
1119
+ unless kwargs.empty?
1120
+ name, val = kwargs.to_a.first
1121
+ raise ArgumentError.new("Unknown argument $#{name} (#{val})")
1122
+ end
1123
+
1124
+ color.with(with)
1125
+ end
1126
+ declare :adjust_color, [:color], :var_kwargs => true
1127
+
1128
+ # Fluidly scales one or more properties of a color. Unlike
1129
+ # \{#adjust_color adjust-color}, which changes a color's properties by fixed
1130
+ # amounts, \{#scale_color scale-color} fluidly changes them based on how
1131
+ # high or low they already are. That means that lightening an already-light
1132
+ # color with \{#scale_color scale-color} won't change the lightness much,
1133
+ # but lightening a dark color by the same amount will change it more
1134
+ # dramatically. This has the benefit of making `scale-color($color, ...)`
1135
+ # have a similar effect regardless of what `$color` is.
1136
+ #
1137
+ # For example, the lightness of a color can be anywhere between `0%` and
1138
+ # `100%`. If `scale-color($color, $lightness: 40%)` is called, the resulting
1139
+ # color's lightness will be 40% of the way between its original lightness
1140
+ # and 100. If `scale-color($color, $lightness: -40%)` is called instead, the
1141
+ # lightness will be 40% of the way between the original and 0.
1142
+ #
1143
+ # This can change the red, green, blue, saturation, value, and alpha
1144
+ # properties. The properties are specified as keyword arguments. All
1145
+ # arguments should be percentages between `0%` and `100%`.
1146
+ #
1147
+ # All properties are optional. You can't specify both RGB properties
1148
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$saturation`, `$value`)
1149
+ # at the same time.
1150
+ #
1151
+ # @example
1152
+ # scale-color(hsl(120, 70%, 80%), $lightness: 50%) => hsl(120, 70%, 90%)
1153
+ # scale-color(rgb(200, 150%, 170%), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
1154
+ # scale-color(hsl(200, 70%, 80%), $saturation: -90%, $alpha: -30%) => hsla(200, 7%, 80%, 0.7)
1155
+ # @comment
1156
+ # rubocop:disable LineLength
1157
+ # @overload scale_color($color, [$red], [$green], [$blue], [$saturation], [$lightness], [$alpha])
1158
+ # @comment
1159
+ # rubocop:disable LineLength
1160
+ # @param $color [Sass::Script::Value::Color]
1161
+ # @param $red [Sass::Script::Value::Number]
1162
+ # @param $green [Sass::Script::Value::Number]
1163
+ # @param $blue [Sass::Script::Value::Number]
1164
+ # @param $saturation [Sass::Script::Value::Number]
1165
+ # @param $lightness [Sass::Script::Value::Number]
1166
+ # @param $alpha [Sass::Script::Value::Number]
1167
+ # @return [Sass::Script::Value::Color]
1168
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1169
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1170
+ # same time
1171
+ def scale_color(color, kwargs)
1172
+ assert_type color, :Color, :color
1173
+ with = Sass::Util.map_hash(
1174
+ "red" => 255,
1175
+ "green" => 255,
1176
+ "blue" => 255,
1177
+ "saturation" => 100,
1178
+ "lightness" => 100,
1179
+ "alpha" => 1
1180
+ ) do |name, max|
1181
+
1182
+ val = kwargs.delete(name)
1183
+ next unless val
1184
+ assert_type val, :Number, name
1185
+ assert_unit val, '%', name
1186
+ Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
1187
+
1188
+ current = color.send(name)
1189
+ scale = val.value / 100.0
1190
+ diff = scale > 0 ? max - current : current
1191
+ [name.to_sym, current + diff * scale]
1192
+ end
1193
+
1194
+ unless kwargs.empty?
1195
+ name, val = kwargs.to_a.first
1196
+ raise ArgumentError.new("Unknown argument $#{name} (#{val})")
1197
+ end
1198
+
1199
+ color.with(with)
1200
+ end
1201
+ declare :scale_color, [:color], :var_kwargs => true
1202
+
1203
+ # Changes one or more properties of a color. This can change the red, green,
1204
+ # blue, hue, saturation, value, and alpha properties. The properties are
1205
+ # specified as keyword arguments, and replace the color's current value for
1206
+ # that property.
1207
+ #
1208
+ # All properties are optional. You can't specify both RGB properties
1209
+ # (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
1210
+ # `$value`) at the same time.
1211
+ #
1212
+ # @example
1213
+ # change-color(#102030, $blue: 5) => #102005
1214
+ # change-color(#102030, $red: 120, $blue: 5) => #782005
1215
+ # change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
1216
+ # @comment
1217
+ # rubocop:disable LineLength
1218
+ # @overload change_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
1219
+ # @comment
1220
+ # rubocop:disable LineLength
1221
+ # @param $color [Sass::Script::Value::Color]
1222
+ # @param $red [Sass::Script::Value::Number] The new red component for the
1223
+ # color, within 0 and 255 inclusive
1224
+ # @param $green [Sass::Script::Value::Number] The new green component for
1225
+ # the color, within 0 and 255 inclusive
1226
+ # @param $blue [Sass::Script::Value::Number] The new blue component for the
1227
+ # color, within 0 and 255 inclusive
1228
+ # @param $hue [Sass::Script::Value::Number] The new hue component for the
1229
+ # color, in degrees
1230
+ # @param $saturation [Sass::Script::Value::Number] The new saturation
1231
+ # component for the color, between `0%` and `100%` inclusive
1232
+ # @param $lightness [Sass::Script::Value::Number] The new lightness
1233
+ # component for the color, within `0%` and `100%` inclusive
1234
+ # @param $alpha [Sass::Script::Value::Number] The new alpha component for
1235
+ # the color, within 0 and 1 inclusive
1236
+ # @return [Sass::Script::Value::Color]
1237
+ # @raise [ArgumentError] if any parameter is the wrong type or out-of
1238
+ # bounds, or if RGB properties and HSL properties are adjusted at the
1239
+ # same time
1240
+ def change_color(color, kwargs)
1241
+ assert_type color, :Color, :color
1242
+ with = Sass::Util.map_hash(
1243
+ 'red' => ['Red value', 0..255],
1244
+ 'green' => ['Green value', 0..255],
1245
+ 'blue' => ['Blue value', 0..255],
1246
+ 'hue' => [],
1247
+ 'saturation' => ['Saturation', 0..100, '%'],
1248
+ 'lightness' => ['Lightness', 0..100, '%'],
1249
+ 'alpha' => ['Alpha channel', 0..1]
1250
+ ) do |name, (desc, range, unit)|
1251
+ val = kwargs.delete(name)
1252
+ next unless val
1253
+ assert_type val, :Number, name
1254
+
1255
+ if range
1256
+ val = Sass::Util.check_range(desc, range, val, unit)
1257
+ else
1258
+ val = val.value
1259
+ end
1260
+
1261
+ [name.to_sym, val]
1262
+ end
1263
+
1264
+ unless kwargs.empty?
1265
+ name, val = kwargs.to_a.first
1266
+ raise ArgumentError.new("Unknown argument $#{name} (#{val})")
1267
+ end
1268
+
1269
+ color.with(with)
1270
+ end
1271
+ declare :change_color, [:color], :var_kwargs => true
1272
+
1273
+ # Mixes two colors together. Specifically, takes the average of each of the
1274
+ # RGB components, optionally weighted by the given percentage. The opacity
1275
+ # of the colors is also considered when weighting the components.
1276
+ #
1277
+ # The weight specifies the amount of the first color that should be included
1278
+ # in the returned color. The default, `50%`, means that half the first color
1279
+ # and half the second color should be used. `25%` means that a quarter of
1280
+ # the first color and three quarters of the second color should be used.
1281
+ #
1282
+ # @example
1283
+ # mix(#f00, #00f) => #7f007f
1284
+ # mix(#f00, #00f, 25%) => #3f00bf
1285
+ # mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
1286
+ # @overload mix($color1, $color2, $weight: 50%)
1287
+ # @param $color1 [Sass::Script::Value::Color]
1288
+ # @param $color2 [Sass::Script::Value::Color]
1289
+ # @param $weight [Sass::Script::Value::Number] The relative weight of each
1290
+ # color. Closer to `0%` gives more weight to `$color`, closer to `100%`
1291
+ # gives more weight to `$color2`
1292
+ # @return [Sass::Script::Value::Color]
1293
+ # @raise [ArgumentError] if `$weight` is out of bounds or any parameter is
1294
+ # the wrong type
1295
+ def mix(color1, color2, weight = number(50))
1296
+ assert_type color1, :Color, :color1
1297
+ assert_type color2, :Color, :color2
1298
+ assert_type weight, :Number, :weight
1299
+
1300
+ Sass::Util.check_range("Weight", 0..100, weight, '%')
1301
+
1302
+ # This algorithm factors in both the user-provided weight (w) and the
1303
+ # difference between the alpha values of the two colors (a) to decide how
1304
+ # to perform the weighted average of the two RGB values.
1305
+ #
1306
+ # It works by first normalizing both parameters to be within [-1, 1],
1307
+ # where 1 indicates "only use color1", -1 indicates "only use color2", and
1308
+ # all values in between indicated a proportionately weighted average.
1309
+ #
1310
+ # Once we have the normalized variables w and a, we apply the formula
1311
+ # (w + a)/(1 + w*a) to get the combined weight (in [-1, 1]) of color1.
1312
+ # This formula has two especially nice properties:
1313
+ #
1314
+ # * When either w or a are -1 or 1, the combined weight is also that number
1315
+ # (cases where w * a == -1 are undefined, and handled as a special case).
1316
+ #
1317
+ # * When a is 0, the combined weight is w, and vice versa.
1318
+ #
1319
+ # Finally, the weight of color1 is renormalized to be within [0, 1]
1320
+ # and the weight of color2 is given by 1 minus the weight of color1.
1321
+ p = (weight.value / 100.0).to_f
1322
+ w = p * 2 - 1
1323
+ a = color1.alpha - color2.alpha
1324
+
1325
+ w1 = ((w * a == -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0
1326
+ w2 = 1 - w1
1327
+
1328
+ rgba = color1.rgb.zip(color2.rgb).map {|v1, v2| v1 * w1 + v2 * w2}
1329
+ rgba << color1.alpha * p + color2.alpha * (1 - p)
1330
+ rgb_color(*rgba)
1331
+ end
1332
+ declare :mix, [:color1, :color2]
1333
+ declare :mix, [:color1, :color2, :weight]
1334
+
1335
+ # Converts a color to grayscale. This is identical to `desaturate(color,
1336
+ # 100%)`.
1337
+ #
1338
+ # @see #desaturate
1339
+ # @overload grayscale($color)
1340
+ # @param $color [Sass::Script::Value::Color]
1341
+ # @return [Sass::Script::Value::Color]
1342
+ # @raise [ArgumentError] if `$color` isn't a color
1343
+ def grayscale(color)
1344
+ if color.is_a?(Sass::Script::Value::Number)
1345
+ return identifier("grayscale(#{color})")
1346
+ end
1347
+ desaturate color, number(100)
1348
+ end
1349
+ declare :grayscale, [:color]
1350
+
1351
+ # Returns the complement of a color. This is identical to `adjust-hue(color,
1352
+ # 180deg)`.
1353
+ #
1354
+ # @see #adjust_hue #adjust-hue
1355
+ # @overload complement($color)
1356
+ # @param $color [Sass::Script::Value::Color]
1357
+ # @return [Sass::Script::Value::Color]
1358
+ # @raise [ArgumentError] if `$color` isn't a color
1359
+ def complement(color)
1360
+ adjust_hue color, number(180)
1361
+ end
1362
+ declare :complement, [:color]
1363
+
1364
+ # Returns the inverse (negative) of a color. The red, green, and blue values
1365
+ # are inverted, while the opacity is left alone.
1366
+ #
1367
+ # @overload invert($color)
1368
+ # @param $color [Sass::Script::Value::Color]
1369
+ # @return [Sass::Script::Value::Color]
1370
+ # @raise [ArgumentError] if `$color` isn't a color
1371
+ def invert(color)
1372
+ if color.is_a?(Sass::Script::Value::Number)
1373
+ return identifier("invert(#{color})")
1374
+ end
1375
+
1376
+ assert_type color, :Color, :color
1377
+ color.with(
1378
+ :red => (255 - color.red),
1379
+ :green => (255 - color.green),
1380
+ :blue => (255 - color.blue))
1381
+ end
1382
+ declare :invert, [:color]
1383
+
1384
+ # Removes quotes from a string. If the string is already unquoted, this will
1385
+ # return it unmodified.
1386
+ #
1387
+ # @see #quote
1388
+ # @example
1389
+ # unquote("foo") => foo
1390
+ # unquote(foo) => foo
1391
+ # @overload unquote($string)
1392
+ # @param $string [Sass::Script::Value::String]
1393
+ # @return [Sass::Script::Value::String]
1394
+ # @raise [ArgumentError] if `$string` isn't a string
1395
+ def unquote(string)
1396
+ if string.is_a?(Sass::Script::Value::String) && string.type != :identifier
1397
+ identifier(string.value)
1398
+ else
1399
+ string
1400
+ end
1401
+ end
1402
+ declare :unquote, [:string]
1403
+
1404
+ # Add quotes to a string if the string isn't quoted,
1405
+ # or returns the same string if it is.
1406
+ #
1407
+ # @see #unquote
1408
+ # @example
1409
+ # quote("foo") => "foo"
1410
+ # quote(foo) => "foo"
1411
+ # @overload quote($string)
1412
+ # @param $string [Sass::Script::Value::String]
1413
+ # @return [Sass::Script::Value::String]
1414
+ # @raise [ArgumentError] if `$string` isn't a string
1415
+ def quote(string)
1416
+ assert_type string, :String, :string
1417
+ if string.type != :string
1418
+ quoted_string(string.value)
1419
+ else
1420
+ string
1421
+ end
1422
+ end
1423
+ declare :quote, [:string]
1424
+
1425
+ # Returns the number of characters in a string.
1426
+ #
1427
+ # @example
1428
+ # str-length("foo") => 3
1429
+ # @overload str_length($string)
1430
+ # @param $string [Sass::Script::Value::String]
1431
+ # @return [Sass::Script::Value::Number]
1432
+ # @raise [ArgumentError] if `$string` isn't a string
1433
+ def str_length(string)
1434
+ assert_type string, :String, :string
1435
+ number(string.value.size)
1436
+ end
1437
+ declare :str_length, [:string]
1438
+
1439
+ # Inserts `$insert` into `$string` at `$index`.
1440
+ #
1441
+ # Note that unlike some languages, the first character in a Sass string is
1442
+ # number 1, the second number 2, and so forth.
1443
+ #
1444
+ # @example
1445
+ # str-insert("abcd", "X", 1) => "Xabcd"
1446
+ # str-insert("abcd", "X", 4) => "abcXd"
1447
+ # str-insert("abcd", "X", 5) => "abcdX"
1448
+ #
1449
+ # @overload str_insert($string, $insert, $index)
1450
+ # @param $string [Sass::Script::Value::String]
1451
+ # @param $insert [Sass::Script::Value::String]
1452
+ # @param $index [Sass::Script::Value::Number] The position at which
1453
+ # `$insert` will be inserted. Negative indices count from the end of
1454
+ # `$string`. An index that's outside the bounds of the string will insert
1455
+ # `$insert` at the front or back of the string
1456
+ # @return [Sass::Script::Value::String] The result string. This will be
1457
+ # quoted if and only if `$string` was quoted
1458
+ # @raise [ArgumentError] if any parameter is the wrong type
1459
+ def str_insert(original, insert, index)
1460
+ assert_type original, :String, :string
1461
+ assert_type insert, :String, :insert
1462
+ assert_integer index, :index
1463
+ assert_unit index, nil, :index
1464
+ insertion_point = if index.value > 0
1465
+ [index.value - 1, original.value.size].min
1466
+ else
1467
+ [index.value, -original.value.size - 1].max
1468
+ end
1469
+ result = original.value.dup.insert(insertion_point, insert.value)
1470
+ Sass::Script::Value::String.new(result, original.type)
1471
+ end
1472
+ declare :str_insert, [:string, :insert, :index]
1473
+
1474
+ # Returns the index of the first occurrence of `$substring` in `$string`. If
1475
+ # there is no such occurrence, returns `null`.
1476
+ #
1477
+ # Note that unlike some languages, the first character in a Sass string is
1478
+ # number 1, the second number 2, and so forth.
1479
+ #
1480
+ # @example
1481
+ # str-index(abcd, a) => 1
1482
+ # str-index(abcd, ab) => 1
1483
+ # str-index(abcd, X) => null
1484
+ # str-index(abcd, c) => 3
1485
+ #
1486
+ # @overload str_index($string, $substring)
1487
+ # @param $string [Sass::Script::Value::String]
1488
+ # @param $substring [Sass::Script::Value::String]
1489
+ # @return [Sass::Script::Value::Number, Sass::Script::Value::Null]
1490
+ # @raise [ArgumentError] if any parameter is the wrong type
1491
+ def str_index(string, substring)
1492
+ assert_type string, :String, :string
1493
+ assert_type substring, :String, :substring
1494
+ index = string.value.index(substring.value)
1495
+ index ? number(index + 1) : null
1496
+ end
1497
+ declare :str_index, [:string, :substring]
1498
+
1499
+ # Extracts a substring from `$string`. The substring will begin at index
1500
+ # `$start-at` and ends at index `$end-at`.
1501
+ #
1502
+ # Note that unlike some languages, the first character in a Sass string is
1503
+ # number 1, the second number 2, and so forth.
1504
+ #
1505
+ # @example
1506
+ # str-slice("abcd", 2, 3) => "bc"
1507
+ # str-slice("abcd", 2) => "bcd"
1508
+ # str-slice("abcd", -3, -2) => "bc"
1509
+ # str-slice("abcd", 2, -2) => "bc"
1510
+ #
1511
+ # @overload str_slice($string, $start-at, $end-at: -1)
1512
+ # @param $start-at [Sass::Script::Value::Number] The index of the first
1513
+ # character of the substring. If this is negative, it counts from the end
1514
+ # of `$string`
1515
+ # @param $end-before [Sass::Script::Value::Number] The index of the last
1516
+ # character of the substring. If this is negative, it counts from the end
1517
+ # of `$string`. Defaults to -1
1518
+ # @return [Sass::Script::Value::String] The substring. This will be quoted
1519
+ # if and only if `$string` was quoted
1520
+ # @raise [ArgumentError] if any parameter is the wrong type
1521
+ def str_slice(string, start_at, end_at = nil)
1522
+ assert_type string, :String, :string
1523
+ assert_unit start_at, nil, "start-at"
1524
+
1525
+ end_at = number(-1) if end_at.nil?
1526
+ assert_unit end_at, nil, "end-at"
1527
+
1528
+ return Sass::Script::Value::String.new("", string.type) if end_at.value == 0
1529
+ s = start_at.value > 0 ? start_at.value - 1 : start_at.value
1530
+ e = end_at.value > 0 ? end_at.value - 1 : end_at.value
1531
+ s = string.value.length + s if s < 0
1532
+ s = 0 if s < 0
1533
+ e = string.value.length + e if e < 0
1534
+ e = 0 if s < 0
1535
+ extracted = string.value.slice(s..e)
1536
+ Sass::Script::Value::String.new(extracted || "", string.type)
1537
+ end
1538
+ declare :str_slice, [:string, :start_at]
1539
+ declare :str_slice, [:string, :start_at, :end_at]
1540
+
1541
+ # Converts a string to upper case.
1542
+ #
1543
+ # @example
1544
+ # to-upper-case(abcd) => ABCD
1545
+ #
1546
+ # @overload to_upper_case($string)
1547
+ # @param $string [Sass::Script::Value::String]
1548
+ # @return [Sass::Script::Value::String]
1549
+ # @raise [ArgumentError] if `$string` isn't a string
1550
+ def to_upper_case(string)
1551
+ assert_type string, :String, :string
1552
+ Sass::Script::Value::String.new(string.value.upcase, string.type)
1553
+ end
1554
+ declare :to_upper_case, [:string]
1555
+
1556
+ # Convert a string to lower case,
1557
+ #
1558
+ # @example
1559
+ # to-lower-case(ABCD) => abcd
1560
+ #
1561
+ # @overload to_lower_case($string)
1562
+ # @param $string [Sass::Script::Value::String]
1563
+ # @return [Sass::Script::Value::String]
1564
+ # @raise [ArgumentError] if `$string` isn't a string
1565
+ def to_lower_case(string)
1566
+ assert_type string, :String, :string
1567
+ Sass::Script::Value::String.new(string.value.downcase, string.type)
1568
+ end
1569
+ declare :to_lower_case, [:string]
1570
+
1571
+ # Returns the type of a value.
1572
+ #
1573
+ # @example
1574
+ # type-of(100px) => number
1575
+ # type-of(asdf) => string
1576
+ # type-of("asdf") => string
1577
+ # type-of(true) => bool
1578
+ # type-of(#fff) => color
1579
+ # type-of(blue) => color
1580
+ # @overload type_of($value)
1581
+ # @param $value [Sass::Script::Value::Base] The value to inspect
1582
+ # @return [Sass::Script::Value::String] The unquoted string name of the
1583
+ # value's type
1584
+ def type_of(value)
1585
+ identifier(value.class.name.gsub(/Sass::Script::Value::/, '').downcase)
1586
+ end
1587
+ declare :type_of, [:value]
1588
+
1589
+ # Returns whether a feature exists in the current Sass runtime.
1590
+ #
1591
+ # The following features are supported:
1592
+ #
1593
+ # * `global-variable-shadowing` indicates that a local variable will shadow
1594
+ # a global variable unless `!global` is used.
1595
+ #
1596
+ # * `extend-selector-pseudoclass` indicates that `@extend` will reach into
1597
+ # selector pseudoclasses like `:not`.
1598
+ #
1599
+ # * `units-level-3` indicates full support for unit arithmetic using units
1600
+ # defined in the [Values and Units Level 3][] spec.
1601
+ #
1602
+ # [Values and Units Level 3]: http://www.w3.org/TR/css3-values/
1603
+ #
1604
+ # * `at-error` indicates that the Sass `@error` directive is supported.
1605
+ #
1606
+ # @example
1607
+ # feature-exists(some-feature-that-exists) => true
1608
+ # feature-exists(what-is-this-i-dont-know) => false
1609
+ #
1610
+ # @overload feature_exists($feature)
1611
+ # @param $feature [Sass::Script::Value::String] The name of the feature
1612
+ # @return [Sass::Script::Value::Bool] Whether the feature is supported in this version of Sass
1613
+ # @raise [ArgumentError] if `$feature` isn't a string
1614
+ def feature_exists(feature)
1615
+ assert_type feature, :String, :feature
1616
+ bool(Sass.has_feature?(feature.value))
1617
+ end
1618
+ declare :feature_exists, [:feature]
1619
+
1620
+ # Returns the unit(s) associated with a number. Complex units are sorted in
1621
+ # alphabetical order by numerator and denominator.
1622
+ #
1623
+ # @example
1624
+ # unit(100) => ""
1625
+ # unit(100px) => "px"
1626
+ # unit(3em) => "em"
1627
+ # unit(10px * 5em) => "em*px"
1628
+ # unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
1629
+ # @overload unit($number)
1630
+ # @param $number [Sass::Script::Value::Number]
1631
+ # @return [Sass::Script::Value::String] The unit(s) of the number, as a
1632
+ # quoted string
1633
+ # @raise [ArgumentError] if `$number` isn't a number
1634
+ def unit(number)
1635
+ assert_type number, :Number, :number
1636
+ quoted_string(number.unit_str)
1637
+ end
1638
+ declare :unit, [:number]
1639
+
1640
+ # Returns whether a number has units.
1641
+ #
1642
+ # @example
1643
+ # unitless(100) => true
1644
+ # unitless(100px) => false
1645
+ # @overload unitless($number)
1646
+ # @param $number [Sass::Script::Value::Number]
1647
+ # @return [Sass::Script::Value::Bool]
1648
+ # @raise [ArgumentError] if `$number` isn't a number
1649
+ def unitless(number)
1650
+ assert_type number, :Number, :number
1651
+ bool(number.unitless?)
1652
+ end
1653
+ declare :unitless, [:number]
1654
+
1655
+ # Returns whether two numbers can added, subtracted, or compared.
1656
+ #
1657
+ # @example
1658
+ # comparable(2px, 1px) => true
1659
+ # comparable(100px, 3em) => false
1660
+ # comparable(10cm, 3mm) => true
1661
+ # @overload comparable($number1, $number2)
1662
+ # @param $number1 [Sass::Script::Value::Number]
1663
+ # @param $number2 [Sass::Script::Value::Number]
1664
+ # @return [Sass::Script::Value::Bool]
1665
+ # @raise [ArgumentError] if either parameter is the wrong type
1666
+ def comparable(number1, number2)
1667
+ assert_type number1, :Number, :number1
1668
+ assert_type number2, :Number, :number2
1669
+ bool(number1.comparable_to?(number2))
1670
+ end
1671
+ declare :comparable, [:number1, :number2]
1672
+
1673
+ # Converts a unitless number to a percentage.
1674
+ #
1675
+ # @example
1676
+ # percentage(0.2) => 20%
1677
+ # percentage(100px / 50px) => 200%
1678
+ # @overload percentage($number)
1679
+ # @param $number [Sass::Script::Value::Number]
1680
+ # @return [Sass::Script::Value::Number]
1681
+ # @raise [ArgumentError] if `$number` isn't a unitless number
1682
+ def percentage(number)
1683
+ unless number.is_a?(Sass::Script::Value::Number) && number.unitless?
1684
+ raise ArgumentError.new("$number: #{number.inspect} is not a unitless number")
1685
+ end
1686
+ number(number.value * 100, '%')
1687
+ end
1688
+ declare :percentage, [:number]
1689
+
1690
+ # Rounds a number to the nearest whole number.
1691
+ #
1692
+ # @example
1693
+ # round(10.4px) => 10px
1694
+ # round(10.6px) => 11px
1695
+ # @overload round($number)
1696
+ # @param $number [Sass::Script::Value::Number]
1697
+ # @return [Sass::Script::Value::Number]
1698
+ # @raise [ArgumentError] if `$number` isn't a number
1699
+ def round(number)
1700
+ numeric_transformation(number) {|n| n.round}
1701
+ end
1702
+ declare :round, [:number]
1703
+
1704
+ # Rounds a number up to the next whole number.
1705
+ #
1706
+ # @example
1707
+ # ceil(10.4px) => 11px
1708
+ # ceil(10.6px) => 11px
1709
+ # @overload ceil($number)
1710
+ # @param $number [Sass::Script::Value::Number]
1711
+ # @return [Sass::Script::Value::Number]
1712
+ # @raise [ArgumentError] if `$number` isn't a number
1713
+ def ceil(number)
1714
+ numeric_transformation(number) {|n| n.ceil}
1715
+ end
1716
+ declare :ceil, [:number]
1717
+
1718
+ # Rounds a number down to the previous whole number.
1719
+ #
1720
+ # @example
1721
+ # floor(10.4px) => 10px
1722
+ # floor(10.6px) => 10px
1723
+ # @overload floor($number)
1724
+ # @param $number [Sass::Script::Value::Number]
1725
+ # @return [Sass::Script::Value::Number]
1726
+ # @raise [ArgumentError] if `$number` isn't a number
1727
+ def floor(number)
1728
+ numeric_transformation(number) {|n| n.floor}
1729
+ end
1730
+ declare :floor, [:number]
1731
+
1732
+ # Returns the absolute value of a number.
1733
+ #
1734
+ # @example
1735
+ # abs(10px) => 10px
1736
+ # abs(-10px) => 10px
1737
+ # @overload abs($number)
1738
+ # @param $number [Sass::Script::Value::Number]
1739
+ # @return [Sass::Script::Value::Number]
1740
+ # @raise [ArgumentError] if `$number` isn't a number
1741
+ def abs(number)
1742
+ numeric_transformation(number) {|n| n.abs}
1743
+ end
1744
+ declare :abs, [:number]
1745
+
1746
+ # Finds the minimum of several numbers. This function takes any number of
1747
+ # arguments.
1748
+ #
1749
+ # @example
1750
+ # min(1px, 4px) => 1px
1751
+ # min(5em, 3em, 4em) => 3em
1752
+ # @overload min($numbers...)
1753
+ # @param $numbers [[Sass::Script::Value::Number]]
1754
+ # @return [Sass::Script::Value::Number]
1755
+ # @raise [ArgumentError] if any argument isn't a number, or if not all of
1756
+ # the arguments have comparable units
1757
+ def min(*numbers)
1758
+ numbers.each {|n| assert_type n, :Number}
1759
+ numbers.inject {|min, num| min.lt(num).to_bool ? min : num}
1760
+ end
1761
+ declare :min, [], :var_args => :true
1762
+
1763
+ # Finds the maximum of several numbers. This function takes any number of
1764
+ # arguments.
1765
+ #
1766
+ # @example
1767
+ # max(1px, 4px) => 4px
1768
+ # max(5em, 3em, 4em) => 5em
1769
+ # @overload max($numbers...)
1770
+ # @param $numbers [[Sass::Script::Value::Number]]
1771
+ # @return [Sass::Script::Value::Number]
1772
+ # @raise [ArgumentError] if any argument isn't a number, or if not all of
1773
+ # the arguments have comparable units
1774
+ def max(*values)
1775
+ values.each {|v| assert_type v, :Number}
1776
+ values.inject {|max, val| max.gt(val).to_bool ? max : val}
1777
+ end
1778
+ declare :max, [], :var_args => :true
1779
+
1780
+ # Return the length of a list.
1781
+ #
1782
+ # This can return the number of pairs in a map as well.
1783
+ #
1784
+ # @example
1785
+ # length(10px) => 1
1786
+ # length(10px 20px 30px) => 3
1787
+ # length((width: 10px, height: 20px)) => 2
1788
+ # @overload length($list)
1789
+ # @param $list [Sass::Script::Value::Base]
1790
+ # @return [Sass::Script::Value::Number]
1791
+ def length(list)
1792
+ number(list.to_a.size)
1793
+ end
1794
+ declare :length, [:list]
1795
+
1796
+ # Return a new list, based on the list provided, but with the nth
1797
+ # element changed to the value given.
1798
+ #
1799
+ # Note that unlike some languages, the first item in a Sass list is number
1800
+ # 1, the second number 2, and so forth.
1801
+ #
1802
+ # Negative index values address elements in reverse order, starting with the last element
1803
+ # in the list.
1804
+ #
1805
+ # @example
1806
+ # set-nth($list: 10px 20px 30px, $n: 2, $value: -20px) => 10px -20px 30px
1807
+ # @overload set-nth($list, $n, $value)
1808
+ # @param $list [Sass::Script::Value::Base] The list that will be copied, having the element
1809
+ # at index `$n` changed.
1810
+ # @param $n [Sass::Script::Value::Number] The index of the item to set.
1811
+ # Negative indices count from the end of the list.
1812
+ # @param $value [Sass::Script::Value::Base] The new value at index `$n`.
1813
+ # @return [Sass::Script::Value::List]
1814
+ # @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
1815
+ # of `$list`
1816
+ def set_nth(list, n, value)
1817
+ assert_type n, :Number, :n
1818
+ Sass::Script::Value::List.assert_valid_index(list, n)
1819
+ index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1820
+ new_list = list.to_a.dup
1821
+ new_list[index] = value
1822
+ Sass::Script::Value::List.new(new_list, list.separator)
1823
+ end
1824
+ declare :set_nth, [:list, :n, :value]
1825
+
1826
+ # Gets the nth item in a list.
1827
+ #
1828
+ # Note that unlike some languages, the first item in a Sass list is number
1829
+ # 1, the second number 2, and so forth.
1830
+ #
1831
+ # This can return the nth pair in a map as well.
1832
+ #
1833
+ # Negative index values address elements in reverse order, starting with the last element in
1834
+ # the list.
1835
+ #
1836
+ # @example
1837
+ # nth(10px 20px 30px, 1) => 10px
1838
+ # nth((Helvetica, Arial, sans-serif), 3) => sans-serif
1839
+ # nth((width: 10px, length: 20px), 2) => length, 20px
1840
+ # @overload nth($list, $n)
1841
+ # @param $list [Sass::Script::Value::Base]
1842
+ # @param $n [Sass::Script::Value::Number] The index of the item to get.
1843
+ # Negative indices count from the end of the list.
1844
+ # @return [Sass::Script::Value::Base]
1845
+ # @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
1846
+ # of `$list`
1847
+ def nth(list, n)
1848
+ assert_type n, :Number, :n
1849
+ Sass::Script::Value::List.assert_valid_index(list, n)
1850
+
1851
+ index = n.to_i > 0 ? n.to_i - 1 : n.to_i
1852
+ list.to_a[index]
1853
+ end
1854
+ declare :nth, [:list, :n]
1855
+
1856
+ # Joins together two lists into one.
1857
+ #
1858
+ # Unless `$separator` is passed, if one list is comma-separated and one is
1859
+ # space-separated, the first parameter's separator is used for the resulting
1860
+ # list. If both lists have fewer than two items, spaces are used for the
1861
+ # resulting list.
1862
+ #
1863
+ # @example
1864
+ # join(10px 20px, 30px 40px) => 10px 20px 30px 40px
1865
+ # join((blue, red), (#abc, #def)) => blue, red, #abc, #def
1866
+ # join(10px, 20px) => 10px 20px
1867
+ # join(10px, 20px, comma) => 10px, 20px
1868
+ # join((blue, red), (#abc, #def), space) => blue red #abc #def
1869
+ # @overload join($list1, $list2, $separator: auto)
1870
+ # @param $list1 [Sass::Script::Value::Base]
1871
+ # @param $list2 [Sass::Script::Value::Base]
1872
+ # @param $separator [Sass::Script::Value::String] The list separator to use.
1873
+ # If this is `comma` or `space`, that separator will be used. If this is
1874
+ # `auto` (the default), the separator is determined as explained above.
1875
+ # @return [Sass::Script::Value::List]
1876
+ def join(list1, list2, separator = identifier("auto"))
1877
+ assert_type separator, :String, :separator
1878
+ unless %w[auto space comma].include?(separator.value)
1879
+ raise ArgumentError.new("Separator name must be space, comma, or auto")
1880
+ end
1881
+ sep = if separator.value == 'auto'
1882
+ list1.separator || list2.separator || :space
1883
+ else
1884
+ separator.value.to_sym
1885
+ end
1886
+ list(list1.to_a + list2.to_a, sep)
1887
+ end
1888
+ declare :join, [:list1, :list2]
1889
+ declare :join, [:list1, :list2, :separator]
1890
+
1891
+ # Appends a single value onto the end of a list.
1892
+ #
1893
+ # Unless the `$separator` argument is passed, if the list had only one item,
1894
+ # the resulting list will be space-separated.
1895
+ #
1896
+ # @example
1897
+ # append(10px 20px, 30px) => 10px 20px 30px
1898
+ # append((blue, red), green) => blue, red, green
1899
+ # append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
1900
+ # append(10px, 20px, comma) => 10px, 20px
1901
+ # append((blue, red), green, space) => blue red green
1902
+ # @overload append($list, $val, $separator: auto)
1903
+ # @param $list [Sass::Script::Value::Base]
1904
+ # @param $val [Sass::Script::Value::Base]
1905
+ # @param $separator [Sass::Script::Value::String] The list separator to use.
1906
+ # If this is `comma` or `space`, that separator will be used. If this is
1907
+ # `auto` (the default), the separator is determined as explained above.
1908
+ # @return [Sass::Script::Value::List]
1909
+ def append(list, val, separator = identifier("auto"))
1910
+ assert_type separator, :String, :separator
1911
+ unless %w[auto space comma].include?(separator.value)
1912
+ raise ArgumentError.new("Separator name must be space, comma, or auto")
1913
+ end
1914
+ sep = if separator.value == 'auto'
1915
+ list.separator || :space
1916
+ else
1917
+ separator.value.to_sym
1918
+ end
1919
+ list(list.to_a + [val], sep)
1920
+ end
1921
+ declare :append, [:list, :val]
1922
+ declare :append, [:list, :val, :separator]
1923
+
1924
+ # Combines several lists into a single multidimensional list. The nth value
1925
+ # of the resulting list is a space separated list of the source lists' nth
1926
+ # values.
1927
+ #
1928
+ # The length of the resulting list is the length of the
1929
+ # shortest list.
1930
+ #
1931
+ # @example
1932
+ # zip(1px 1px 3px, solid dashed solid, red green blue)
1933
+ # => 1px solid red, 1px dashed green, 3px solid blue
1934
+ # @overload zip($lists...)
1935
+ # @param $lists [[Sass::Script::Value::Base]]
1936
+ # @return [Sass::Script::Value::List]
1937
+ def zip(*lists)
1938
+ length = nil
1939
+ values = []
1940
+ lists.each do |list|
1941
+ array = list.to_a
1942
+ values << array.dup
1943
+ length = length.nil? ? array.length : [length, array.length].min
1944
+ end
1945
+ values.each do |value|
1946
+ value.slice!(length)
1947
+ end
1948
+ new_list_value = values.first.zip(*values[1..-1])
1949
+ list(new_list_value.map {|list| list(list, :space)}, :comma)
1950
+ end
1951
+ declare :zip, [], :var_args => true
1952
+
1953
+ # Returns the position of a value within a list. If the value isn't found,
1954
+ # returns `null` instead.
1955
+ #
1956
+ # Note that unlike some languages, the first item in a Sass list is number
1957
+ # 1, the second number 2, and so forth.
1958
+ #
1959
+ # This can return the position of a pair in a map as well.
1960
+ #
1961
+ # @example
1962
+ # index(1px solid red, solid) => 2
1963
+ # index(1px solid red, dashed) => null
1964
+ # index((width: 10px, height: 20px), (height 20px)) => 2
1965
+ # @overload index($list, $value)
1966
+ # @param $list [Sass::Script::Value::Base]
1967
+ # @param $value [Sass::Script::Value::Base]
1968
+ # @return [Sass::Script::Value::Number, Sass::Script::Value::Null] The
1969
+ # 1-based index of `$value` in `$list`, or `null`
1970
+ def index(list, value)
1971
+ index = list.to_a.index {|e| e.eq(value).to_bool}
1972
+ index ? number(index + 1) : null
1973
+ end
1974
+ declare :index, [:list, :value]
1975
+
1976
+ # Returns the separator of a list. If the list doesn't have a separator due
1977
+ # to having fewer than two elements, returns `space`.
1978
+ #
1979
+ # @example
1980
+ # list-separator(1px 2px 3px) => space
1981
+ # list-separator(1px, 2px, 3px) => comma
1982
+ # list-separator('foo') => space
1983
+ # @overload list_separator($list)
1984
+ # @param $list [Sass::Script::Value::Base]
1985
+ # @return [Sass::Script::Value::String] `comma` or `space`
1986
+ def list_separator(list)
1987
+ identifier((list.separator || :space).to_s)
1988
+ end
1989
+ declare :separator, [:list]
1990
+
1991
+ # Returns the value in a map associated with the given key. If the map
1992
+ # doesn't have such a key, returns `null`.
1993
+ #
1994
+ # @example
1995
+ # map-get(("foo": 1, "bar": 2), "foo") => 1
1996
+ # map-get(("foo": 1, "bar": 2), "bar") => 2
1997
+ # map-get(("foo": 1, "bar": 2), "baz") => null
1998
+ # @overload map_get($map, $key)
1999
+ # @param $map [Sass::Script::Value::Map]
2000
+ # @param $key [Sass::Script::Value::Base]
2001
+ # @return [Sass::Script::Value::Base] The value indexed by `$key`, or `null`
2002
+ # if the map doesn't contain the given key
2003
+ # @raise [ArgumentError] if `$map` is not a map
2004
+ def map_get(map, key)
2005
+ assert_type map, :Map, :map
2006
+ map.to_h[key] || null
2007
+ end
2008
+ declare :map_get, [:map, :key]
2009
+
2010
+ # Merges two maps together into a new map. Keys in `$map2` will take
2011
+ # precedence over keys in `$map1`.
2012
+ #
2013
+ # This is the best way to add new values to a map.
2014
+ #
2015
+ # All keys in the returned map that also appear in `$map1` will have the
2016
+ # same order as in `$map1`. New keys from `$map2` will be placed at the end
2017
+ # of the map.
2018
+ #
2019
+ # @example
2020
+ # map-merge(("foo": 1), ("bar": 2)) => ("foo": 1, "bar": 2)
2021
+ # map-merge(("foo": 1, "bar": 2), ("bar": 3)) => ("foo": 1, "bar": 3)
2022
+ # @overload map_merge($map1, $map2)
2023
+ # @param $map1 [Sass::Script::Value::Map]
2024
+ # @param $map2 [Sass::Script::Value::Map]
2025
+ # @return [Sass::Script::Value::Map]
2026
+ # @raise [ArgumentError] if either parameter is not a map
2027
+ def map_merge(map1, map2)
2028
+ assert_type map1, :Map, :map1
2029
+ assert_type map2, :Map, :map2
2030
+ map(map1.to_h.merge(map2.to_h))
2031
+ end
2032
+ declare :map_merge, [:map1, :map2]
2033
+
2034
+ # Returns a new map with keys removed.
2035
+ #
2036
+ # @example
2037
+ # map-remove(("foo": 1, "bar": 2), "bar") => ("foo": 1)
2038
+ # map-remove(("foo": 1, "bar": 2, "baz": 3), "bar", "baz") => ("foo": 1)
2039
+ # map-remove(("foo": 1, "bar": 2), "baz") => ("foo": 1, "bar": 2)
2040
+ # @overload map_remove($map, $keys...)
2041
+ # @param $map [Sass::Script::Value::Map]
2042
+ # @param $keys [[Sass::Script::Value::Base]]
2043
+ # @return [Sass::Script::Value::Map]
2044
+ # @raise [ArgumentError] if `$map` is not a map
2045
+ def map_remove(map, *keys)
2046
+ assert_type map, :Map, :map
2047
+ hash = map.to_h.dup
2048
+ hash.delete_if {|key, _| keys.include?(key)}
2049
+ map(hash)
2050
+ end
2051
+ declare :map_remove, [:map, :key], :var_args => true
2052
+
2053
+ # Returns a list of all keys in a map.
2054
+ #
2055
+ # @example
2056
+ # map-keys(("foo": 1, "bar": 2)) => "foo", "bar"
2057
+ # @overload map_keys($map)
2058
+ # @param $map [Map]
2059
+ # @return [List] the list of keys, comma-separated
2060
+ # @raise [ArgumentError] if `$map` is not a map
2061
+ def map_keys(map)
2062
+ assert_type map, :Map, :map
2063
+ list(map.to_h.keys, :comma)
2064
+ end
2065
+ declare :map_keys, [:map]
2066
+
2067
+ # Returns a list of all values in a map. This list may include duplicate
2068
+ # values, if multiple keys have the same value.
2069
+ #
2070
+ # @example
2071
+ # map-values(("foo": 1, "bar": 2)) => 1, 2
2072
+ # map-values(("foo": 1, "bar": 2, "baz": 1)) => 1, 2, 1
2073
+ # @overload map_values($map)
2074
+ # @param $map [Map]
2075
+ # @return [List] the list of values, comma-separated
2076
+ # @raise [ArgumentError] if `$map` is not a map
2077
+ def map_values(map)
2078
+ assert_type map, :Map, :map
2079
+ list(map.to_h.values, :comma)
2080
+ end
2081
+ declare :map_values, [:map]
2082
+
2083
+ # Returns whether a map has a value associated with a given key.
2084
+ #
2085
+ # @example
2086
+ # map-has-key(("foo": 1, "bar": 2), "foo") => true
2087
+ # map-has-key(("foo": 1, "bar": 2), "baz") => false
2088
+ # @overload map_has_key($map, $key)
2089
+ # @param $map [Sass::Script::Value::Map]
2090
+ # @param $key [Sass::Script::Value::Base]
2091
+ # @return [Sass::Script::Value::Bool]
2092
+ # @raise [ArgumentError] if `$map` is not a map
2093
+ def map_has_key(map, key)
2094
+ assert_type map, :Map, :map
2095
+ bool(map.to_h.has_key?(key))
2096
+ end
2097
+ declare :map_has_key, [:map, :key]
2098
+
2099
+ # Returns the map of named arguments passed to a function or mixin that
2100
+ # takes a variable argument list. The argument names are strings, and they
2101
+ # do not contain the leading `$`.
2102
+ #
2103
+ # @example
2104
+ # @mixin foo($args...) {
2105
+ # @debug keywords($args); //=> (arg1: val, arg2: val)
2106
+ # }
2107
+ #
2108
+ # @include foo($arg1: val, $arg2: val);
2109
+ # @overload keywords($args)
2110
+ # @param $args [Sass::Script::Value::ArgList]
2111
+ # @return [Sass::Script::Value::Map]
2112
+ # @raise [ArgumentError] if `$args` isn't a variable argument list
2113
+ def keywords(args)
2114
+ assert_type args, :ArgList, :args
2115
+ map(Sass::Util.map_keys(args.keywords.as_stored) {|k| Sass::Script::Value::String.new(k)})
2116
+ end
2117
+ declare :keywords, [:args]
2118
+
2119
+ # Returns one of two values, depending on whether or not `$condition` is
2120
+ # true. Just like in `@if`, all values other than `false` and `null` are
2121
+ # considered to be true.
2122
+ #
2123
+ # @example
2124
+ # if(true, 1px, 2px) => 1px
2125
+ # if(false, 1px, 2px) => 2px
2126
+ # @overload if($condition, $if-true, $if-false)
2127
+ # @param $condition [Sass::Script::Value::Base] Whether the `$if-true` or
2128
+ # `$if-false` will be returned
2129
+ # @param $if-true [Sass::Script::Tree::Node]
2130
+ # @param $if-false [Sass::Script::Tree::Node]
2131
+ # @return [Sass::Script::Value::Base] `$if-true` or `$if-false`
2132
+ def if(condition, if_true, if_false)
2133
+ if condition.to_bool
2134
+ perform(if_true)
2135
+ else
2136
+ perform(if_false)
2137
+ end
2138
+ end
2139
+ declare :if, [:condition, :"&if_true", :"&if_false"]
2140
+
2141
+ # Returns a unique CSS identifier. The identifier is returned as an unquoted
2142
+ # string. The identifier returned is only guaranteed to be unique within the
2143
+ # scope of a single Sass run.
2144
+ #
2145
+ # @overload unique_id()
2146
+ # @return [Sass::Script::Value::String]
2147
+ def unique_id
2148
+ generator = Sass::Script::Functions.random_number_generator
2149
+ Thread.current[:sass_last_unique_id] ||= generator.rand(36**8)
2150
+ # avoid the temptation of trying to guess the next unique value.
2151
+ value = (Thread.current[:sass_last_unique_id] += (generator.rand(10) + 1))
2152
+ # the u makes this a legal identifier if it would otherwise start with a number.
2153
+ identifier("u" + value.to_s(36).rjust(8, '0'))
2154
+ end
2155
+ declare :unique_id, []
2156
+
2157
+ # Dynamically calls a function. This can call user-defined
2158
+ # functions, built-in functions, or plain CSS functions. It will
2159
+ # pass along all arguments, including keyword arguments, to the
2160
+ # called function.
2161
+ #
2162
+ # @example
2163
+ # call(rgb, 10, 100, 255) => #0a64ff
2164
+ # call(scale-color, #0a64ff, $lightness: -10%) => #0058ef
2165
+ #
2166
+ # $fn: nth;
2167
+ # call($fn, (a b c), 2) => b
2168
+ #
2169
+ # @overload call($name, $args...)
2170
+ # @param $name [String] The name of the function to call.
2171
+ def call(name, *args)
2172
+ assert_type name, :String, :name
2173
+ kwargs = args.last.is_a?(Hash) ? args.pop : {}
2174
+ funcall = Sass::Script::Tree::Funcall.new(
2175
+ name.value,
2176
+ args.map {|a| Sass::Script::Tree::Literal.new(a)},
2177
+ Sass::Util.map_vals(kwargs) {|v| Sass::Script::Tree::Literal.new(v)},
2178
+ nil,
2179
+ nil)
2180
+ funcall.options = options
2181
+ perform(funcall)
2182
+ end
2183
+ declare :call, [:name], :var_args => true, :var_kwargs => true
2184
+
2185
+ # This function only exists as a workaround for IE7's [`content:
2186
+ # counter` bug](http://jes.st/2013/ie7s-css-breaking-content-counter-bug/).
2187
+ # It works identically to any other plain-CSS function, except it
2188
+ # avoids adding spaces between the argument commas.
2189
+ #
2190
+ # @example
2191
+ # counter(item, ".") => counter(item,".")
2192
+ # @overload counter($args...)
2193
+ # @return [Sass::Script::Value::String]
2194
+ def counter(*args)
2195
+ identifier("counter(#{args.map {|a| a.to_s(options)}.join(',')})")
2196
+ end
2197
+ declare :counter, [], :var_args => true
2198
+
2199
+ # This function only exists as a workaround for IE7's [`content:
2200
+ # counter` bug](http://jes.st/2013/ie7s-css-breaking-content-counter-bug/).
2201
+ # It works identically to any other plain-CSS function, except it
2202
+ # avoids adding spaces between the argument commas.
2203
+ #
2204
+ # @example
2205
+ # counters(item, ".") => counters(item,".")
2206
+ # @overload counters($args...)
2207
+ # @return [Sass::Script::Value::String]
2208
+ def counters(*args)
2209
+ identifier("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
2210
+ end
2211
+ declare :counters, [], :var_args => true
2212
+
2213
+ # Check whether a variable with the given name exists in the current
2214
+ # scope or in the global scope.
2215
+ #
2216
+ # @example
2217
+ # $a-false-value: false;
2218
+ # variable-exists(a-false-value) => true
2219
+ #
2220
+ # variable-exists(nonexistent) => false
2221
+ #
2222
+ # @overload variable_exists($name)
2223
+ # @param $name [Sass::Script::Value::String] The name of the variable to
2224
+ # check. The name should not include the `$`.
2225
+ # @return [Sass::Script::Value::Bool] Whether the variable is defined in
2226
+ # the current scope.
2227
+ def variable_exists(name)
2228
+ assert_type name, :String, :name
2229
+ bool(environment.caller.var(name.value))
2230
+ end
2231
+ declare :variable_exists, [:name]
2232
+
2233
+ # Check whether a variable with the given name exists in the global
2234
+ # scope (at the top level of the file).
2235
+ #
2236
+ # @example
2237
+ # $a-false-value: false;
2238
+ # global-variable-exists(a-false-value) => true
2239
+ #
2240
+ # .foo {
2241
+ # $some-var: false;
2242
+ # @if global-variable-exists(some-var) { /* false, doesn't run */ }
2243
+ # }
2244
+ #
2245
+ # @overload global_variable_exists($name)
2246
+ # @param $name [Sass::Script::Value::String] The name of the variable to
2247
+ # check. The name should not include the `$`.
2248
+ # @return [Sass::Script::Value::Bool] Whether the variable is defined in
2249
+ # the global scope.
2250
+ def global_variable_exists(name)
2251
+ assert_type name, :String, :name
2252
+ bool(environment.global_env.var(name.value))
2253
+ end
2254
+ declare :global_variable_exists, [:name]
2255
+
2256
+ # Check whether a function with the given name exists.
2257
+ #
2258
+ # @example
2259
+ # function-exists(lighten) => true
2260
+ #
2261
+ # @function myfunc { @return "something"; }
2262
+ # function-exists(myfunc) => true
2263
+ #
2264
+ # @overload function_exists($name)
2265
+ # @param name [Sass::Script::Value::String] The name of the function to
2266
+ # check.
2267
+ # @return [Sass::Script::Value::Bool] Whether the function is defined.
2268
+ def function_exists(name)
2269
+ assert_type name, :String, :name
2270
+ exists = Sass::Script::Functions.callable?(name.value.tr("-", "_"))
2271
+ exists ||= environment.function(name.value)
2272
+ bool(exists)
2273
+ end
2274
+ declare :function_exists, [:name]
2275
+
2276
+ # Check whether a mixin with the given name exists.
2277
+ #
2278
+ # @example
2279
+ # mixin-exists(nonexistent) => false
2280
+ #
2281
+ # @mixin red-text { color: red; }
2282
+ # mixin-exists(red-text) => true
2283
+ #
2284
+ # @overload mixin_exists($name)
2285
+ # @param name [Sass::Script::Value::String] The name of the mixin to
2286
+ # check.
2287
+ # @return [Sass::Script::Value::Bool] Whether the mixin is defined.
2288
+ def mixin_exists(name)
2289
+ assert_type name, :String, :name
2290
+ bool(environment.mixin(name.value))
2291
+ end
2292
+ declare :mixin_exists, [:name]
2293
+
2294
+ # Return a string containing the value as its Sass representation.
2295
+ #
2296
+ # @overload inspect($value)
2297
+ # @param $value [Sass::Script::Value::Base] The value to inspect.
2298
+ # @return [Sass::Script::Value::String] A representation of the value as
2299
+ # it would be written in Sass.
2300
+ def inspect(value)
2301
+ unquoted_string(value.to_sass)
2302
+ end
2303
+ declare :inspect, [:value]
2304
+
2305
+ # @overload random()
2306
+ # Return a decimal between 0 and 1, inclusive of 0 but not 1.
2307
+ # @return [Sass::Script::Value::Number] A decimal value.
2308
+ # @overload random($limit)
2309
+ # Return an integer between 1 and `$limit`, inclusive of 1 but not `$limit`.
2310
+ # @param $limit [Sass::Script::Value::Number] The maximum of the random integer to be
2311
+ # returned, a positive integer.
2312
+ # @return [Sass::Script::Value::Number] An integer.
2313
+ # @raise [ArgumentError] if the `$limit` is not 1 or greater
2314
+ def random(limit = nil)
2315
+ generator = Sass::Script::Functions.random_number_generator
2316
+ if limit
2317
+ assert_integer limit, "limit"
2318
+ if limit.value < 1
2319
+ raise ArgumentError.new("$limit #{limit} must be greater than or equal to 1")
2320
+ end
2321
+ number(1 + generator.rand(limit.value))
2322
+ else
2323
+ number(generator.rand)
2324
+ end
2325
+ end
2326
+ declare :random, []
2327
+ declare :random, [:limit]
2328
+
2329
+ # Parses a user-provided selector into a list of lists of strings
2330
+ # as returned by `&`.
2331
+ #
2332
+ # @example
2333
+ # selector-parse(".foo .bar, .baz .bang") => ('.foo' '.bar', '.baz' '.bang')
2334
+ #
2335
+ # @overload selector_parse($selector)
2336
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2337
+ # The selector to parse. This can be either a string, a list of
2338
+ # strings, or a list of lists of strings as returned by `&`.
2339
+ # @return [Sass::Script::Value::List]
2340
+ # A list of lists of strings representing `$selector`. This is
2341
+ # in the same format as a selector returned by `&`.
2342
+ def selector_parse(selector)
2343
+ parse_selector(selector, :selector).to_sass_script
2344
+ end
2345
+ declare :selector_parse, [:selector]
2346
+
2347
+ # Return a new selector with all selectors in `$selectors` nested beneath
2348
+ # one another as though they had been nested in the stylesheet as
2349
+ # `$selector1 { $selector2 { ... } }`.
2350
+ #
2351
+ # Unlike most selector functions, `selector-nest` allows the
2352
+ # parent selector `&` to be used in any selector but the first.
2353
+ #
2354
+ # @example
2355
+ # selector-nest(".foo", ".bar", ".baz") => .foo .bar .baz
2356
+ # selector-nest(".a .foo", ".b .bar") => .a .foo .b .bar
2357
+ # selector-nest(".foo", "&.bar") => .foo.bar
2358
+ #
2359
+ # @overload selector_nest($selectors...)
2360
+ # @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
2361
+ # The selectors to nest. At least one selector must be passed. Each of
2362
+ # these can be either a string, a list of strings, or a list of lists of
2363
+ # strings as returned by `&`.
2364
+ # @return [Sass::Script::Value::List]
2365
+ # A list of lists of strings representing the result of nesting
2366
+ # `$selectors`. This is in the same format as a selector returned by
2367
+ # `&`.
2368
+ def selector_nest(*selectors)
2369
+ if selectors.empty?
2370
+ raise ArgumentError.new("$selectors: At least one selector must be passed")
2371
+ end
2372
+
2373
+ parsed = [parse_selector(selectors.first, :selectors)]
2374
+ parsed += selectors[1..-1].map {|sel| parse_selector(sel, :selectors, !!:parse_parent_ref)}
2375
+ parsed.inject {|result, child| child.resolve_parent_refs(result)}.to_sass_script
2376
+ end
2377
+ declare :selector_nest, [], :var_args => true
2378
+
2379
+ # Return a new selector with all selectors in `$selectors` appended one
2380
+ # another as though they had been nested in the stylesheet as `$selector1 {
2381
+ # &$selector2 { ... } }`.
2382
+ #
2383
+ # @example
2384
+ # selector-append(".foo", ".bar", ".baz") => .foo.bar.baz
2385
+ # selector-append(".a .foo", ".b .bar") => "a .foo.b .bar"
2386
+ # selector-append(".foo", "-suffix") => ".foo-suffix"
2387
+ #
2388
+ # @overload selector_append($selectors...)
2389
+ # @param $selectors [[Sass::Script::Value::String, Sass::Script::Value::List]]
2390
+ # The selectors to append. At least one selector must be passed. Each of
2391
+ # these can be either a string, a list of strings, or a list of lists of
2392
+ # strings as returned by `&`.
2393
+ # @return [Sass::Script::Value::List]
2394
+ # A list of lists of strings representing the result of appending
2395
+ # `$selectors`. This is in the same format as a selector returned by
2396
+ # `&`.
2397
+ # @raise [ArgumentError] if a selector could not be appended.
2398
+ def selector_append(*selectors)
2399
+ if selectors.empty?
2400
+ raise ArgumentError.new("$selectors: At least one selector must be passed")
2401
+ end
2402
+
2403
+ selectors.map {|sel| parse_selector(sel, :selectors)}.inject do |parent, child|
2404
+ child.members.each do |seq|
2405
+ sseq = seq.members.first
2406
+ unless sseq.is_a?(Sass::Selector::SimpleSequence)
2407
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2408
+ end
2409
+
2410
+ base = sseq.base
2411
+ case base
2412
+ when Sass::Selector::Universal
2413
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2414
+ when Sass::Selector::Element
2415
+ unless base.namespace.nil?
2416
+ raise ArgumentError.new("Can't append \"#{seq}\" to \"#{parent}\"")
2417
+ end
2418
+ sseq.members[0] = Sass::Selector::Parent.new(base.name)
2419
+ else
2420
+ sseq.members.unshift Sass::Selector::Parent.new
2421
+ end
2422
+ end
2423
+ child.resolve_parent_refs(parent)
2424
+ end.to_sass_script
2425
+ end
2426
+ declare :selector_append, [], :var_args => true
2427
+
2428
+ # Returns a new version of `$selector` with `$extendee` extended
2429
+ # with `$extender`. This works just like the result of
2430
+ #
2431
+ # $selector { ... }
2432
+ # $extender { @extend $extendee }
2433
+ #
2434
+ # @example
2435
+ # selector-extend(".a .b", ".b", ".foo .bar") => .a .b, .a .foo .bar, .foo .a .bar
2436
+ #
2437
+ # @overload selector_extend($selector, $extendee, $extender)
2438
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2439
+ # The selector within which `$extendee` is extended with
2440
+ # `$extender`. This can be either a string, a list of strings,
2441
+ # or a list of lists of strings as returned by `&`.
2442
+ # @param $extendee [Sass::Script::Value::String, Sass::Script::Value::List]
2443
+ # The selector being extended. This can be either a string, a
2444
+ # list of strings, or a list of lists of strings as returned
2445
+ # by `&`.
2446
+ # @param $extender [Sass::Script::Value::String, Sass::Script::Value::List]
2447
+ # The selector being injected into `$selector`. This can be
2448
+ # either a string, a list of strings, or a list of lists of
2449
+ # strings as returned by `&`.
2450
+ # @return [Sass::Script::Value::List]
2451
+ # A list of lists of strings representing the result of the
2452
+ # extension. This is in the same format as a selector returned
2453
+ # by `&`.
2454
+ # @raise [ArgumentError] if the extension fails
2455
+ def selector_extend(selector, extendee, extender)
2456
+ selector = parse_selector(selector, :selector)
2457
+ extendee = parse_selector(extendee, :extendee)
2458
+ extender = parse_selector(extender, :extender)
2459
+
2460
+ extends = Sass::Util::SubsetMap.new
2461
+ begin
2462
+ extender.populate_extends(extends, extendee)
2463
+ selector.do_extend(extends).to_sass_script
2464
+ rescue Sass::SyntaxError => e
2465
+ raise ArgumentError.new(e.to_s)
2466
+ end
2467
+ end
2468
+ declare :selector_extend, [:selector, :extendee, :extender]
2469
+
2470
+ # Replaces all instances of `$original` with `$replacement` in `$selector`
2471
+ #
2472
+ # This works by using `@extend` and throwing away the original
2473
+ # selector. This means that it can be used to do very advanced
2474
+ # replacements; see the examples below.
2475
+ #
2476
+ # @example
2477
+ # selector-replace(".foo .bar", ".bar", ".baz") => ".foo .baz"
2478
+ # selector-replace(".foo.bar.baz", ".foo.baz", ".qux") => ".bar.qux"
2479
+ #
2480
+ # @overload selector_replace($selector, $original, $replacement)
2481
+ # @param $selector [Sass::Script::Value::String, Sass::Script::Value::List]
2482
+ # The selector within which `$original` is replaced with
2483
+ # `$replacement`. This can be either a string, a list of
2484
+ # strings, or a list of lists of strings as returned by `&`.
2485
+ # @param $original [Sass::Script::Value::String, Sass::Script::Value::List]
2486
+ # The selector being replaced. This can be either a string, a
2487
+ # list of strings, or a list of lists of strings as returned
2488
+ # by `&`.
2489
+ # @param $replacement [Sass::Script::Value::String, Sass::Script::Value::List]
2490
+ # The selector that `$original` is being replaced with. This
2491
+ # can be either a string, a list of strings, or a list of
2492
+ # lists of strings as returned by `&`.
2493
+ # @return [Sass::Script::Value::List]
2494
+ # A list of lists of strings representing the result of the
2495
+ # extension. This is in the same format as a selector returned
2496
+ # by `&`.
2497
+ # @raise [ArgumentError] if the replacement fails
2498
+ def selector_replace(selector, original, replacement)
2499
+ selector = parse_selector(selector, :selector)
2500
+ original = parse_selector(original, :original)
2501
+ replacement = parse_selector(replacement, :replacement)
2502
+
2503
+ extends = Sass::Util::SubsetMap.new
2504
+ begin
2505
+ replacement.populate_extends(extends, original)
2506
+ selector.do_extend(extends, [], !!:replace).to_sass_script
2507
+ rescue Sass::SyntaxError => e
2508
+ raise ArgumentError.new(e.to_s)
2509
+ end
2510
+ end
2511
+ declare :selector_replace, [:selector, :original, :replacement]
2512
+
2513
+ # Unifies two selectors into a single selector that matches only
2514
+ # elements matched by both input selectors. Returns `null` if
2515
+ # there is no such selector.
2516
+ #
2517
+ # Like the selector unification done for `@extend`, this doesn't
2518
+ # guarantee that the output selector will match *all* elements
2519
+ # matched by both input selectors. For example, if `.a .b` is
2520
+ # unified with `.x .y`, `.a .x .b.y, .x .a .b.y` will be returned,
2521
+ # but `.a.x .b.y` will not. This avoids exponential output size
2522
+ # while matching all elements that are likely to exist in
2523
+ # practice.
2524
+ #
2525
+ # @example
2526
+ # selector-unify(".a", ".b") => .a.b
2527
+ # selector-unify(".a .b", ".x .y") => .a .x .b.y, .x .a .b.y
2528
+ # selector-unify(".a.b", ".b.c") => .a.b.c
2529
+ # selector-unify("#a", "#b") => null
2530
+ #
2531
+ # @overload selector_unify($selector1, $selector2)
2532
+ # @param $selector1 [Sass::Script::Value::String, Sass::Script::Value::List]
2533
+ # The first selector to be unified. This can be either a
2534
+ # string, a list of strings, or a list of lists of strings as
2535
+ # returned by `&`.
2536
+ # @param $selector2 [Sass::Script::Value::String, Sass::Script::Value::List]
2537
+ # The second selector to be unified. This can be either a
2538
+ # string, a list of strings, or a list of lists of strings as
2539
+ # returned by `&`.
2540
+ # @return [Sass::Script::Value::List, Sass::Script::Value::Null]
2541
+ # A list of lists of strings representing the result of the
2542
+ # unification, or null if no unification exists. This is in
2543
+ # the same format as a selector returned by `&`.
2544
+ def selector_unify(selector1, selector2)
2545
+ selector1 = parse_selector(selector1, :selector1)
2546
+ selector2 = parse_selector(selector2, :selector2)
2547
+ return null unless (unified = selector1.unify(selector2))
2548
+ unified.to_sass_script
2549
+ end
2550
+ declare :selector_unify, [:selector1, :selector2]
2551
+
2552
+ # Returns the [simple
2553
+ # selectors](http://dev.w3.org/csswg/selectors4/#simple) that
2554
+ # comprise the compound selector `$selector`.
2555
+ #
2556
+ # Note that `$selector` **must be** a [compound
2557
+ # selector](http://dev.w3.org/csswg/selectors4/#compound). That
2558
+ # means it cannot contain commas or spaces. It also means that
2559
+ # unlike other selector functions, this takes only strings, not
2560
+ # lists.
2561
+ #
2562
+ # @example
2563
+ # simple-selectors(".foo.bar") => ".foo", ".bar"
2564
+ # simple-selectors(".foo.bar.baz") => ".foo", ".bar", ".baz"
2565
+ #
2566
+ # @overload simple_selectors($selector)
2567
+ # @param $selector [Sass::Script::Value::String]
2568
+ # The compound selector whose simple selectors will be extracted.
2569
+ # @return [Sass::Script::Value::List]
2570
+ # A list of simple selectors in the compound selector.
2571
+ def simple_selectors(selector)
2572
+ selector = parse_compound_selector(selector, :selector)
2573
+ list(selector.members.map {|simple| unquoted_string(simple.to_s)}, :comma)
2574
+ end
2575
+ declare :simple_selectors, [:selector]
2576
+
2577
+ # Returns whether `$super` is a superselector of `$sub`. This means that
2578
+ # `$super` matches all the elements that `$sub` matches, as well as possibly
2579
+ # additional elements. In general, simpler selectors tend to be
2580
+ # superselectors of more complex oned.
2581
+ #
2582
+ # @example
2583
+ # is-superselector(".foo", ".foo.bar") => true
2584
+ # is-superselector(".foo.bar", ".foo") => false
2585
+ # is-superselector(".bar", ".foo .bar") => true
2586
+ # is-superselector(".foo .bar", ".bar") => false
2587
+ #
2588
+ # @overload is_superselector($super, $sub)
2589
+ # @param $super [Sass::Script::Value::String, Sass::Script::Value::List]
2590
+ # The potential superselector. This can be either a string, a list of
2591
+ # strings, or a list of lists of strings as returned by `&`.
2592
+ # @param $sub [Sass::Script::Value::String, Sass::Script::Value::List]
2593
+ # The potential subselector. This can be either a string, a list of
2594
+ # strings, or a list of lists of strings as returned by `&`.
2595
+ # @return [Sass::Script::Value::Bool]
2596
+ # Whether `$selector1` is a superselector of `$selector2`.
2597
+ def is_superselector(sup, sub)
2598
+ sup = parse_selector(sup, :super)
2599
+ sub = parse_selector(sub, :sub)
2600
+ bool(sup.superselector?(sub))
2601
+ end
2602
+ declare :is_superselector, [:super, :sub]
2603
+
2604
+ private
2605
+
2606
+ # This method implements the pattern of transforming a numeric value into
2607
+ # another numeric value with the same units.
2608
+ # It yields a number to a block to perform the operation and return a number
2609
+ def numeric_transformation(value)
2610
+ assert_type value, :Number, :value
2611
+ Sass::Script::Value::Number.new(
2612
+ yield(value.value), value.numerator_units, value.denominator_units)
2613
+ end
2614
+
2615
+ # @comment
2616
+ # rubocop:disable ParameterLists
2617
+ def _adjust(color, amount, attr, range, op, units = "")
2618
+ # rubocop:enable ParameterLists
2619
+ assert_type color, :Color, :color
2620
+ assert_type amount, :Number, :amount
2621
+ Sass::Util.check_range('Amount', range, amount, units)
2622
+
2623
+ color.with(attr => color.send(attr).send(op, amount.value))
2624
+ end
2625
+ end
2626
+ end