Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 8835 Bytes
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(120, 100%, 50%) |
6 | # |
7 | # and it will call {Sass::Script::Functions#hsl}. |
8 | # |
9 | # The following functions are provided: |
10 | # |
11 | # \{#hsl} |
12 | # : Converts an `hsl(hue, saturation, lightness)` triplet into a color. |
13 | # |
14 | # \{#rgb} |
15 | # : Converts an `rgb(red, green, blue)` triplet into a color. |
16 | # |
17 | # \{#percentage} |
18 | # : Converts a unitless number to a percentage. |
19 | # |
20 | # \{#round} |
21 | # : Rounds a number to the nearest whole number. |
22 | # |
23 | # \{#ceil} |
24 | # : Rounds a number up to the nearest whole number. |
25 | # |
26 | # \{#floor} |
27 | # : Rounds a number down to the nearest whole number. |
28 | # |
29 | # \{#abs} |
30 | # : Returns the absolute value of a number. |
31 | # |
32 | # These functions are described in more detail below. |
33 | # |
34 | # ## Adding Custom Functions |
35 | # |
36 | # New Sass functions can be added by adding Ruby methods to this module. |
37 | # For example: |
38 | # |
39 | # module Sass::Script::Functions |
40 | # def reverse(string) |
41 | # assert_type string, :String |
42 | # Sass::Script::String.new(string.value.reverse) |
43 | # end |
44 | # end |
45 | # |
46 | # There are a few things to keep in mind when modifying this module. |
47 | # First of all, the arguments passed are {Sass::Script::Literal} objects. |
48 | # Literal objects are also expected to be returned. |
49 | # This means that Ruby values must be unwrapped and wrapped. |
50 | # |
51 | # Most Literal objects support the {Sass::Script::Literal#value value} accessor |
52 | # for getting their Ruby values. |
53 | # Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb}. |
54 | # |
55 | # Second, making Ruby functions accessible from Sass introduces the temptation |
56 | # to do things like database access within stylesheets. |
57 | # This is generally a bad idea; |
58 | # since Sass files are by default only compiled once, |
59 | # dynamic code is not a great fit. |
60 | # |
61 | # If you really, really need to compile Sass on each request, |
62 | # first make sure you have adequate caching set up. |
63 | # Then you can use {Sass::Engine} to render the code, |
64 | # using the {file:SASS_REFERENCE.md#custom-option `options` parameter} |
65 | # to pass in data that {EvaluationContext#options can be accessed} |
66 | # from your Sass functions. |
67 | # |
68 | # Within one of the functions in this module, |
69 | # methods of {EvaluationContext} can be used. |
70 | module Functions |
71 | # The context in which methods in {Script::Functions} are evaluated. |
72 | # That means that all instance methods of {EvaluationContext} |
73 | # are available to use in functions. |
74 | class EvaluationContext |
75 | # The options hash for the {Sass::Engine} that is processing the function call |
76 | # |
77 | # @return [{Symbol => Object}] |
78 | attr_reader :options |
79 | |
80 | # @param options [{Symbol => Object}] See \{#options} |
81 | def initialize(options) |
82 | @options = options |
83 | |
84 | # We need to include this individually in each instance |
85 | # because of an icky Ruby restriction |
86 | class << self; include Sass::Script::Functions; end |
87 | end |
88 | |
89 | # Asserts that the type of a given SassScript value |
90 | # is the expected type (designated by a symbol). |
91 | # For example: |
92 | # |
93 | # assert_type value, :String |
94 | # assert_type value, :Number |
95 | # |
96 | # Valid types are `:Bool`, `:Color`, `:Number`, and `:String`. |
97 | # |
98 | # @param value [Sass::Script::Literal] A SassScript value |
99 | # @param type [Symbol] The name of the type the value is expected to be |
100 | def assert_type(value, type) |
101 | return if value.is_a?(Sass::Script.const_get(type)) |
102 | raise ArgumentError.new("#{value.inspect} is not a #{type.to_s.downcase}") |
103 | end |
104 | end |
105 | |
106 | instance_methods.each { |m| undef_method m unless m.to_s =~ /^__/ } |
107 | |
108 | |
109 | # Creates a {Color} object from red, green, and blue values. |
110 | # @param red |
111 | # A number between 0 and 255 inclusive, |
112 | # or between 0% and 100% inclusive |
113 | # @param green |
114 | # A number between 0 and 255 inclusive, |
115 | # or between 0% and 100% inclusive |
116 | # @param blue |
117 | # A number between 0 and 255 inclusive, |
118 | # or between 0% and 100% inclusive |
119 | def rgb(red, green, blue) |
120 | assert_type red, :Number |
121 | assert_type green, :Number |
122 | assert_type blue, :Number |
123 | |
124 | rgb = [red, green, blue].map do |c| |
125 | v = c.value |
126 | if c.numerator_units == ["%"] && c.denominator_units.empty? |
127 | next v * 255 / 100.0 if (0..100).include?(v) |
128 | raise ArgumentError.new("Color value #{c} must be between 0% and 100% inclusive") |
129 | else |
130 | next v if (0..255).include?(v) |
131 | raise ArgumentError.new("Color value #{v} must be between 0 and 255 inclusive") |
132 | end |
133 | end |
134 | Color.new(rgb) |
135 | end |
136 | |
137 | # Creates a {Color} object from hue, saturation, and lightness. |
138 | # Uses the algorithm from the [CSS3 spec](http://www.w3.org/TR/css3-color/#hsl-color). |
139 | # |
140 | # @param hue [Number] The hue of the color. |
141 | # Should be between 0 and 360 degrees, inclusive |
142 | # @param saturation [Number] The saturation of the color. |
143 | # Must be between `0%` and `100%`, inclusive |
144 | # @param lightness [Number] The lightness of the color. |
145 | # Must be between `0%` and `100%`, inclusive |
146 | # @return [Color] The resulting color |
147 | # @raise [ArgumentError] if `saturation` or `lightness` are out of bounds |
148 | def hsl(hue, saturation, lightness) |
149 | assert_type hue, :Number |
150 | assert_type saturation, :Number |
151 | assert_type lightness, :Number |
152 | |
153 | original_s = saturation |
154 | original_l = lightness |
155 | # This algorithm is from http://www.w3.org/TR/css3-color#hsl-color |
156 | h, s, l = [hue, saturation, lightness].map { |a| a.value } |
157 | raise ArgumentError.new("Saturation #{s} must be between 0% and 100%") if s < 0 || s > 100 |
158 | raise ArgumentError.new("Lightness #{l} must be between 0% and 100%") if l < 0 || l > 100 |
159 | |
160 | h = (h % 360) / 360.0 |
161 | s /= 100.0 |
162 | l /= 100.0 |
163 | |
164 | m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s |
165 | m1 = l * 2 - m2 |
166 | Color.new([hue_to_rgb(m1, m2, h + 1.0/3), |
167 | hue_to_rgb(m1, m2, h), |
168 | hue_to_rgb(m1, m2, h - 1.0/3)].map { |c| (c * 0xff).round }) |
169 | end |
170 | |
171 | # Converts a decimal number to a percentage. |
172 | # For example: |
173 | # |
174 | # percentage(100px / 50px) => 200% |
175 | # |
176 | # @param value [Number] The decimal number to convert to a percentage |
177 | # @return [Number] The percentage |
178 | # @raise [ArgumentError] If `value` isn't a unitless number |
179 | def percentage(value) |
180 | unless value.is_a?(Sass::Script::Number) && value.unitless? |
181 | raise ArgumentError.new("#{value.inspect} is not a unitless number") |
182 | end |
183 | Sass::Script::Number.new(value.value * 100, ['%']) |
184 | end |
185 | |
186 | # Rounds a number to the nearest whole number. |
187 | # For example: |
188 | # |
189 | # round(10.4px) => 10px |
190 | # round(10.6px) => 11px |
191 | # |
192 | # @param value [Number] The number |
193 | # @return [Number] The rounded number |
194 | # @raise [Sass::SyntaxError] if `value` isn't a number |
195 | def round(value) |
196 | numeric_transformation(value) {|n| n.round} |
197 | end |
198 | |
199 | # Rounds a number up to the nearest whole number. |
200 | # For example: |
201 | # |
202 | # ciel(10.4px) => 11px |
203 | # ciel(10.6px) => 11px |
204 | # |
205 | # @param value [Number] The number |
206 | # @return [Number] The rounded number |
207 | # @raise [Sass::SyntaxError] if `value` isn't a number |
208 | def ceil(value) |
209 | numeric_transformation(value) {|n| n.ceil} |
210 | end |
211 | |
212 | # Rounds down to the nearest whole number. |
213 | # For example: |
214 | # |
215 | # floor(10.4px) => 10px |
216 | # floor(10.6px) => 10px |
217 | # |
218 | # @param value [Number] The number |
219 | # @return [Number] The rounded number |
220 | # @raise [Sass::SyntaxError] if `value` isn't a number |
221 | def floor(value) |
222 | numeric_transformation(value) {|n| n.floor} |
223 | end |
224 | |
225 | # Finds the absolute value of a number. |
226 | # For example: |
227 | # |
228 | # abs(10px) => 10px |
229 | # abs(-10px) => 10px |
230 | # |
231 | # @param value [Number] The number |
232 | # @return [Number] The absolute value |
233 | # @raise [Sass::SyntaxError] if `value` isn't a number |
234 | def abs(value) |
235 | numeric_transformation(value) {|n| n.abs} |
236 | end |
237 | |
238 | private |
239 | |
240 | # This method implements the pattern of transforming a numeric value into |
241 | # another numeric value with the same units. |
242 | # It yields a number to a block to perform the operation and return a number |
243 | def numeric_transformation(value) |
244 | assert_type value, :Number |
245 | Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units) |
246 | end |
247 | |
248 | def hue_to_rgb(m1, m2, h) |
249 | h += 1 if h < 0 |
250 | h -= 1 if h > 1 |
251 | return m1 + (m2 - m1) * h * 6 if h * 6 < 1 |
252 | return m2 if h * 2 < 1 |
253 | return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2 |
254 | return m1 |
255 | end |
256 | end |
257 | end |