haml 3.1.0.alpha.14 → 3.1.0.alpha.17

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of haml might be problematic. Click here for more details.

Files changed (222) hide show
  1. data/EDGE_GEM_VERSION +1 -1
  2. data/VERSION +1 -1
  3. data/lib/haml.rb +3 -2
  4. data/lib/haml/exec.rb +0 -226
  5. data/lib/sass.rb +8 -0
  6. data/lib/sass/plugin.rb +8 -0
  7. data/lib/sass/rails2_shim.rb +9 -0
  8. data/lib/sass/rails3_shim.rb +16 -0
  9. data/vendor/sass/CONTRIBUTING +3 -0
  10. data/vendor/sass/MIT-LICENSE +20 -0
  11. data/vendor/sass/README.md +201 -0
  12. data/vendor/sass/Rakefile +363 -0
  13. data/vendor/sass/TODO +39 -0
  14. data/vendor/sass/VERSION +1 -0
  15. data/vendor/sass/VERSION_NAME +1 -0
  16. data/vendor/sass/bin/css2sass +13 -0
  17. data/vendor/sass/bin/sass +8 -0
  18. data/vendor/sass/bin/sass-convert +7 -0
  19. data/vendor/sass/doc-src/FAQ.md +35 -0
  20. data/vendor/sass/doc-src/INDENTED_SYNTAX.md +210 -0
  21. data/vendor/sass/doc-src/SASS_CHANGELOG.md +1878 -0
  22. data/vendor/sass/doc-src/SASS_REFERENCE.md +1713 -0
  23. data/vendor/sass/doc-src/SCSS_FOR_SASS_USERS.md +155 -0
  24. data/vendor/sass/ext/extconf.rb +10 -0
  25. data/vendor/sass/extra/update_watch.rb +13 -0
  26. data/vendor/sass/init.rb +18 -0
  27. data/vendor/sass/lib/sass.rb +71 -0
  28. data/vendor/sass/lib/sass/cache_store.rb +208 -0
  29. data/vendor/sass/lib/sass/callbacks.rb +66 -0
  30. data/vendor/sass/lib/sass/css.rb +294 -0
  31. data/vendor/sass/lib/sass/engine.rb +792 -0
  32. data/vendor/sass/lib/sass/environment.rb +143 -0
  33. data/vendor/sass/lib/sass/error.rb +201 -0
  34. data/vendor/sass/lib/sass/exec.rb +619 -0
  35. data/vendor/sass/lib/sass/importers.rb +22 -0
  36. data/vendor/sass/lib/sass/importers/base.rb +138 -0
  37. data/vendor/sass/lib/sass/importers/filesystem.rb +121 -0
  38. data/vendor/sass/lib/sass/less.rb +363 -0
  39. data/vendor/sass/lib/sass/plugin.rb +126 -0
  40. data/vendor/sass/lib/sass/plugin/compiler.rb +346 -0
  41. data/vendor/sass/lib/sass/plugin/configuration.rb +123 -0
  42. data/vendor/sass/lib/sass/plugin/generic.rb +15 -0
  43. data/vendor/sass/lib/sass/plugin/merb.rb +48 -0
  44. data/vendor/sass/lib/sass/plugin/rack.rb +47 -0
  45. data/vendor/sass/lib/sass/plugin/rails.rb +41 -0
  46. data/vendor/sass/lib/sass/plugin/staleness_checker.rb +145 -0
  47. data/vendor/sass/lib/sass/railtie.rb +8 -0
  48. data/vendor/sass/lib/sass/repl.rb +58 -0
  49. data/vendor/sass/lib/sass/root.rb +7 -0
  50. data/vendor/sass/lib/sass/script.rb +63 -0
  51. data/vendor/sass/lib/sass/script/bool.rb +18 -0
  52. data/vendor/sass/lib/sass/script/color.rb +491 -0
  53. data/vendor/sass/lib/sass/script/css_lexer.rb +29 -0
  54. data/vendor/sass/lib/sass/script/css_parser.rb +31 -0
  55. data/vendor/sass/lib/sass/script/funcall.rb +76 -0
  56. data/vendor/sass/lib/sass/script/functions.rb +852 -0
  57. data/vendor/sass/lib/sass/script/interpolation.rb +70 -0
  58. data/vendor/sass/lib/sass/script/lexer.rb +337 -0
  59. data/vendor/sass/lib/sass/script/literal.rb +236 -0
  60. data/vendor/sass/lib/sass/script/node.rb +112 -0
  61. data/vendor/sass/lib/sass/script/number.rb +423 -0
  62. data/vendor/sass/lib/sass/script/operation.rb +90 -0
  63. data/vendor/sass/lib/sass/script/parser.rb +392 -0
  64. data/vendor/sass/lib/sass/script/string.rb +67 -0
  65. data/vendor/sass/lib/sass/script/string_interpolation.rb +93 -0
  66. data/vendor/sass/lib/sass/script/unary_operation.rb +57 -0
  67. data/vendor/sass/lib/sass/script/variable.rb +48 -0
  68. data/vendor/sass/lib/sass/scss.rb +17 -0
  69. data/vendor/sass/lib/sass/scss/css_parser.rb +51 -0
  70. data/vendor/sass/lib/sass/scss/parser.rb +838 -0
  71. data/vendor/sass/lib/sass/scss/rx.rb +126 -0
  72. data/vendor/sass/lib/sass/scss/sass_parser.rb +11 -0
  73. data/vendor/sass/lib/sass/scss/script_lexer.rb +15 -0
  74. data/vendor/sass/lib/sass/scss/script_parser.rb +25 -0
  75. data/vendor/sass/lib/sass/scss/static_parser.rb +40 -0
  76. data/vendor/sass/lib/sass/selector.rb +361 -0
  77. data/vendor/sass/lib/sass/selector/abstract_sequence.rb +62 -0
  78. data/vendor/sass/lib/sass/selector/comma_sequence.rb +82 -0
  79. data/vendor/sass/lib/sass/selector/sequence.rb +236 -0
  80. data/vendor/sass/lib/sass/selector/simple.rb +113 -0
  81. data/vendor/sass/lib/sass/selector/simple_sequence.rb +135 -0
  82. data/vendor/sass/lib/sass/shared.rb +78 -0
  83. data/vendor/sass/lib/sass/tree/comment_node.rb +128 -0
  84. data/vendor/sass/lib/sass/tree/debug_node.rb +36 -0
  85. data/vendor/sass/lib/sass/tree/directive_node.rb +75 -0
  86. data/vendor/sass/lib/sass/tree/extend_node.rb +65 -0
  87. data/vendor/sass/lib/sass/tree/for_node.rb +67 -0
  88. data/vendor/sass/lib/sass/tree/if_node.rb +81 -0
  89. data/vendor/sass/lib/sass/tree/import_node.rb +124 -0
  90. data/vendor/sass/lib/sass/tree/mixin_def_node.rb +60 -0
  91. data/vendor/sass/lib/sass/tree/mixin_node.rb +123 -0
  92. data/vendor/sass/lib/sass/tree/node.rb +490 -0
  93. data/vendor/sass/lib/sass/tree/prop_node.rb +220 -0
  94. data/vendor/sass/lib/sass/tree/root_node.rb +125 -0
  95. data/vendor/sass/lib/sass/tree/rule_node.rb +273 -0
  96. data/vendor/sass/lib/sass/tree/variable_node.rb +39 -0
  97. data/vendor/sass/lib/sass/tree/warn_node.rb +42 -0
  98. data/vendor/sass/lib/sass/tree/while_node.rb +48 -0
  99. data/vendor/sass/lib/sass/util.rb +700 -0
  100. data/vendor/sass/lib/sass/util/subset_map.rb +101 -0
  101. data/vendor/sass/lib/sass/version.rb +109 -0
  102. data/vendor/sass/rails/init.rb +1 -0
  103. data/vendor/sass/sass.gemspec +32 -0
  104. data/vendor/sass/test/sass/cache_test.rb +74 -0
  105. data/vendor/sass/test/sass/callbacks_test.rb +61 -0
  106. data/vendor/sass/test/sass/conversion_test.rb +1210 -0
  107. data/vendor/sass/test/sass/css2sass_test.rb +364 -0
  108. data/vendor/sass/test/sass/data/hsl-rgb.txt +319 -0
  109. data/vendor/sass/test/sass/engine_test.rb +2305 -0
  110. data/vendor/sass/test/sass/extend_test.rb +1348 -0
  111. data/vendor/sass/test/sass/functions_test.rb +565 -0
  112. data/vendor/sass/test/sass/importer_test.rb +104 -0
  113. data/vendor/sass/test/sass/less_conversion_test.rb +632 -0
  114. data/vendor/sass/test/sass/mock_importer.rb +49 -0
  115. data/vendor/sass/test/sass/more_results/more1.css +9 -0
  116. data/vendor/sass/test/sass/more_results/more1_with_line_comments.css +26 -0
  117. data/vendor/sass/test/sass/more_results/more_import.css +29 -0
  118. data/vendor/sass/test/sass/more_templates/_more_partial.sass +2 -0
  119. data/vendor/sass/test/sass/more_templates/more1.sass +23 -0
  120. data/vendor/sass/test/sass/more_templates/more_import.sass +11 -0
  121. data/vendor/sass/test/sass/plugin_test.rb +430 -0
  122. data/vendor/sass/test/sass/results/alt.css +4 -0
  123. data/vendor/sass/test/sass/results/basic.css +9 -0
  124. data/vendor/sass/test/sass/results/compact.css +5 -0
  125. data/vendor/sass/test/sass/results/complex.css +86 -0
  126. data/vendor/sass/test/sass/results/compressed.css +1 -0
  127. data/vendor/sass/test/sass/results/expanded.css +19 -0
  128. data/vendor/sass/test/sass/results/import.css +31 -0
  129. data/vendor/sass/test/sass/results/line_numbers.css +49 -0
  130. data/vendor/sass/test/sass/results/mixins.css +95 -0
  131. data/vendor/sass/test/sass/results/multiline.css +24 -0
  132. data/vendor/sass/test/sass/results/nested.css +22 -0
  133. data/vendor/sass/test/sass/results/options.css +1 -0
  134. data/vendor/sass/test/sass/results/parent_ref.css +13 -0
  135. data/vendor/sass/test/sass/results/script.css +16 -0
  136. data/vendor/sass/test/sass/results/scss_import.css +31 -0
  137. data/vendor/sass/test/sass/results/scss_importee.css +2 -0
  138. data/vendor/sass/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  139. data/vendor/sass/test/sass/results/subdir/subdir.css +3 -0
  140. data/vendor/sass/test/sass/results/units.css +11 -0
  141. data/vendor/sass/test/sass/results/warn.css +0 -0
  142. data/vendor/sass/test/sass/results/warn_imported.css +0 -0
  143. data/vendor/sass/test/sass/script_conversion_test.rb +254 -0
  144. data/vendor/sass/test/sass/script_test.rb +470 -0
  145. data/vendor/sass/test/sass/scss/css_test.rb +897 -0
  146. data/vendor/sass/test/sass/scss/rx_test.rb +156 -0
  147. data/vendor/sass/test/sass/scss/scss_test.rb +1088 -0
  148. data/vendor/sass/test/sass/scss/test_helper.rb +37 -0
  149. data/vendor/sass/test/sass/templates/_partial.sass +2 -0
  150. data/vendor/sass/test/sass/templates/alt.sass +16 -0
  151. data/vendor/sass/test/sass/templates/basic.sass +23 -0
  152. data/vendor/sass/test/sass/templates/bork1.sass +2 -0
  153. data/vendor/sass/test/sass/templates/bork2.sass +2 -0
  154. data/vendor/sass/test/sass/templates/bork3.sass +2 -0
  155. data/vendor/sass/test/sass/templates/bork4.sass +2 -0
  156. data/vendor/sass/test/sass/templates/compact.sass +17 -0
  157. data/vendor/sass/test/sass/templates/complex.sass +305 -0
  158. data/vendor/sass/test/sass/templates/compressed.sass +15 -0
  159. data/vendor/sass/test/sass/templates/expanded.sass +17 -0
  160. data/vendor/sass/test/sass/templates/import.sass +12 -0
  161. data/vendor/sass/test/sass/templates/importee.less +2 -0
  162. data/vendor/sass/test/sass/templates/importee.sass +19 -0
  163. data/vendor/sass/test/sass/templates/line_numbers.sass +13 -0
  164. data/vendor/sass/test/sass/templates/mixin_bork.sass +5 -0
  165. data/vendor/sass/test/sass/templates/mixins.sass +76 -0
  166. data/vendor/sass/test/sass/templates/multiline.sass +20 -0
  167. data/vendor/sass/test/sass/templates/nested.sass +25 -0
  168. data/vendor/sass/test/sass/templates/nested_bork1.sass +2 -0
  169. data/vendor/sass/test/sass/templates/nested_bork2.sass +2 -0
  170. data/vendor/sass/test/sass/templates/nested_bork3.sass +2 -0
  171. data/vendor/sass/test/sass/templates/nested_bork4.sass +2 -0
  172. data/vendor/sass/test/sass/templates/nested_mixin_bork.sass +6 -0
  173. data/vendor/sass/test/sass/templates/options.sass +2 -0
  174. data/vendor/sass/test/sass/templates/parent_ref.sass +25 -0
  175. data/vendor/sass/test/sass/templates/script.sass +101 -0
  176. data/vendor/sass/test/sass/templates/scss_import.scss +11 -0
  177. data/vendor/sass/test/sass/templates/scss_importee.scss +1 -0
  178. data/vendor/sass/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  179. data/vendor/sass/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  180. data/vendor/sass/test/sass/templates/subdir/subdir.sass +6 -0
  181. data/vendor/sass/test/sass/templates/units.sass +11 -0
  182. data/vendor/sass/test/sass/templates/warn.sass +3 -0
  183. data/vendor/sass/test/sass/templates/warn_imported.sass +4 -0
  184. data/vendor/sass/test/sass/test_helper.rb +8 -0
  185. data/vendor/sass/test/sass/util/subset_map_test.rb +91 -0
  186. data/vendor/sass/test/sass/util_test.rb +275 -0
  187. data/vendor/sass/test/test_helper.rb +64 -0
  188. data/vendor/sass/vendor/fssm/LICENSE +20 -0
  189. data/vendor/sass/vendor/fssm/README.markdown +55 -0
  190. data/vendor/sass/vendor/fssm/Rakefile +59 -0
  191. data/vendor/sass/vendor/fssm/VERSION.yml +5 -0
  192. data/vendor/sass/vendor/fssm/example.rb +9 -0
  193. data/vendor/sass/vendor/fssm/fssm.gemspec +77 -0
  194. data/vendor/sass/vendor/fssm/lib/fssm.rb +33 -0
  195. data/vendor/sass/vendor/fssm/lib/fssm/backends/fsevents.rb +36 -0
  196. data/vendor/sass/vendor/fssm/lib/fssm/backends/inotify.rb +26 -0
  197. data/vendor/sass/vendor/fssm/lib/fssm/backends/polling.rb +25 -0
  198. data/vendor/sass/vendor/fssm/lib/fssm/backends/rubycocoa/fsevents.rb +131 -0
  199. data/vendor/sass/vendor/fssm/lib/fssm/monitor.rb +26 -0
  200. data/vendor/sass/vendor/fssm/lib/fssm/path.rb +91 -0
  201. data/vendor/sass/vendor/fssm/lib/fssm/pathname.rb +502 -0
  202. data/vendor/sass/vendor/fssm/lib/fssm/state/directory.rb +57 -0
  203. data/vendor/sass/vendor/fssm/lib/fssm/state/file.rb +24 -0
  204. data/vendor/sass/vendor/fssm/lib/fssm/support.rb +63 -0
  205. data/vendor/sass/vendor/fssm/lib/fssm/tree.rb +176 -0
  206. data/vendor/sass/vendor/fssm/profile/prof-cache.rb +40 -0
  207. data/vendor/sass/vendor/fssm/profile/prof-fssm-pathname.html +1231 -0
  208. data/vendor/sass/vendor/fssm/profile/prof-pathname.rb +68 -0
  209. data/vendor/sass/vendor/fssm/profile/prof-plain-pathname.html +988 -0
  210. data/vendor/sass/vendor/fssm/profile/prof.html +2379 -0
  211. data/vendor/sass/vendor/fssm/spec/path_spec.rb +75 -0
  212. data/vendor/sass/vendor/fssm/spec/root/duck/quack.txt +0 -0
  213. data/vendor/sass/vendor/fssm/spec/root/file.css +0 -0
  214. data/vendor/sass/vendor/fssm/spec/root/file.rb +0 -0
  215. data/vendor/sass/vendor/fssm/spec/root/file.yml +0 -0
  216. data/vendor/sass/vendor/fssm/spec/root/moo/cow.txt +0 -0
  217. data/vendor/sass/vendor/fssm/spec/spec_helper.rb +14 -0
  218. data/vendor/sass/yard/callbacks.rb +29 -0
  219. data/vendor/sass/yard/default/fulldoc/html/css/common.sass +26 -0
  220. data/vendor/sass/yard/default/layout/html/footer.erb +12 -0
  221. data/vendor/sass/yard/inherited_hash.rb +41 -0
  222. metadata +219 -2
