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,530 @@
1
+ module Sass::Script::Value
2
+ # A SassScript object representing a number.
3
+ # SassScript numbers can have decimal values,
4
+ # and can also have units.
5
+ # For example, `12`, `1px`, and `10.45em`
6
+ # are all valid values.
7
+ #
8
+ # Numbers can also have more complex units, such as `1px*em/in`.
9
+ # These cannot be inputted directly in Sass code at the moment.
10
+ class Number < Base
11
+ # The Ruby value of the number.
12
+ #
13
+ # @return [Numeric]
14
+ attr_reader :value
15
+
16
+ # A list of units in the numerator of the number.
17
+ # For example, `1px*em/in*cm` would return `["px", "em"]`
18
+ # @return [Array<String>]
19
+ attr_reader :numerator_units
20
+
21
+ # A list of units in the denominator of the number.
22
+ # For example, `1px*em/in*cm` would return `["in", "cm"]`
23
+ # @return [Array<String>]
24
+ attr_reader :denominator_units
25
+
26
+ # The original representation of this number.
27
+ # For example, although the result of `1px/2px` is `0.5`,
28
+ # the value of `#original` is `"1px/2px"`.
29
+ #
30
+ # This is only non-nil when the original value should be used as the CSS value,
31
+ # as in `font: 1px/2px`.
32
+ #
33
+ # @return [Boolean, nil]
34
+ attr_accessor :original
35
+
36
+ def self.precision
37
+ @precision ||= 5
38
+ end
39
+
40
+ # Sets the number of digits of precision
41
+ # For example, if this is `3`,
42
+ # `3.1415926` will be printed as `3.142`.
43
+ def self.precision=(digits)
44
+ @precision = digits.round
45
+ @precision_factor = 10.0**@precision
46
+ end
47
+
48
+ # the precision factor used in numeric output
49
+ # it is derived from the `precision` method.
50
+ def self.precision_factor
51
+ @precision_factor ||= 10.0**precision
52
+ end
53
+
54
+ # Used so we don't allocate two new arrays for each new number.
55
+ NO_UNITS = []
56
+
57
+ # @param value [Numeric] The value of the number
58
+ # @param numerator_units [::String, Array<::String>] See \{#numerator\_units}
59
+ # @param denominator_units [::String, Array<::String>] See \{#denominator\_units}
60
+ def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
61
+ numerator_units = [numerator_units] if numerator_units.is_a?(::String)
62
+ denominator_units = [denominator_units] if denominator_units.is_a?(::String)
63
+ super(value)
64
+ @numerator_units = numerator_units
65
+ @denominator_units = denominator_units
66
+ normalize!
67
+ end
68
+
69
+ # The SassScript `+` operation.
70
+ # Its functionality depends on the type of its argument:
71
+ #
72
+ # {Number}
73
+ # : Adds the two numbers together, converting units if possible.
74
+ #
75
+ # {Color}
76
+ # : Adds this number to each of the RGB color channels.
77
+ #
78
+ # {Value}
79
+ # : See {Value::Base#plus}.
80
+ #
81
+ # @param other [Value] The right-hand side of the operator
82
+ # @return [Value] The result of the operation
83
+ # @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
84
+ def plus(other)
85
+ if other.is_a? Number
86
+ operate(other, :+)
87
+ elsif other.is_a?(Color)
88
+ other.plus(self)
89
+ else
90
+ super
91
+ end
92
+ end
93
+
94
+ # The SassScript binary `-` operation (e.g. `$a - $b`).
95
+ # Its functionality depends on the type of its argument:
96
+ #
97
+ # {Number}
98
+ # : Subtracts this number from the other, converting units if possible.
99
+ #
100
+ # {Value}
101
+ # : See {Value::Base#minus}.
102
+ #
103
+ # @param other [Value] The right-hand side of the operator
104
+ # @return [Value] The result of the operation
105
+ # @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
106
+ def minus(other)
107
+ if other.is_a? Number
108
+ operate(other, :-)
109
+ else
110
+ super
111
+ end
112
+ end
113
+
114
+ # The SassScript unary `+` operation (e.g. `+$a`).
115
+ #
116
+ # @return [Number] The value of this number
117
+ def unary_plus
118
+ self
119
+ end
120
+
121
+ # The SassScript unary `-` operation (e.g. `-$a`).
122
+ #
123
+ # @return [Number] The negative value of this number
124
+ def unary_minus
125
+ Number.new(-value, @numerator_units, @denominator_units)
126
+ end
127
+
128
+ # The SassScript `*` operation.
129
+ # Its functionality depends on the type of its argument:
130
+ #
131
+ # {Number}
132
+ # : Multiplies the two numbers together, converting units appropriately.
133
+ #
134
+ # {Color}
135
+ # : Multiplies each of the RGB color channels by this number.
136
+ #
137
+ # @param other [Number, Color] The right-hand side of the operator
138
+ # @return [Number, Color] The result of the operation
139
+ # @raise [NoMethodError] if `other` is an invalid type
140
+ def times(other)
141
+ if other.is_a? Number
142
+ operate(other, :*)
143
+ elsif other.is_a? Color
144
+ other.times(self)
145
+ else
146
+ raise NoMethodError.new(nil, :times)
147
+ end
148
+ end
149
+
150
+ # The SassScript `/` operation.
151
+ # Its functionality depends on the type of its argument:
152
+ #
153
+ # {Number}
154
+ # : Divides this number by the other, converting units appropriately.
155
+ #
156
+ # {Value}
157
+ # : See {Value::Base#div}.
158
+ #
159
+ # @param other [Value] The right-hand side of the operator
160
+ # @return [Value] The result of the operation
161
+ def div(other)
162
+ if other.is_a? Number
163
+ res = operate(other, :/)
164
+ if original && other.original
165
+ res.original = "#{original}/#{other.original}"
166
+ end
167
+ res
168
+ else
169
+ super
170
+ end
171
+ end
172
+
173
+ # The SassScript `%` operation.
174
+ #
175
+ # @param other [Number] The right-hand side of the operator
176
+ # @return [Number] This number modulo the other
177
+ # @raise [NoMethodError] if `other` is an invalid type
178
+ # @raise [Sass::UnitConversionError] if `other` has incompatible units
179
+ def mod(other)
180
+ if other.is_a?(Number)
181
+ operate(other, :%)
182
+ else
183
+ raise NoMethodError.new(nil, :mod)
184
+ end
185
+ end
186
+
187
+ # The SassScript `==` operation.
188
+ #
189
+ # @param other [Value] The right-hand side of the operator
190
+ # @return [Boolean] Whether this number is equal to the other object
191
+ def eq(other)
192
+ return Bool::FALSE unless other.is_a?(Sass::Script::Value::Number)
193
+ this = self
194
+ begin
195
+ if unitless?
196
+ this = this.coerce(other.numerator_units, other.denominator_units)
197
+ else
198
+ other = other.coerce(@numerator_units, @denominator_units)
199
+ end
200
+ rescue Sass::UnitConversionError
201
+ return Bool::FALSE
202
+ end
203
+ Bool.new(this.value == other.value)
204
+ end
205
+
206
+ def hash
207
+ [value, numerator_units, denominator_units].hash
208
+ end
209
+
210
+ # Hash-equality works differently than `==` equality for numbers.
211
+ # Hash-equality must be transitive, so it just compares the exact value,
212
+ # numerator units, and denominator units.
213
+ def eql?(other)
214
+ value == other.value && numerator_units == other.numerator_units &&
215
+ denominator_units == other.denominator_units
216
+ end
217
+
218
+ # The SassScript `>` operation.
219
+ #
220
+ # @param other [Number] The right-hand side of the operator
221
+ # @return [Boolean] Whether this number is greater than the other
222
+ # @raise [NoMethodError] if `other` is an invalid type
223
+ def gt(other)
224
+ raise NoMethodError.new(nil, :gt) unless other.is_a?(Number)
225
+ operate(other, :>)
226
+ end
227
+
228
+ # The SassScript `>=` operation.
229
+ #
230
+ # @param other [Number] The right-hand side of the operator
231
+ # @return [Boolean] Whether this number is greater than or equal to the other
232
+ # @raise [NoMethodError] if `other` is an invalid type
233
+ def gte(other)
234
+ raise NoMethodError.new(nil, :gte) unless other.is_a?(Number)
235
+ operate(other, :>=)
236
+ end
237
+
238
+ # The SassScript `<` operation.
239
+ #
240
+ # @param other [Number] The right-hand side of the operator
241
+ # @return [Boolean] Whether this number is less than the other
242
+ # @raise [NoMethodError] if `other` is an invalid type
243
+ def lt(other)
244
+ raise NoMethodError.new(nil, :lt) unless other.is_a?(Number)
245
+ operate(other, :<)
246
+ end
247
+
248
+ # The SassScript `<=` operation.
249
+ #
250
+ # @param other [Number] The right-hand side of the operator
251
+ # @return [Boolean] Whether this number is less than or equal to the other
252
+ # @raise [NoMethodError] if `other` is an invalid type
253
+ def lte(other)
254
+ raise NoMethodError.new(nil, :lte) unless other.is_a?(Number)
255
+ operate(other, :<=)
256
+ end
257
+
258
+ # @return [String] The CSS representation of this number
259
+ # @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
260
+ # (e.g. `px*in`)
261
+ def to_s(opts = {})
262
+ return original if original
263
+ raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
264
+ inspect
265
+ end
266
+
267
+ # Returns a readable representation of this number.
268
+ #
269
+ # This representation is valid CSS (and valid SassScript)
270
+ # as long as there is only one unit.
271
+ #
272
+ # @return [String] The representation
273
+ def inspect(opts = {})
274
+ return original if original
275
+
276
+ value = self.class.round(self.value)
277
+ str = value.to_s
278
+
279
+ # Ruby will occasionally print in scientific notation if the number is
280
+ # small enough. That's technically valid CSS, but it's not well-supported
281
+ # and confusing.
282
+ str = ("%0.#{self.class.precision}f" % value).gsub(/0*$/, '') if str.include?('e')
283
+
284
+ unitless? ? str : "#{str}#{unit_str}"
285
+ end
286
+ alias_method :to_sass, :inspect
287
+
288
+ # @return [Fixnum] The integer value of the number
289
+ # @raise [Sass::SyntaxError] if the number isn't an integer
290
+ def to_i
291
+ super unless int?
292
+ value
293
+ end
294
+
295
+ # @return [Boolean] Whether or not this number is an integer.
296
+ def int?
297
+ value % 1 == 0.0
298
+ end
299
+
300
+ # @return [Boolean] Whether or not this number has no units.
301
+ def unitless?
302
+ @numerator_units.empty? && @denominator_units.empty?
303
+ end
304
+
305
+ # Checks whether the number has the numerator unit specified.
306
+ #
307
+ # @example
308
+ # number = Sass::Script::Value::Number.new(10, "px")
309
+ # number.is_unit?("px") => true
310
+ # number.is_unit?(nil) => false
311
+ #
312
+ # @param unit [::String, nil] The unit the number should have or nil if the number
313
+ # should be unitless.
314
+ # @see Number#unitless? The unitless? method may be more readable.
315
+ def is_unit?(unit)
316
+ if unit
317
+ denominator_units.size == 0 && numerator_units.size == 1 && numerator_units.first == unit
318
+ else
319
+ unitless?
320
+ end
321
+ end
322
+
323
+ # @return [Boolean] Whether or not this number has units that can be represented in CSS
324
+ # (that is, zero or one \{#numerator\_units}).
325
+ def legal_units?
326
+ (@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
327
+ end
328
+
329
+ # Returns this number converted to other units.
330
+ # The conversion takes into account the relationship between e.g. mm and cm,
331
+ # as well as between e.g. in and cm.
332
+ #
333
+ # If this number has no units, it will simply return itself
334
+ # with the given units.
335
+ #
336
+ # An incompatible coercion, e.g. between px and cm, will raise an error.
337
+ #
338
+ # @param num_units [Array<String>] The numerator units to coerce this number into.
339
+ # See {\#numerator\_units}
340
+ # @param den_units [Array<String>] The denominator units to coerce this number into.
341
+ # See {\#denominator\_units}
342
+ # @return [Number] The number with the new units
343
+ # @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
344
+ # current units
345
+ def coerce(num_units, den_units)
346
+ Number.new(if unitless?
347
+ value
348
+ else
349
+ value * coercion_factor(@numerator_units, num_units) /
350
+ coercion_factor(@denominator_units, den_units)
351
+ end, num_units, den_units)
352
+ end
353
+
354
+ # @param other [Number] A number to decide if it can be compared with this number.
355
+ # @return [Boolean] Whether or not this number can be compared with the other.
356
+ def comparable_to?(other)
357
+ operate(other, :+)
358
+ true
359
+ rescue Sass::UnitConversionError
360
+ false
361
+ end
362
+
363
+ # Returns a human readable representation of the units in this number.
364
+ # For complex units this takes the form of:
365
+ # numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
366
+ # @return [String] a string that represents the units in this number
367
+ def unit_str
368
+ rv = @numerator_units.sort.join("*")
369
+ if @denominator_units.any?
370
+ rv << "/"
371
+ rv << @denominator_units.sort.join("*")
372
+ end
373
+ rv
374
+ end
375
+
376
+ private
377
+
378
+ # @private
379
+ def self.round(num)
380
+ if num.is_a?(Float) && (num.infinite? || num.nan?)
381
+ num
382
+ elsif num % 1 == 0.0
383
+ num.to_i
384
+ else
385
+ ((num * precision_factor).round / precision_factor).to_f
386
+ end
387
+ end
388
+
389
+ OPERATIONS = [:+, :-, :<=, :<, :>, :>=, :%]
390
+
391
+ def operate(other, operation)
392
+ this = self
393
+ if OPERATIONS.include?(operation)
394
+ if unitless?
395
+ this = this.coerce(other.numerator_units, other.denominator_units)
396
+ else
397
+ other = other.coerce(@numerator_units, @denominator_units)
398
+ end
399
+ end
400
+ # avoid integer division
401
+ value = :/ == operation ? this.value.to_f : this.value
402
+ result = value.send(operation, other.value)
403
+
404
+ if result.is_a?(Numeric)
405
+ Number.new(result, *compute_units(this, other, operation))
406
+ else # Boolean op
407
+ Bool.new(result)
408
+ end
409
+ end
410
+
411
+ def coercion_factor(from_units, to_units)
412
+ # get a list of unmatched units
413
+ from_units, to_units = sans_common_units(from_units, to_units)
414
+
415
+ if from_units.size != to_units.size || !convertable?(from_units | to_units)
416
+ raise Sass::UnitConversionError.new(
417
+ "Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
418
+ end
419
+
420
+ from_units.zip(to_units).inject(1) {|m, p| m * conversion_factor(p[0], p[1])}
421
+ end
422
+
423
+ def compute_units(this, other, operation)
424
+ case operation
425
+ when :*
426
+ [this.numerator_units + other.numerator_units,
427
+ this.denominator_units + other.denominator_units]
428
+ when :/
429
+ [this.numerator_units + other.denominator_units,
430
+ this.denominator_units + other.numerator_units]
431
+ else
432
+ [this.numerator_units, this.denominator_units]
433
+ end
434
+ end
435
+
436
+ def normalize!
437
+ return if unitless?
438
+ @numerator_units, @denominator_units =
439
+ sans_common_units(@numerator_units, @denominator_units)
440
+
441
+ @denominator_units.each_with_index do |d, i|
442
+ if convertable?(d) && (u = @numerator_units.find(&method(:convertable?)))
443
+ @value /= conversion_factor(d, u)
444
+ @denominator_units.delete_at(i)
445
+ @numerator_units.delete_at(@numerator_units.index(u))
446
+ end
447
+ end
448
+ end
449
+
450
+ # This is the source data for all the unit logic. It's pre-processed to make
451
+ # it efficient to figure out whether a set of units is mutually compatible
452
+ # and what the conversion ratio is between two units.
453
+ #
454
+ # These come from http://www.w3.org/TR/2012/WD-css3-values-20120308/.
455
+ relative_sizes = [
456
+ {
457
+ 'in' => Rational(1),
458
+ 'cm' => Rational(1, 2.54),
459
+ 'pc' => Rational(1, 6),
460
+ 'mm' => Rational(1, 25.4),
461
+ 'pt' => Rational(1, 72),
462
+ 'px' => Rational(1, 96)
463
+ },
464
+ {
465
+ 'deg' => Rational(1, 360),
466
+ 'grad' => Rational(1, 400),
467
+ 'rad' => Rational(1, 2 * Math::PI),
468
+ 'turn' => Rational(1)
469
+ },
470
+ {
471
+ 's' => Rational(1),
472
+ 'ms' => Rational(1, 1000)
473
+ },
474
+ {
475
+ 'Hz' => Rational(1),
476
+ 'kHz' => Rational(1000)
477
+ },
478
+ {
479
+ 'dpi' => Rational(1),
480
+ 'dpcm' => Rational(1, 2.54),
481
+ 'dppx' => Rational(1, 96)
482
+ }
483
+ ]
484
+
485
+ # A hash from each known unit to the set of units that it's mutually
486
+ # convertible with.
487
+ MUTUALLY_CONVERTIBLE = {}
488
+ relative_sizes.map do |values|
489
+ set = values.keys.to_set
490
+ values.keys.each {|name| MUTUALLY_CONVERTIBLE[name] = set}
491
+ end
492
+
493
+ # A two-dimensional hash from two units to the conversion ratio between
494
+ # them. Multiply `X` by `CONVERSION_TABLE[X][Y]` to convert it to `Y`.
495
+ CONVERSION_TABLE = {}
496
+ relative_sizes.each do |values|
497
+ values.each do |(name1, value1)|
498
+ CONVERSION_TABLE[name1] ||= {}
499
+ values.each do |(name2, value2)|
500
+ value = value1 / value2
501
+ CONVERSION_TABLE[name1][name2] = value.denominator == 1 ? value.to_i : value.to_f
502
+ end
503
+ end
504
+ end
505
+
506
+ def conversion_factor(from_unit, to_unit)
507
+ CONVERSION_TABLE[from_unit][to_unit]
508
+ end
509
+
510
+ def convertable?(units)
511
+ units = Array(units).to_set
512
+ return true if units.empty?
513
+ return false unless (mutually_convertible = MUTUALLY_CONVERTIBLE[units.first])
514
+ units.subset?(mutually_convertible)
515
+ end
516
+
517
+ def sans_common_units(units1, units2)
518
+ units2 = units2.dup
519
+ # Can't just use -, because we want px*px to coerce properly to px*mm
520
+ units1 = units1.map do |u|
521
+ j = units2.index(u)
522
+ next u unless j
523
+ units2.delete_at(j)
524
+ nil
525
+ end
526
+ units1.compact!
527
+ return units1, units2
528
+ end
529
+ end
530
+ end