@@ -0,0 +1,29 @@
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
+ return unless uri = scan(URI)
16
+ return [:string, Script::String.new(uri)]
17
+ end
18
+
19
+ return unless scan(STRING)
20
+ [:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
21
+ end
22
+
23
+ def important
24
+ return unless s = scan(IMPORTANT)
25
+ [:raw, s]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
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
+ return number unless tok = try_tok(:string)
21
+ return tok.value unless @lexer.peek && @lexer.peek.type == :begin_interpolation
22
+ end
23
+
24
+ # Short-circuit all the SassScript-only productions
25
+ alias_method :interpolation, :concat
26
+ alias_method :or_expr, :div
27
+ alias_method :unary_div, :ident
28
+ alias_method :paren, :string
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,76 @@
1
+ require File.join(File.dirname(__FILE__), 'functions')
2
+ module Sass
3
+ module Script
4
+ # A SassScript parse node representing a function call.
5
+ #
6
+ # A function call either calls one of the functions in {Script::Functions},
7
+ # or if no function with the given name exists
8
+ # it returns a string representation of the function call.
9
+ class Funcall < Node
10
+ # The name of the function.
11
+ #
12
+ # @return [String]
13
+ attr_reader :name
14
+
15
+ # The arguments to the function.
16
+ #
17
+ # @return [Array<Script::Node>]
18
+ attr_reader :args
19
+
20
+ # Don't set the context for child nodes if this is `url()`,
21
+ # since `url()` allows quoted strings.
22
+ #
23
+ # @param context [Symbol]
24
+ # @see Node#context=
25
+ def context=(context)
26
+ super unless @name == "url"
27
+ end
28
+
29
+ # @param name [String] See \{#name}
30
+ # @param name [Array<Script::Node>] See \{#args}
31
+ def initialize(name, args)
32
+ @name = name
33
+ @args = args
34
+ super()
35
+ end
36
+
37
+ # @return [String] A string representation of the function call
38
+ def inspect
39
+ "#{name}(#{args.map {|a| a.inspect}.join(', ')})"
40
+ end
41
+
42
+ # @see Node#to_sass
43
+ def to_sass(opts = {})
44
+ "#{dasherize(name, opts)}(#{args.map {|a| a.to_sass(opts)}.join(', ')})"
45
+ end
46
+
47
+ # Returns the arguments to the function.
48
+ #
49
+ # @return [Array<Node>]
50
+ # @see Node#children
51
+ def children
52
+ @args
53
+ end
54
+
55
+ protected
56
+
57
+ # Evaluates the function call.
58
+ #
59
+ # @param environment [Sass::Environment] The environment in which to evaluate the SassScript
60
+ # @return [Literal] The SassScript object that is the value of the function call
61
+ # @raise [Sass::SyntaxError] if the function call raises an ArgumentError
62
+ def _perform(environment)
63
+ args = self.args.map {|a| a.perform(environment)}
64
+ ruby_name = name.gsub('-', '_')
65
+ unless Sass::Util.has?(:public_instance_method, Functions, ruby_name) && ruby_name !~ /^__/
66
+ opts(Script::String.new("#{name}(#{args.map {|a| a.perform(environment)}.join(', ')})"))
67
+ else
68
+ opts(Functions::EvaluationContext.new(environment.options).send(ruby_name, *args))
69
+ end
70
+ rescue ArgumentError => e
71
+ raise e unless e.backtrace.any? {|t| t =~ /:in `(block in )?(#{name}|perform)'$/}
72
+ raise Sass::SyntaxError.new("#{e.message} for `#{name}'")
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,852 @@
1
+ module Sass::Script
2
+ # Methods in this module are accessible from the SassScript context.
3
+ # For example, you can write
4
+ #
5
+ # $color = hsl(120deg, 100%, 50%)
6
+ #
7
+ # and it will call {Sass::Script::Functions#hsl}.
8
+ #
9
+ # The following functions are provided:
10
+ #
11
+ # ## RGB Functions
12
+ #
13
+ # \{#rgb}
14
+ # : Converts an `rgb(red, green, blue)` triplet into a color.
15
+ #
16
+ # \{#rgba}
17
+ # : Converts an `rgba(red, green, blue, alpha)` quadruplet into a color.
18
+ #
19
+ # \{#red}
20
+ # : Gets the red component of a color.
21
+ #
22
+ # \{#green}
23
+ # : Gets the green component of a color.
24
+ #
25
+ # \{#blue}
26
+ # : Gets the blue component of a color.
27
+ #
28
+ # \{#mix}
29
+ # : Mixes two colors together.
30
+ #
31
+ # ## HSL Functions
32
+ #
33
+ # \{#hsl}
34
+ # : Converts an `hsl(hue, saturation, lightness)` triplet into a color.
35
+ #
36
+ # \{#hsla}
37
+ # : Converts an `hsla(hue, saturation, lightness, alpha)` quadruplet into a color.
38
+ #
39
+ # \{#hue}
40
+ # : Gets the hue component of a color.
41
+ #
42
+ # \{#saturation}
43
+ # : Gets the saturation component of a color.
44
+ #
45
+ # \{#lightness}
46
+ # : Gets the lightness component of a color.
47
+ #
48
+ # \{#adjust_hue #adjust-hue}
49
+ # : Changes the hue of a color.
50
+ #
51
+ # \{#lighten}
52
+ # : Makes a color lighter.
53
+ #
54
+ # \{#darken}
55
+ # : Makes a color darker.
56
+ #
57
+ # \{#saturate}
58
+ # : Makes a color more saturated.
59
+ #
60
+ # \{#desaturate}
61
+ # : Makes a color less saturated.
62
+ #
63
+ # \{#grayscale}
64
+ # : Converts a color to grayscale.
65
+ #
66
+ # \{#complement}
67
+ # : Returns the complement of a color.
68
+ #
69
+ # \{#invert}
70
+ # : Returns the inverse of a color.
71
+ #
72
+ # ## Opacity Functions
73
+ #
74
+ # \{#alpha} / \{#opacity}
75
+ # : Gets the alpha component (opacity) of a color.
76
+ #
77
+ # \{#rgba}
78
+ # : Sets the alpha component of a color.
79
+ #
80
+ # \{#opacify} / \{#fade_in #fade-in}
81
+ # : Makes a color more opaque.
82
+ #
83
+ # \{#transparentize} / \{#fade_out #fade-out}
84
+ # : Makes a color more transparent.
85
+ #
86
+ # ## String Functions
87
+ #
88
+ # \{#unquote}
89
+ # : Removes the quotes from a string.
90
+ #
91
+ # \{#quote}
92
+ # : Adds quotes to a string.
93
+ #
94
+ # ## Number Functions
95
+ #
96
+ # \{#percentage}
97
+ # : Converts a unitless number to a percentage.
98
+ #
99
+ # \{#round}
100
+ # : Rounds a number to the nearest whole number.
101
+ #
102
+ # \{#ceil}
103
+ # : Rounds a number up to the nearest whole number.
104
+ #
105
+ # \{#floor}
106
+ # : Rounds a number down to the nearest whole number.
107
+ #
108
+ # \{#abs}
109
+ # : Returns the absolute value of a number.
110
+ #
111
+ # ## Introspection Functions
112
+ #
113
+ # \{#type_of}
114
+ # : Returns the type of a value.
115
+ #
116
+ # \{#unit}
117
+ # : Returns the units associated with a number.
118
+ #
119
+ # \{#unitless}
120
+ # : Returns whether a number has units or not.
121
+ #
122
+ # \{#comparable}
123
+ # : Returns whether two numbers can be added or compared.
124
+ #
125
+ # These functions are described in more detail below.
126
+ #
127
+ # ## Adding Custom Functions
128
+ #
129
+ # New Sass functions can be added by adding Ruby methods to this module.
130
+ # For example:
131
+ #
132
+ # module Sass::Script::Functions
133
+ # def reverse(string)
134
+ # assert_type string, :String
135
+ # Sass::Script::String.new(string.value.reverse)
136
+ # end
137
+ # end
138
+ #
139
+ # There are a few things to keep in mind when modifying this module.
140
+ # First of all, the arguments passed are {Sass::Script::Literal} objects.
141
+ # Literal objects are also expected to be returned.
142
+ # This means that Ruby values must be unwrapped and wrapped.
143
+ #
144
+ # Most Literal objects support the {Sass::Script::Literal#value value} accessor
145
+ # for getting their Ruby values.
146
+ # Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
147
+ # {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
148
+ #
149
+ # Second, making Ruby functions accessible from Sass introduces the temptation
150
+ # to do things like database access within stylesheets.
151
+ # This is generally a bad idea;
152
+ # since Sass files are by default only compiled once,
153
+ # dynamic code is not a great fit.
154
+ #
155
+ # If you really, really need to compile Sass on each request,
156
+ # first make sure you have adequate caching set up.
157
+ # Then you can use {Sass::Engine} to render the code,
158
+ # using the {file:SASS_REFERENCE.md#custom-option `options` parameter}
159
+ # to pass in data that {EvaluationContext#options can be accessed}
160
+ # from your Sass functions.
161
+ #
162
+ # Within one of the functions in this module,
163
+ # methods of {EvaluationContext} can be used.
164
+ #
165
+ # ### Caveats
166
+ #
167
+ # When creating new {Literal} objects within functions,
168
+ # be aware that it's not safe to call {Literal#to_s #to_s}
169
+ # (or other methods that use the string representation)
170
+ # on those objects without first setting {Node#options= the #options attribute}.
171
+ module Functions
172
+ # The context in which methods in {Script::Functions} are evaluated.
173
+ # That means that all instance methods of {EvaluationContext}
174
+ # are available to use in functions.
175
+ class EvaluationContext
176
+ # The options hash for the {Sass::Engine} that is processing the function call
177
+ #
178
+ # @return [{Symbol => Object}]
179
+ attr_reader :options
180
+
181
+ # @param options [{Symbol => Object}] See \{#options}
182
+ def initialize(options)
183
+ @options = options
184
+
185
+ # We need to include this individually in each instance
186
+ # because of an icky Ruby restriction
187
+ class << self; include Sass::Script::Functions; end
188
+ end
189
+
190
+ # Asserts that the type of a given SassScript value
191
+ # is the expected type (designated by a symbol).
192
+ #
193
+ # Valid types are `:Bool`, `:Color`, `:Number`, and `:String`.
194
+ # Note that `:String` will match both double-quoted strings
195
+ # and unquoted identifiers.
196
+ #
197
+ # @example
198
+ # assert_type value, :String
199
+ # assert_type value, :Number
200
+ # @param value [Sass::Script::Literal] A SassScript value
201
+ # @param type [Symbol] The name of the type the value is expected to be
202
+ def assert_type(value, type)
203
+ return if value.is_a?(Sass::Script.const_get(type))
204
+ raise ArgumentError.new("#{value.inspect} is not a #{type.to_s.downcase}")
205
+ end
206
+ end
207
+
208
+ instance_methods.each { |m| undef_method m unless m.to_s =~ /^__/ }
209
+
210
+
211
+ # Creates a {Color} object from red, green, and blue values.
212
+ #
213
+ # @param red [Number]
214
+ # A number between 0 and 255 inclusive,
215
+ # or between 0% and 100% inclusive
216
+ # @param green [Number]
217
+ # A number between 0 and 255 inclusive,
218
+ # or between 0% and 100% inclusive
219
+ # @param blue [Number]
220
+ # A number between 0 and 255 inclusive,
221
+ # or between 0% and 100% inclusive
222
+ # @see #rgba
223
+ # @return [Color]
224
+ def rgb(red, green, blue)
225
+ assert_type red, :Number
226
+ assert_type green, :Number
227
+ assert_type blue, :Number
228
+
229
+ Color.new([red, green, blue].map do |c|
230
+ v = c.value
231
+ if c.numerator_units == ["%"] && c.denominator_units.empty?
232
+ next v * 255 / 100.0 if (0..100).include?(v)
233
+ raise ArgumentError.new("Color value #{c} must be between 0% and 100% inclusive")
234
+ else
235
+ next v if (0..255).include?(v)
236
+ raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive")
237
+ end
238
+ end)
239
+ end
240
+
241
+ # @see #rgb
242
+ # @overload rgba(red, green, blue, alpha)
243
+ # Creates a {Color} object from red, green, and blue values,
244
+ # as well as an alpha channel indicating opacity.
245
+ #
246
+ # @param red [Number]
247
+ # A number between 0 and 255 inclusive
248
+ # @param green [Number]
249
+ # A number between 0 and 255 inclusive
250
+ # @param blue [Number]
251
+ # A number between 0 and 255 inclusive
252
+ # @param alpha [Number]
253
+ # A number between 0 and 1
254
+ # @return [Color]
255
+ #
256
+ # @overload rgba(color, alpha)
257
+ # Sets the opacity of a color.
258
+ #
259
+ # @example
260
+ # rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
261
+ # rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
262
+ #
263
+ # @param color [Color]
264
+ # @param alpha [Number]
265
+ # A number between 0 and 1
266
+ # @return [Color]
267
+ def rgba(*args)
268
+ case args.size
269
+ when 2
270
+ color, alpha = args
271
+
272
+ assert_type color, :Color
273
+ assert_type alpha, :Number
274
+
275
+ unless (0..1).include?(alpha.value)
276
+ raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1 inclusive")
277
+ end
278
+
279
+ color.with(:alpha => alpha.value)
280
+ when 4
281
+ red, green, blue, alpha = args
282
+ rgba(rgb(red, green, blue), alpha)
283
+ else
284
+ raise ArgumentError.new("wrong number of arguments (#{args.size} for 4)")
285
+ end
286
+ end
287
+
288
+ # Creates a {Color} object from hue, saturation, and lightness.
289
+ # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
290
+ #
291
+ # @param hue [Number] The hue of the color.
292
+ # Should be between 0 and 360 degrees, inclusive
293
+ # @param saturation [Number] The saturation of the color.
294
+ # Must be between `0%` and `100%`, inclusive
295
+ # @param lightness [Number] The lightness of the color.
296
+ # Must be between `0%` and `100%`, inclusive
297
+ # @return [Color] The resulting color
298
+ # @see #hsla
299
+ # @raise [ArgumentError] if `saturation` or `lightness` are out of bounds
300
+ def hsl(hue, saturation, lightness)
301
+ hsla(hue, saturation, lightness, Number.new(1))
302
+ end
303
+
304
+ # Creates a {Color} object from hue, saturation, and lightness,
305
+ # as well as an alpha channel indicating opacity.
306
+ # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color).
307
+ #
308
+ # @param hue [Number] The hue of the color.
309
+ # Should be between 0 and 360 degrees, inclusive
310
+ # @param saturation [Number] The saturation of the color.
311
+ # Must be between `0%` and `100%`, inclusive
312
+ # @param lightness [Number] The lightness of the color.
313
+ # Must be between `0%` and `100%`, inclusive
314
+ # @param alpha [Number] The opacity of the color.
315
+ # Must be between 0 and 1, inclusive
316
+ # @return [Color] The resulting color
317
+ # @see #hsl
318
+ # @raise [ArgumentError] if `saturation`, `lightness`, or `alpha` are out of bounds
319
+ def hsla(hue, saturation, lightness, alpha)
320
+ assert_type hue, :Number
321
+ assert_type saturation, :Number
322
+ assert_type lightness, :Number
323
+ assert_type alpha, :Number
324
+
325
+ unless (0..1).include?(alpha.value)
326
+ raise ArgumentError.new("Alpha channel #{alpha.value} must be between 0 and 1")
327
+ end
328
+
329
+ original_s = saturation
330
+ original_l = lightness
331
+ # This algorithm is from http://www.w3.org/TR/css3-color#hsl-color
332
+ h, s, l = [hue, saturation, lightness].map { |a| a.value }
333
+ raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") unless (0..100).include?(s)
334
+ raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") unless (0..100).include?(l)
335
+
336
+ Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
337
+ end
338
+
339
+ # Returns the red component of a color.
340
+ #
341
+ # @param color [Color]
342
+ # @return [Number]
343
+ # @raise [ArgumentError] If `color` isn't a color
344
+ def red(color)
345
+ assert_type color, :Color
346
+ Sass::Script::Number.new(color.red)
347
+ end
348
+
349
+ # Returns the green component of a color.
350
+ #
351
+ # @param color [Color]
352
+ # @return [Number]
353
+ # @raise [ArgumentError] If `color` isn't a color
354
+ def green(color)
355
+ assert_type color, :Color
356
+ Sass::Script::Number.new(color.green)
357
+ end
358
+
359
+ # Returns the blue component of a color.
360
+ #
361
+ # @param color [Color]
362
+ # @return [Number]
363
+ # @raise [ArgumentError] If `color` isn't a color
364
+ def blue(color)
365
+ assert_type color, :Color
366
+ Sass::Script::Number.new(color.blue)
367
+ end
368
+
369
+ # Returns the hue component of a color.
370
+ #
371
+ # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
372
+ #
373
+ # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
374
+ #
375
+ # @param color [Color]
376
+ # @return [Number] between 0deg and 360deg
377
+ # @see #adjust_hue
378
+ # @raise [ArgumentError] if `color` isn't a color
379
+ def hue(color)
380
+ assert_type color, :Color
381
+ Sass::Script::Number.new(color.hue, ["deg"])
382
+ end
383
+
384
+ # Returns the saturation component of a color.
385
+ #
386
+ # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
387
+ #
388
+ # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
389
+ #
390
+ # @param color [Color]
391
+ # @return [Number] between 0% and 100%
392
+ # @see #saturate
393
+ # @see #desaturate
394
+ # @raise [ArgumentError] if `color` isn't a color
395
+ def saturation(color)
396
+ assert_type color, :Color
397
+ Sass::Script::Number.new(color.saturation, ["%"])
398
+ end
399
+
400
+ # Returns the hue component of a color.
401
+ #
402
+ # See [the CSS3 HSL specification](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
403
+ #
404
+ # Calculated from RGB where necessary via [this algorithm](http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV).
405
+ #
406
+ # @param color [Color]
407
+ # @return [Number] between 0% and 100%
408
+ # @see #lighten
409
+ # @see #darken
410
+ # @raise [ArgumentError] if `color` isn't a color
411
+ def lightness(color)
412
+ assert_type color, :Color
413
+ Sass::Script::Number.new(color.lightness, ["%"])
414
+ end
415
+
416
+ # Returns the alpha component (opacity) of a color.
417
+ # This is 1 unless otherwise specified.
418
+ #
419
+ # This function also supports the proprietary Microsoft
420
+ # `alpha(opacity=20)` syntax.
421
+ #
422
+ # @overload def alpha(color)
423
+ # @param color [Color]
424
+ # @return [Number]
425
+ # @see #opacify
426
+ # @see #transparentize
427
+ # @raise [ArgumentError] If `color` isn't a color
428
+ def alpha(*args)
429
+ if args.all? do |a|
430
+ a.is_a?(Sass::Script::String) && a.type == :identifier &&
431
+ a.value =~ /^[a-zA-Z]+\s*=/
432
+ end
433
+ # Support the proprietary MS alpha() function
434
+ return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
435
+ end
436
+
437
+ opacity(*args)
438
+ end
439
+
440
+ # Returns the alpha component (opacity) of a color.
441
+ # This is 1 unless otherwise specified.
442
+ #
443
+ # @param color [Color]
444
+ # @return [Number]
445
+ # @see #opacify
446
+ # @see #transparentize
447
+ # @raise [ArgumentError] If `color` isn't a color
448
+ def opacity(color)
449
+ assert_type color, :Color
450
+ Sass::Script::Number.new(color.alpha)
451
+ end
452
+
453
+ # Makes a color more opaque.
454
+ # Takes a color and an amount between 0 and 1,
455
+ # and returns a color with the opacity increased by that value.
456
+ #
457
+ # @example
458
+ # opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
459
+ # opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
460
+ # @param color [Color]
461
+ # @param amount [Number]
462
+ # @return [Color]
463
+ # @see #transparentize
464
+ # @raise [ArgumentError] If `color` isn't a color,
465
+ # or `number` isn't a number between 0 and 1
466
+ def opacify(color, amount)
467
+ adjust(color, amount, :alpha, 0..1, :+)
468
+ end
469
+ alias_method :fade_in, :opacify
470
+
471
+ # Makes a color more transparent.
472
+ # Takes a color and an amount between 0 and 1,
473
+ # and returns a color with the opacity decreased by that value.
474
+ #
475
+ # @example
476
+ # transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
477
+ # transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
478
+ # @param color [Color]
479
+ # @param amount [Number]
480
+ # @return [Color]
481
+ # @see #opacify
482
+ # @raise [ArgumentError] If `color` isn't a color,
483
+ # or `number` isn't a number between 0 and 1
484
+ def transparentize(color, amount)
485
+ adjust(color, amount, :alpha, 0..1, :-)
486
+ end
487
+ alias_method :fade_out, :transparentize
488
+
489
+ # Makes a color lighter.
490
+ # Takes a color and an amount between 0% and 100%,
491
+ # and returns a color with the lightness increased by that value.
492
+ #
493
+ # @example
494
+ # lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
495
+ # lighten(#800, 20%) => #e00
496
+ # @param color [Color]
497
+ # @param amount [Number]
498
+ # @return [Color]
499
+ # @see #darken
500
+ # @raise [ArgumentError] If `color` isn't a color,
501
+ # or `number` isn't a number between 0% and 100%
502
+ def lighten(color, amount)
503
+ adjust(color, amount, :lightness, 0..100, :+, "%")
504
+ end
505
+
506
+ # Makes a color darker.
507
+ # Takes a color and an amount between 0% and 100%,
508
+ # and returns a color with the lightness decreased by that value.
509
+ #
510
+ # @example
511
+ # darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
512
+ # darken(#800, 20%) => #200
513
+ # @param color [Color]
514
+ # @param amount [Number]
515
+ # @return [Color]
516
+ # @see #lighten
517
+ # @raise [ArgumentError] If `color` isn't a color,
518
+ # or `number` isn't a number between 0% and 100%
519
+ def darken(color, amount)
520
+ adjust(color, amount, :lightness, 0..100, :-, "%")
521
+ end
522
+
523
+ # Makes a color more saturated.
524
+ # Takes a color and an amount between 0% and 100%,
525
+ # and returns a color with the saturation increased by that value.
526
+ #
527
+ # @example
528
+ # saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
529
+ # saturate(#855, 20%) => #9e3f3f
530
+ # @param color [Color]
531
+ # @param amount [Number]
532
+ # @return [Color]
533
+ # @see #desaturate
534
+ # @raise [ArgumentError] If `color` isn't a color,
535
+ # or `number` isn't a number between 0% and 100%
536
+ def saturate(color, amount)
537
+ adjust(color, amount, :saturation, 0..100, :+, "%")
538
+ end
539
+
540
+ # Makes a color less saturated.
541
+ # Takes a color and an amount between 0% and 100%,
542
+ # and returns a color with the saturation decreased by that value.
543
+ #
544
+ # @example
545
+ # desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
546
+ # desaturate(#855, 20%) => #726b6b
547
+ # @param color [Color]
548
+ # @param amount [Number]
549
+ # @return [Color]
550
+ # @see #saturate
551
+ # @raise [ArgumentError] If `color` isn't a color,
552
+ # or `number` isn't a number between 0% and 100%
553
+ def desaturate(color, amount)
554
+ adjust(color, amount, :saturation, 0..100, :-, "%")
555
+ end
556
+
557
+ # Changes the hue of a color while retaining the lightness and saturation.
558
+ # Takes a color and a number of degrees (usually between -360deg and 360deg),
559
+ # and returns a color with the hue rotated by that value.
560
+ #
561
+ # @example
562
+ # adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
563
+ # adjust-hue(hsl(120, 30%, 90%), 060deg) => hsl(60, 30%, 90%)
564
+ # adjust-hue(#811, 45deg) => #886a11
565
+ # @param color [Color]
566
+ # @param amount [Number]
567
+ # @return [Color]
568
+ # @raise [ArgumentError] If `color` isn't a color, or `number` isn't a number
569
+ def adjust_hue(color, degrees)
570
+ assert_type color, :Color
571
+ assert_type degrees, :Number
572
+ color.with(:hue => color.hue + degrees.value)
573
+ end
574
+
575
+ # Mixes together two colors.
576
+ # Specifically, takes the average of each of the RGB components,
577
+ # optionally weighted by the given percentage.
578
+ # The opacity of the colors is also considered when weighting the components.
579
+ #
580
+ # The weight specifies the amount of the first color that should be included
581
+ # in the returned color.
582
+ # The default, 50%, means that half the first color
583
+ # and half the second color should be used.
584
+ # 25% means that a quarter of the first color
585
+ # and three quarters of the second color should be used.
586
+ #
587
+ # @example
588
+ # mix(#f00, #00f) => #7f007f
589
+ # mix(#f00, #00f, 25%) => #3f00bf
590
+ # mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
591
+ # @overload mix(color1, color2, weight = 50%)
592
+ # @param color1 [Color]
593
+ # @param color2 [Color]
594
+ # @param weight [Number] between 0% and 100%
595
+ # @return [Color]
596
+ # @raise [ArgumentError] if `color1` or `color2` aren't colors,
597
+ # or `weight` isn't a number between 0% and 100%
598
+ def mix(color1, color2, weight = Number.new(50))
599
+ assert_type color1, :Color
600
+ assert_type color2, :Color
601
+ assert_type weight, :Number
602
+
603
+ unless (0..100).include?(weight.value)
604
+ raise ArgumentError.new("Weight #{weight} must be between 0% and 100%")
605
+ end
606
+
607
+ # This algorithm factors in both the user-provided weight
608
+ # and the difference between the alpha values of the two colors
609
+ # to decide how to perform the weighted average of the two RGB values.
610
+ #
611
+ # It works by first normalizing both parameters to be within [-1, 1],
612
+ # where 1 indicates "only use color1", -1 indicates "only use color 0",
613
+ # and all values in between indicated a proportionately weighted average.
614
+ #
615
+ # Once we have the normalized variables w and a,
616
+ # we apply the formula (w + a)/(1 + w*a)
617
+ # to get the combined weight (in [-1, 1]) of color1.
618
+ # This formula has two especially nice properties:
619
+ #
620
+ # * When either w or a are -1 or 1, the combined weight is also that number
621
+ # (cases where w * a == -1 are undefined, and handled as a special case).
622
+ #
623
+ # * When a is 0, the combined weight is w, and vice versa
624
+ #
625
+ # Finally, the weight of color1 is renormalized to be within [0, 1]
626
+ # and the weight of color2 is given by 1 minus the weight of color1.
627
+ p = weight.value/100.0
628
+ w = p*2 - 1
629
+ a = color1.alpha - color2.alpha
630
+
631
+ w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
632
+ w2 = 1 - w1
633
+
634
+ rgb = color1.rgb.zip(color2.rgb).map {|v1, v2| v1*w1 + v2*w2}
635
+ alpha = color1.alpha*p + color2.alpha*(1-p)
636
+ Color.new(rgb + [alpha])
637
+ end
638
+
639
+ # Converts a color to grayscale.
640
+ # This is identical to `desaturate(color, 100%)`.
641
+ #
642
+ # @param color [Color]
643
+ # @return [Color]
644
+ # @raise [ArgumentError] if `color` isn't a color
645
+ # @see #desaturate
646
+ def grayscale(color)
647
+ desaturate color, Number.new(100)
648
+ end
649
+
650
+ # Returns the complement of a color.
651
+ # This is identical to `adjust-hue(color, 180deg)`.
652
+ #
653
+ # @param color [Color]
654
+ # @return [Color]
655
+ # @raise [ArgumentError] if `color` isn't a color
656
+ # @see #adjust_hue #adjust-hue
657
+ def complement(color)
658
+ adjust_hue color, Number.new(180)
659
+ end
660
+
661
+ # Returns the inverse (negative) of a color.
662
+ # The red, green, and blue values are inverted, while the opacity is left alone.
663
+ #
664
+ # @param color [Color]
665
+ # @return [Color]
666
+ # @raise [ArgumentError] if `color` isn't a color
667
+ def invert(color)
668
+ assert_type color, :Color
669
+ color.with(
670
+ :red => (255 - color.red),
671
+ :green => (255 - color.green),
672
+ :blue => (255 - color.blue))
673
+ end
674
+
675
+ # Removes quotes from a string if the string is quoted,
676
+ # or returns the same string if it's not.
677
+ #
678
+ # @param str [String]
679
+ # @return [String]
680
+ # @raise [ArgumentError] if `str` isn't a string
681
+ # @see #quote
682
+ # @example
683
+ # unquote("foo") => foo
684
+ # unquote(foo) => foo
685
+ def unquote(str)
686
+ assert_type str, :String
687
+ Sass::Script::String.new(str.value, :identifier)
688
+ end
689
+
690
+ # Add quotes to a string if the string isn't quoted,
691
+ # or returns the same string if it is.
692
+ #
693
+ # @param str [String]
694
+ # @return [String]
695
+ # @raise [ArgumentError] if `str` isn't a string
696
+ # @see #unquote
697
+ # @example
698
+ # quote("foo") => "foo"
699
+ # quote(foo) => "foo"
700
+ def quote(str)
701
+ assert_type str, :String
702
+ Sass::Script::String.new(str.value, :string)
703
+ end
704
+
705
+ # Inspects the type of the argument, returning it as an unquoted string.
706
+ #
707
+ # @example
708
+ # type-of(100px) => number
709
+ # type-of(asdf) => string
710
+ # type-of("asdf") => string
711
+ # type-of(true) => bool
712
+ # type-of(#fff) => color
713
+ # type-of(blue) => color
714
+ # @param obj [Literal] The object to inspect
715
+ # @return [String] The unquoted string name of the literal's type
716
+ def type_of(obj)
717
+ Sass::Script::String.new(obj.class.name.gsub(/Sass::Script::/,'').downcase)
718
+ end
719
+
720
+ # Inspects the unit of the number, returning it as a quoted string.
721
+ # Complex units are sorted in alphabetical order by numerator and denominator.
722
+ #
723
+ # @example
724
+ # unit(100) => ""
725
+ # unit(100px) => "px"
726
+ # unit(3em) => "em"
727
+ # unit(10px * 5em) => "em*px"
728
+ # unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
729
+ # @param number [Literal] The number to inspect
730
+ # @return [String] The unit(s) of the number
731
+ # @raise [ArgumentError] if `number` isn't a number
732
+ def unit(number)
733
+ assert_type number, :Number
734
+ Sass::Script::String.new(number.unit_str, :string)
735
+ end
736
+
737
+ # Inspects the unit of the number, returning a boolean indicating if it is unitless.
738
+ #
739
+ # @example
740
+ # unitless(100) => true
741
+ # unitless(100px) => false
742
+ # @param number [Literal] The number to inspect
743
+ # @return [Bool] Whether or not the number is unitless
744
+ # @raise [ArgumentError] if `number` isn't a number
745
+ def unitless(number)
746
+ assert_type number, :Number
747
+ Sass::Script::Bool.new(number.unitless?)
748
+ end
749
+
750
+ # Returns true if two numbers are similar enough to be added, subtracted, or compared.
751
+ #
752
+ # @example
753
+ # comparable(2px, 1px) => true
754
+ # comparable(100px, 3em) => false
755
+ # comparable(10cm, 3mm) => true
756
+ # @param number1 [Number]
757
+ # @param number2 [Number]
758
+ # @return [Bool] indicating if the numbers can be compared.
759
+ # @raise [ArgumentError] if `number1` or `number2` aren't numbers
760
+ def comparable(number1, number2)
761
+ assert_type number1, :Number
762
+ assert_type number2, :Number
763
+ Sass::Script::Bool.new(number1.comparable_to?(number2))
764
+ end
765
+
766
+ # Converts a decimal number to a percentage.
767
+ #
768
+ # @example
769
+ # percentage(100px / 50px) => 200%
770
+ # @param value [Number] The decimal number to convert to a percentage
771
+ # @return [Number] The percentage
772
+ # @raise [ArgumentError] If `value` isn't a unitless number
773
+ def percentage(value)
774
+ unless value.is_a?(Sass::Script::Number) && value.unitless?
775
+ raise ArgumentError.new("#{value.inspect} is not a unitless number")
776
+ end
777
+ Sass::Script::Number.new(value.value * 100, ['%'])
778
+ end
779
+
780
+ # Rounds a number to the nearest whole number.
781
+ #
782
+ # @example
783
+ # round(10.4px) => 10px
784
+ # round(10.6px) => 11px
785
+ # @param value [Number] The number
786
+ # @return [Number] The rounded number
787
+ # @raise [Sass::SyntaxError] if `value` isn't a number
788
+ def round(value)
789
+ numeric_transformation(value) {|n| n.round}
790
+ end
791
+
792
+ # Rounds a number up to the nearest whole number.
793
+ #
794
+ # @example
795
+ # ciel(10.4px) => 11px
796
+ # ciel(10.6px) => 11px
797
+ # @param value [Number] The number
798
+ # @return [Number] The rounded number
799
+ # @raise [Sass::SyntaxError] if `value` isn't a number
800
+ def ceil(value)
801
+ numeric_transformation(value) {|n| n.ceil}
802
+ end
803
+
804
+ # Rounds down to the nearest whole number.
805
+ #
806
+ # @example
807
+ # floor(10.4px) => 10px
808
+ # floor(10.6px) => 10px
809
+ # @param value [Number] The number
810
+ # @return [Number] The rounded number
811
+ # @raise [Sass::SyntaxError] if `value` isn't a number
812
+ def floor(value)
813
+ numeric_transformation(value) {|n| n.floor}
814
+ end
815
+
816
+ # Finds the absolute value of a number.
817
+ #
818
+ # @example
819
+ # abs(10px) => 10px
820
+ # abs(-10px) => 10px
821
+ # @param value [Number] The number
822
+ # @return [Number] The absolute value
823
+ # @raise [Sass::SyntaxError] if `value` isn't a number
824
+ def abs(value)
825
+ numeric_transformation(value) {|n| n.abs}
826
+ end
827
+
828
+ private
829
+
830
+ # This method implements the pattern of transforming a numeric value into
831
+ # another numeric value with the same units.
832
+ # It yields a number to a block to perform the operation and return a number
833
+ def numeric_transformation(value)
834
+ assert_type value, :Number
835
+ Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
836
+ end
837
+
838
+ def adjust(color, amount, attr, range, op, units = "")
839
+ assert_type color, :Color
840
+ assert_type amount, :Number
841
+ unless range.include?(amount.value)
842
+ raise ArgumentError.new("Amount #{amount} must be between #{range.first}#{units} and #{range.last}#{units}")
843
+ end
844
+
845
+ # TODO: is it worth restricting here,
846
+ # or should we do so in the Color constructor itself,
847
+ # and allow clipping in rgb() et al?
848
+ color.with(attr => Sass::Util.restrict(
849
+ color.send(attr).send(op, amount.value), range))
850
+ end
851
+ end
852
+ end