xass 0.1.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 (242) 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 +201 -0
  6. data/Rakefile +349 -0
  7. data/VERSION +1 -0
  8. data/VERSION_NAME +1 -0
  9. data/bin/push +13 -0
  10. data/bin/sass +13 -0
  11. data/bin/sass-convert +12 -0
  12. data/bin/scss +13 -0
  13. data/extra/update_watch.rb +13 -0
  14. data/init.rb +18 -0
  15. data/lib/sass/cache_stores/base.rb +88 -0
  16. data/lib/sass/cache_stores/chain.rb +33 -0
  17. data/lib/sass/cache_stores/filesystem.rb +64 -0
  18. data/lib/sass/cache_stores/memory.rb +47 -0
  19. data/lib/sass/cache_stores/null.rb +25 -0
  20. data/lib/sass/cache_stores.rb +15 -0
  21. data/lib/sass/callbacks.rb +66 -0
  22. data/lib/sass/css.rb +409 -0
  23. data/lib/sass/engine.rb +930 -0
  24. data/lib/sass/environment.rb +101 -0
  25. data/lib/sass/error.rb +201 -0
  26. data/lib/sass/exec.rb +707 -0
  27. data/lib/sass/importers/base.rb +139 -0
  28. data/lib/sass/importers/filesystem.rb +186 -0
  29. data/lib/sass/importers.rb +22 -0
  30. data/lib/sass/logger/base.rb +32 -0
  31. data/lib/sass/logger/log_level.rb +49 -0
  32. data/lib/sass/logger.rb +15 -0
  33. data/lib/sass/media.rb +213 -0
  34. data/lib/sass/plugin/compiler.rb +406 -0
  35. data/lib/sass/plugin/configuration.rb +123 -0
  36. data/lib/sass/plugin/generic.rb +15 -0
  37. data/lib/sass/plugin/merb.rb +48 -0
  38. data/lib/sass/plugin/rack.rb +60 -0
  39. data/lib/sass/plugin/rails.rb +47 -0
  40. data/lib/sass/plugin/staleness_checker.rb +199 -0
  41. data/lib/sass/plugin.rb +133 -0
  42. data/lib/sass/railtie.rb +10 -0
  43. data/lib/sass/repl.rb +57 -0
  44. data/lib/sass/root.rb +7 -0
  45. data/lib/sass/script/arg_list.rb +52 -0
  46. data/lib/sass/script/bool.rb +18 -0
  47. data/lib/sass/script/color.rb +606 -0
  48. data/lib/sass/script/css_lexer.rb +29 -0
  49. data/lib/sass/script/css_parser.rb +31 -0
  50. data/lib/sass/script/funcall.rb +245 -0
  51. data/lib/sass/script/functions.rb +1543 -0
  52. data/lib/sass/script/interpolation.rb +79 -0
  53. data/lib/sass/script/lexer.rb +345 -0
  54. data/lib/sass/script/list.rb +85 -0
  55. data/lib/sass/script/literal.rb +221 -0
  56. data/lib/sass/script/node.rb +99 -0
  57. data/lib/sass/script/null.rb +37 -0
  58. data/lib/sass/script/number.rb +453 -0
  59. data/lib/sass/script/operation.rb +110 -0
  60. data/lib/sass/script/parser.rb +502 -0
  61. data/lib/sass/script/string.rb +51 -0
  62. data/lib/sass/script/string_interpolation.rb +103 -0
  63. data/lib/sass/script/unary_operation.rb +69 -0
  64. data/lib/sass/script/variable.rb +58 -0
  65. data/lib/sass/script.rb +39 -0
  66. data/lib/sass/scss/css_parser.rb +36 -0
  67. data/lib/sass/scss/parser.rb +1180 -0
  68. data/lib/sass/scss/rx.rb +133 -0
  69. data/lib/sass/scss/script_lexer.rb +15 -0
  70. data/lib/sass/scss/script_parser.rb +25 -0
  71. data/lib/sass/scss/static_parser.rb +54 -0
  72. data/lib/sass/scss.rb +16 -0
  73. data/lib/sass/selector/abstract_sequence.rb +94 -0
  74. data/lib/sass/selector/comma_sequence.rb +92 -0
  75. data/lib/sass/selector/sequence.rb +507 -0
  76. data/lib/sass/selector/simple.rb +119 -0
  77. data/lib/sass/selector/simple_sequence.rb +215 -0
  78. data/lib/sass/selector.rb +452 -0
  79. data/lib/sass/shared.rb +76 -0
  80. data/lib/sass/supports.rb +229 -0
  81. data/lib/sass/tree/charset_node.rb +22 -0
  82. data/lib/sass/tree/comment_node.rb +82 -0
  83. data/lib/sass/tree/content_node.rb +9 -0
  84. data/lib/sass/tree/css_import_node.rb +60 -0
  85. data/lib/sass/tree/debug_node.rb +18 -0
  86. data/lib/sass/tree/directive_node.rb +42 -0
  87. data/lib/sass/tree/each_node.rb +24 -0
  88. data/lib/sass/tree/extend_node.rb +36 -0
  89. data/lib/sass/tree/for_node.rb +36 -0
  90. data/lib/sass/tree/function_node.rb +34 -0
  91. data/lib/sass/tree/if_node.rb +52 -0
  92. data/lib/sass/tree/import_node.rb +75 -0
  93. data/lib/sass/tree/media_node.rb +58 -0
  94. data/lib/sass/tree/mixin_def_node.rb +38 -0
  95. data/lib/sass/tree/mixin_node.rb +39 -0
  96. data/lib/sass/tree/node.rb +196 -0
  97. data/lib/sass/tree/prop_node.rb +152 -0
  98. data/lib/sass/tree/return_node.rb +18 -0
  99. data/lib/sass/tree/root_node.rb +78 -0
  100. data/lib/sass/tree/rule_node.rb +132 -0
  101. data/lib/sass/tree/supports_node.rb +51 -0
  102. data/lib/sass/tree/trace_node.rb +32 -0
  103. data/lib/sass/tree/variable_node.rb +30 -0
  104. data/lib/sass/tree/visitors/base.rb +75 -0
  105. data/lib/sass/tree/visitors/check_nesting.rb +147 -0
  106. data/lib/sass/tree/visitors/convert.rb +316 -0
  107. data/lib/sass/tree/visitors/cssize.rb +241 -0
  108. data/lib/sass/tree/visitors/deep_copy.rb +102 -0
  109. data/lib/sass/tree/visitors/extend.rb +68 -0
  110. data/lib/sass/tree/visitors/perform.rb +446 -0
  111. data/lib/sass/tree/visitors/set_options.rb +125 -0
  112. data/lib/sass/tree/visitors/to_css.rb +228 -0
  113. data/lib/sass/tree/warn_node.rb +18 -0
  114. data/lib/sass/tree/while_node.rb +18 -0
  115. data/lib/sass/util/multibyte_string_scanner.rb +155 -0
  116. data/lib/sass/util/subset_map.rb +109 -0
  117. data/lib/sass/util/test.rb +10 -0
  118. data/lib/sass/util.rb +948 -0
  119. data/lib/sass/version.rb +126 -0
  120. data/lib/sass.rb +95 -0
  121. data/rails/init.rb +1 -0
  122. data/test/Gemfile +3 -0
  123. data/test/Gemfile.lock +10 -0
  124. data/test/sass/cache_test.rb +89 -0
  125. data/test/sass/callbacks_test.rb +61 -0
  126. data/test/sass/conversion_test.rb +1760 -0
  127. data/test/sass/css2sass_test.rb +458 -0
  128. data/test/sass/data/hsl-rgb.txt +319 -0
  129. data/test/sass/engine_test.rb +3244 -0
  130. data/test/sass/exec_test.rb +86 -0
  131. data/test/sass/extend_test.rb +1482 -0
  132. data/test/sass/fixtures/test_staleness_check_across_importers.css +1 -0
  133. data/test/sass/fixtures/test_staleness_check_across_importers.scss +1 -0
  134. data/test/sass/functions_test.rb +1139 -0
  135. data/test/sass/importer_test.rb +192 -0
  136. data/test/sass/logger_test.rb +58 -0
  137. data/test/sass/mock_importer.rb +49 -0
  138. data/test/sass/more_results/more1.css +9 -0
  139. data/test/sass/more_results/more1_with_line_comments.css +26 -0
  140. data/test/sass/more_results/more_import.css +29 -0
  141. data/test/sass/more_templates/_more_partial.sass +2 -0
  142. data/test/sass/more_templates/more1.sass +23 -0
  143. data/test/sass/more_templates/more_import.sass +11 -0
  144. data/test/sass/plugin_test.rb +564 -0
  145. data/test/sass/results/alt.css +4 -0
  146. data/test/sass/results/basic.css +9 -0
  147. data/test/sass/results/cached_import_option.css +3 -0
  148. data/test/sass/results/compact.css +5 -0
  149. data/test/sass/results/complex.css +86 -0
  150. data/test/sass/results/compressed.css +1 -0
  151. data/test/sass/results/expanded.css +19 -0
  152. data/test/sass/results/filename_fn.css +3 -0
  153. data/test/sass/results/if.css +3 -0
  154. data/test/sass/results/import.css +31 -0
  155. data/test/sass/results/import_charset.css +5 -0
  156. data/test/sass/results/import_charset_1_8.css +5 -0
  157. data/test/sass/results/import_charset_ibm866.css +5 -0
  158. data/test/sass/results/import_content.css +1 -0
  159. data/test/sass/results/line_numbers.css +49 -0
  160. data/test/sass/results/mixins.css +95 -0
  161. data/test/sass/results/multiline.css +24 -0
  162. data/test/sass/results/nested.css +22 -0
  163. data/test/sass/results/options.css +1 -0
  164. data/test/sass/results/parent_ref.css +13 -0
  165. data/test/sass/results/script.css +16 -0
  166. data/test/sass/results/scss_import.css +31 -0
  167. data/test/sass/results/scss_importee.css +2 -0
  168. data/test/sass/results/subdir/nested_subdir/nested_subdir.css +1 -0
  169. data/test/sass/results/subdir/subdir.css +3 -0
  170. data/test/sass/results/units.css +11 -0
  171. data/test/sass/results/warn.css +0 -0
  172. data/test/sass/results/warn_imported.css +0 -0
  173. data/test/sass/script_conversion_test.rb +299 -0
  174. data/test/sass/script_test.rb +622 -0
  175. data/test/sass/scss/css_test.rb +1100 -0
  176. data/test/sass/scss/rx_test.rb +156 -0
  177. data/test/sass/scss/scss_test.rb +2106 -0
  178. data/test/sass/scss/test_helper.rb +37 -0
  179. data/test/sass/templates/_cached_import_option_partial.scss +1 -0
  180. data/test/sass/templates/_double_import_loop2.sass +1 -0
  181. data/test/sass/templates/_filename_fn_import.scss +11 -0
  182. data/test/sass/templates/_imported_charset_ibm866.sass +4 -0
  183. data/test/sass/templates/_imported_charset_utf8.sass +4 -0
  184. data/test/sass/templates/_imported_content.sass +3 -0
  185. data/test/sass/templates/_partial.sass +2 -0
  186. data/test/sass/templates/_same_name_different_partiality.scss +1 -0
  187. data/test/sass/templates/alt.sass +16 -0
  188. data/test/sass/templates/basic.sass +23 -0
  189. data/test/sass/templates/bork1.sass +2 -0
  190. data/test/sass/templates/bork2.sass +2 -0
  191. data/test/sass/templates/bork3.sass +2 -0
  192. data/test/sass/templates/bork4.sass +2 -0
  193. data/test/sass/templates/bork5.sass +3 -0
  194. data/test/sass/templates/cached_import_option.scss +3 -0
  195. data/test/sass/templates/compact.sass +17 -0
  196. data/test/sass/templates/complex.sass +305 -0
  197. data/test/sass/templates/compressed.sass +15 -0
  198. data/test/sass/templates/double_import_loop1.sass +1 -0
  199. data/test/sass/templates/expanded.sass +17 -0
  200. data/test/sass/templates/filename_fn.scss +18 -0
  201. data/test/sass/templates/if.sass +11 -0
  202. data/test/sass/templates/import.sass +12 -0
  203. data/test/sass/templates/import_charset.sass +9 -0
  204. data/test/sass/templates/import_charset_1_8.sass +6 -0
  205. data/test/sass/templates/import_charset_ibm866.sass +11 -0
  206. data/test/sass/templates/import_content.sass +4 -0
  207. data/test/sass/templates/importee.less +2 -0
  208. data/test/sass/templates/importee.sass +19 -0
  209. data/test/sass/templates/line_numbers.sass +13 -0
  210. data/test/sass/templates/mixin_bork.sass +5 -0
  211. data/test/sass/templates/mixins.sass +76 -0
  212. data/test/sass/templates/multiline.sass +20 -0
  213. data/test/sass/templates/nested.sass +25 -0
  214. data/test/sass/templates/nested_bork1.sass +2 -0
  215. data/test/sass/templates/nested_bork2.sass +2 -0
  216. data/test/sass/templates/nested_bork3.sass +2 -0
  217. data/test/sass/templates/nested_bork4.sass +2 -0
  218. data/test/sass/templates/nested_import.sass +2 -0
  219. data/test/sass/templates/nested_mixin_bork.sass +6 -0
  220. data/test/sass/templates/options.sass +2 -0
  221. data/test/sass/templates/parent_ref.sass +25 -0
  222. data/test/sass/templates/same_name_different_ext.sass +2 -0
  223. data/test/sass/templates/same_name_different_ext.scss +1 -0
  224. data/test/sass/templates/same_name_different_partiality.scss +1 -0
  225. data/test/sass/templates/script.sass +101 -0
  226. data/test/sass/templates/scss_import.scss +11 -0
  227. data/test/sass/templates/scss_importee.scss +1 -0
  228. data/test/sass/templates/single_import_loop.sass +1 -0
  229. data/test/sass/templates/subdir/import_up1.scss +1 -0
  230. data/test/sass/templates/subdir/import_up2.scss +1 -0
  231. data/test/sass/templates/subdir/nested_subdir/_nested_partial.sass +2 -0
  232. data/test/sass/templates/subdir/nested_subdir/nested_subdir.sass +3 -0
  233. data/test/sass/templates/subdir/subdir.sass +6 -0
  234. data/test/sass/templates/units.sass +11 -0
  235. data/test/sass/templates/warn.sass +3 -0
  236. data/test/sass/templates/warn_imported.sass +4 -0
  237. data/test/sass/test_helper.rb +8 -0
  238. data/test/sass/util/multibyte_string_scanner_test.rb +147 -0
  239. data/test/sass/util/subset_map_test.rb +91 -0
  240. data/test/sass/util_test.rb +382 -0
  241. data/test/test_helper.rb +80 -0
  242. metadata +354 -0
@@ -0,0 +1,606 @@
1
+ require 'sass/script/literal'
2
+
3
+ module Sass::Script
4
+ # A SassScript object representing a CSS color.
5
+ #
6
+ # A color may be represented internally as RGBA, HSLA, or both.
7
+ # It's originally represented as whatever its input is;
8
+ # if it's created with RGB values, it's represented as RGBA,
9
+ # and if it's created with HSL values, it's represented as HSLA.
10
+ # Once a property is accessed that requires the other representation --
11
+ # for example, \{#red} for an HSL color --
12
+ # that component is calculated and cached.
13
+ #
14
+ # The alpha channel of a color is independent of its RGB or HSL representation.
15
+ # It's always stored, as 1 if nothing else is specified.
16
+ # If only the alpha channel is modified using \{#with},
17
+ # the cached RGB and HSL values are retained.
18
+ class Color < Literal
19
+ class << self; include Sass::Util; end
20
+
21
+ # A hash from color names to `[red, green, blue]` value arrays.
22
+ COLOR_NAMES = map_vals({
23
+ 'aliceblue' => 0xf0f8ff,
24
+ 'antiquewhite' => 0xfaebd7,
25
+ 'aqua' => 0x00ffff,
26
+ 'aquamarine' => 0x7fffd4,
27
+ 'azure' => 0xf0ffff,
28
+ 'beige' => 0xf5f5dc,
29
+ 'bisque' => 0xffe4c4,
30
+ 'black' => 0x000000,
31
+ 'blanchedalmond' => 0xffebcd,
32
+ 'blue' => 0x0000ff,
33
+ 'blueviolet' => 0x8a2be2,
34
+ 'brown' => 0xa52a2a,
35
+ 'burlywood' => 0xdeb887,
36
+ 'cadetblue' => 0x5f9ea0,
37
+ 'chartreuse' => 0x7fff00,
38
+ 'chocolate' => 0xd2691e,
39
+ 'coral' => 0xff7f50,
40
+ 'cornflowerblue' => 0x6495ed,
41
+ 'cornsilk' => 0xfff8dc,
42
+ 'crimson' => 0xdc143c,
43
+ 'cyan' => 0x00ffff,
44
+ 'darkblue' => 0x00008b,
45
+ 'darkcyan' => 0x008b8b,
46
+ 'darkgoldenrod' => 0xb8860b,
47
+ 'darkgray' => 0xa9a9a9,
48
+ 'darkgrey' => 0xa9a9a9,
49
+ 'darkgreen' => 0x006400,
50
+ 'darkkhaki' => 0xbdb76b,
51
+ 'darkmagenta' => 0x8b008b,
52
+ 'darkolivegreen' => 0x556b2f,
53
+ 'darkorange' => 0xff8c00,
54
+ 'darkorchid' => 0x9932cc,
55
+ 'darkred' => 0x8b0000,
56
+ 'darksalmon' => 0xe9967a,
57
+ 'darkseagreen' => 0x8fbc8f,
58
+ 'darkslateblue' => 0x483d8b,
59
+ 'darkslategray' => 0x2f4f4f,
60
+ 'darkslategrey' => 0x2f4f4f,
61
+ 'darkturquoise' => 0x00ced1,
62
+ 'darkviolet' => 0x9400d3,
63
+ 'deeppink' => 0xff1493,
64
+ 'deepskyblue' => 0x00bfff,
65
+ 'dimgray' => 0x696969,
66
+ 'dimgrey' => 0x696969,
67
+ 'dodgerblue' => 0x1e90ff,
68
+ 'firebrick' => 0xb22222,
69
+ 'floralwhite' => 0xfffaf0,
70
+ 'forestgreen' => 0x228b22,
71
+ 'fuchsia' => 0xff00ff,
72
+ 'gainsboro' => 0xdcdcdc,
73
+ 'ghostwhite' => 0xf8f8ff,
74
+ 'gold' => 0xffd700,
75
+ 'goldenrod' => 0xdaa520,
76
+ 'gray' => 0x808080,
77
+ 'green' => 0x008000,
78
+ 'greenyellow' => 0xadff2f,
79
+ 'honeydew' => 0xf0fff0,
80
+ 'hotpink' => 0xff69b4,
81
+ 'indianred' => 0xcd5c5c,
82
+ 'indigo' => 0x4b0082,
83
+ 'ivory' => 0xfffff0,
84
+ 'khaki' => 0xf0e68c,
85
+ 'lavender' => 0xe6e6fa,
86
+ 'lavenderblush' => 0xfff0f5,
87
+ 'lawngreen' => 0x7cfc00,
88
+ 'lemonchiffon' => 0xfffacd,
89
+ 'lightblue' => 0xadd8e6,
90
+ 'lightcoral' => 0xf08080,
91
+ 'lightcyan' => 0xe0ffff,
92
+ 'lightgoldenrodyellow' => 0xfafad2,
93
+ 'lightgreen' => 0x90ee90,
94
+ 'lightgray' => 0xd3d3d3,
95
+ 'lightgrey' => 0xd3d3d3,
96
+ 'lightpink' => 0xffb6c1,
97
+ 'lightsalmon' => 0xffa07a,
98
+ 'lightseagreen' => 0x20b2aa,
99
+ 'lightskyblue' => 0x87cefa,
100
+ 'lightslategray' => 0x778899,
101
+ 'lightslategrey' => 0x778899,
102
+ 'lightsteelblue' => 0xb0c4de,
103
+ 'lightyellow' => 0xffffe0,
104
+ 'lime' => 0x00ff00,
105
+ 'limegreen' => 0x32cd32,
106
+ 'linen' => 0xfaf0e6,
107
+ 'magenta' => 0xff00ff,
108
+ 'maroon' => 0x800000,
109
+ 'mediumaquamarine' => 0x66cdaa,
110
+ 'mediumblue' => 0x0000cd,
111
+ 'mediumorchid' => 0xba55d3,
112
+ 'mediumpurple' => 0x9370db,
113
+ 'mediumseagreen' => 0x3cb371,
114
+ 'mediumslateblue' => 0x7b68ee,
115
+ 'mediumspringgreen' => 0x00fa9a,
116
+ 'mediumturquoise' => 0x48d1cc,
117
+ 'mediumvioletred' => 0xc71585,
118
+ 'midnightblue' => 0x191970,
119
+ 'mintcream' => 0xf5fffa,
120
+ 'mistyrose' => 0xffe4e1,
121
+ 'moccasin' => 0xffe4b5,
122
+ 'navajowhite' => 0xffdead,
123
+ 'navy' => 0x000080,
124
+ 'oldlace' => 0xfdf5e6,
125
+ 'olive' => 0x808000,
126
+ 'olivedrab' => 0x6b8e23,
127
+ 'orange' => 0xffa500,
128
+ 'orangered' => 0xff4500,
129
+ 'orchid' => 0xda70d6,
130
+ 'palegoldenrod' => 0xeee8aa,
131
+ 'palegreen' => 0x98fb98,
132
+ 'paleturquoise' => 0xafeeee,
133
+ 'palevioletred' => 0xdb7093,
134
+ 'papayawhip' => 0xffefd5,
135
+ 'peachpuff' => 0xffdab9,
136
+ 'peru' => 0xcd853f,
137
+ 'pink' => 0xffc0cb,
138
+ 'plum' => 0xdda0dd,
139
+ 'powderblue' => 0xb0e0e6,
140
+ 'purple' => 0x800080,
141
+ 'red' => 0xff0000,
142
+ 'rosybrown' => 0xbc8f8f,
143
+ 'royalblue' => 0x4169e1,
144
+ 'saddlebrown' => 0x8b4513,
145
+ 'salmon' => 0xfa8072,
146
+ 'sandybrown' => 0xf4a460,
147
+ 'seagreen' => 0x2e8b57,
148
+ 'seashell' => 0xfff5ee,
149
+ 'sienna' => 0xa0522d,
150
+ 'silver' => 0xc0c0c0,
151
+ 'skyblue' => 0x87ceeb,
152
+ 'slateblue' => 0x6a5acd,
153
+ 'slategray' => 0x708090,
154
+ 'slategrey' => 0x708090,
155
+ 'snow' => 0xfffafa,
156
+ 'springgreen' => 0x00ff7f,
157
+ 'steelblue' => 0x4682b4,
158
+ 'tan' => 0xd2b48c,
159
+ 'teal' => 0x008080,
160
+ 'thistle' => 0xd8bfd8,
161
+ 'tomato' => 0xff6347,
162
+ 'turquoise' => 0x40e0d0,
163
+ 'violet' => 0xee82ee,
164
+ 'wheat' => 0xf5deb3,
165
+ 'white' => 0xffffff,
166
+ 'whitesmoke' => 0xf5f5f5,
167
+ 'yellow' => 0xffff00,
168
+ 'yellowgreen' => 0x9acd32
169
+ }) {|color| (0..2).map {|n| color >> (n << 3) & 0xff}.reverse}
170
+
171
+ # A hash from `[red, green, blue]` value arrays to color names.
172
+ COLOR_NAMES_REVERSE = map_hash(hash_to_a(COLOR_NAMES)) {|k, v| [v, k]}
173
+
174
+ # Constructs an RGB or HSL color object,
175
+ # optionally with an alpha channel.
176
+ #
177
+ # The RGB values must be between 0 and 255.
178
+ # The saturation and lightness values must be between 0 and 100.
179
+ # The alpha value must be between 0 and 1.
180
+ #
181
+ # @raise [Sass::SyntaxError] if any color value isn't in the specified range
182
+ #
183
+ # @overload initialize(attrs)
184
+ # The attributes are specified as a hash.
185
+ # This hash must contain either `:hue`, `:saturation`, and `:value` keys,
186
+ # or `:red`, `:green`, and `:blue` keys.
187
+ # It cannot contain both HSL and RGB keys.
188
+ # It may also optionally contain an `:alpha` key.
189
+ #
190
+ # @param attrs [{Symbol => Numeric}] A hash of color attributes to values
191
+ # @raise [ArgumentError] if not enough attributes are specified,
192
+ # or both RGB and HSL attributes are specified
193
+ #
194
+ # @overload initialize(rgba)
195
+ # The attributes are specified as an array.
196
+ # This overload only supports RGB or RGBA colors.
197
+ #
198
+ # @param rgba [Array<Numeric>] A three- or four-element array
199
+ # of the red, green, blue, and optionally alpha values (respectively)
200
+ # of the color
201
+ # @raise [ArgumentError] if not enough attributes are specified
202
+ def initialize(attrs, allow_both_rgb_and_hsl = false)
203
+ super(nil)
204
+
205
+ if attrs.is_a?(Array)
206
+ unless (3..4).include?(attrs.size)
207
+ raise ArgumentError.new("Color.new(array) expects a three- or four-element array")
208
+ end
209
+
210
+ red, green, blue = attrs[0...3].map {|c| c.to_i}
211
+ @attrs = {:red => red, :green => green, :blue => blue}
212
+ @attrs[:alpha] = attrs[3] ? attrs[3].to_f : 1
213
+ else
214
+ attrs = attrs.reject {|k, v| v.nil?}
215
+ hsl = [:hue, :saturation, :lightness] & attrs.keys
216
+ rgb = [:red, :green, :blue] & attrs.keys
217
+ if !allow_both_rgb_and_hsl && !hsl.empty? && !rgb.empty?
218
+ raise ArgumentError.new("Color.new(hash) may not have both HSL and RGB keys specified")
219
+ elsif hsl.empty? && rgb.empty?
220
+ raise ArgumentError.new("Color.new(hash) must have either HSL or RGB keys specified")
221
+ elsif !hsl.empty? && hsl.size != 3
222
+ raise ArgumentError.new("Color.new(hash) must have all three HSL values specified")
223
+ elsif !rgb.empty? && rgb.size != 3
224
+ raise ArgumentError.new("Color.new(hash) must have all three RGB values specified")
225
+ end
226
+
227
+ @attrs = attrs
228
+ @attrs[:hue] %= 360 if @attrs[:hue]
229
+ @attrs[:alpha] ||= 1
230
+ end
231
+
232
+ [:red, :green, :blue].each do |k|
233
+ next if @attrs[k].nil?
234
+ @attrs[k] = @attrs[k].to_i
235
+ Sass::Util.check_range("#{k.to_s.capitalize} value", 0..255, @attrs[k])
236
+ end
237
+
238
+ [:saturation, :lightness].each do |k|
239
+ next if @attrs[k].nil?
240
+ value = Number.new(@attrs[k], ['%']) # Get correct unit for error messages
241
+ @attrs[k] = Sass::Util.check_range("#{k.to_s.capitalize}", 0..100, value, '%')
242
+ end
243
+
244
+ @attrs[:alpha] = Sass::Util.check_range("Alpha channel", 0..1, @attrs[:alpha])
245
+ end
246
+
247
+ # The red component of the color.
248
+ #
249
+ # @return [Fixnum]
250
+ def red
251
+ hsl_to_rgb!
252
+ @attrs[:red]
253
+ end
254
+
255
+ # The green component of the color.
256
+ #
257
+ # @return [Fixnum]
258
+ def green
259
+ hsl_to_rgb!
260
+ @attrs[:green]
261
+ end
262
+
263
+ # The blue component of the color.
264
+ #
265
+ # @return [Fixnum]
266
+ def blue
267
+ hsl_to_rgb!
268
+ @attrs[:blue]
269
+ end
270
+
271
+ # The hue component of the color.
272
+ #
273
+ # @return [Numeric]
274
+ def hue
275
+ rgb_to_hsl!
276
+ @attrs[:hue]
277
+ end
278
+
279
+ # The saturation component of the color.
280
+ #
281
+ # @return [Numeric]
282
+ def saturation
283
+ rgb_to_hsl!
284
+ @attrs[:saturation]
285
+ end
286
+
287
+ # The lightness component of the color.
288
+ #
289
+ # @return [Numeric]
290
+ def lightness
291
+ rgb_to_hsl!
292
+ @attrs[:lightness]
293
+ end
294
+
295
+ # The alpha channel (opacity) of the color.
296
+ # This is 1 unless otherwise defined.
297
+ #
298
+ # @return [Fixnum]
299
+ def alpha
300
+ @attrs[:alpha]
301
+ end
302
+
303
+ # Returns whether this color object is translucent;
304
+ # that is, whether the alpha channel is non-1.
305
+ #
306
+ # @return [Boolean]
307
+ def alpha?
308
+ alpha < 1
309
+ end
310
+
311
+ # Returns the red, green, and blue components of the color.
312
+ #
313
+ # @return [Array<Fixnum>] A frozen three-element array of the red, green, and blue
314
+ # values (respectively) of the color
315
+ def rgb
316
+ [red, green, blue].freeze
317
+ end
318
+
319
+ # Returns the hue, saturation, and lightness components of the color.
320
+ #
321
+ # @return [Array<Fixnum>] A frozen three-element array of the
322
+ # hue, saturation, and lightness values (respectively) of the color
323
+ def hsl
324
+ [hue, saturation, lightness].freeze
325
+ end
326
+
327
+ # The SassScript `==` operation.
328
+ # **Note that this returns a {Sass::Script::Bool} object,
329
+ # not a Ruby boolean**.
330
+ #
331
+ # @param other [Literal] The right-hand side of the operator
332
+ # @return [Bool] True if this literal is the same as the other,
333
+ # false otherwise
334
+ def eq(other)
335
+ Sass::Script::Bool.new(
336
+ other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha)
337
+ end
338
+
339
+ # Returns a copy of this color with one or more channels changed.
340
+ # RGB or HSL colors may be changed, but not both at once.
341
+ #
342
+ # For example:
343
+ #
344
+ # Color.new([10, 20, 30]).with(:blue => 40)
345
+ # #=> rgb(10, 40, 30)
346
+ # Color.new([126, 126, 126]).with(:red => 0, :green => 255)
347
+ # #=> rgb(0, 255, 126)
348
+ # Color.new([255, 0, 127]).with(:saturation => 60)
349
+ # #=> rgb(204, 51, 127)
350
+ # Color.new([1, 2, 3]).with(:alpha => 0.4)
351
+ # #=> rgba(1, 2, 3, 0.4)
352
+ #
353
+ # @param attrs [{Symbol => Numeric}]
354
+ # A map of channel names (`:red`, `:green`, `:blue`,
355
+ # `:hue`, `:saturation`, `:lightness`, or `:alpha`) to values
356
+ # @return [Color] The new Color object
357
+ # @raise [ArgumentError] if both RGB and HSL keys are specified
358
+ def with(attrs)
359
+ attrs = attrs.reject {|k, v| v.nil?}
360
+ hsl = !([:hue, :saturation, :lightness] & attrs.keys).empty?
361
+ rgb = !([:red, :green, :blue] & attrs.keys).empty?
362
+ if hsl && rgb
363
+ raise ArgumentError.new("Cannot specify HSL and RGB values for a color at the same time")
364
+ end
365
+
366
+ if hsl
367
+ [:hue, :saturation, :lightness].each {|k| attrs[k] ||= send(k)}
368
+ elsif rgb
369
+ [:red, :green, :blue].each {|k| attrs[k] ||= send(k)}
370
+ else
371
+ # If we're just changing the alpha channel,
372
+ # keep all the HSL/RGB stuff we've calculated
373
+ attrs = @attrs.merge(attrs)
374
+ end
375
+ attrs[:alpha] ||= alpha
376
+
377
+ Color.new(attrs, :allow_both_rgb_and_hsl)
378
+ end
379
+
380
+ # The SassScript `+` operation.
381
+ # Its functionality depends on the type of its argument:
382
+ #
383
+ # {Number}
384
+ # : Adds the number to each of the RGB color channels.
385
+ #
386
+ # {Color}
387
+ # : Adds each of the RGB color channels together.
388
+ #
389
+ # {Literal}
390
+ # : See {Literal#plus}.
391
+ #
392
+ # @param other [Literal] The right-hand side of the operator
393
+ # @return [Color] The resulting color
394
+ # @raise [Sass::SyntaxError] if `other` is a number with units
395
+ def plus(other)
396
+ if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
397
+ piecewise(other, :+)
398
+ else
399
+ super
400
+ end
401
+ end
402
+
403
+ # The SassScript `-` operation.
404
+ # Its functionality depends on the type of its argument:
405
+ #
406
+ # {Number}
407
+ # : Subtracts the number from each of the RGB color channels.
408
+ #
409
+ # {Color}
410
+ # : Subtracts each of the other color's RGB color channels from this color's.
411
+ #
412
+ # {Literal}
413
+ # : See {Literal#minus}.
414
+ #
415
+ # @param other [Literal] The right-hand side of the operator
416
+ # @return [Color] The resulting color
417
+ # @raise [Sass::SyntaxError] if `other` is a number with units
418
+ def minus(other)
419
+ if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
420
+ piecewise(other, :-)
421
+ else
422
+ super
423
+ end
424
+ end
425
+
426
+ # The SassScript `*` operation.
427
+ # Its functionality depends on the type of its argument:
428
+ #
429
+ # {Number}
430
+ # : Multiplies the number by each of the RGB color channels.
431
+ #
432
+ # {Color}
433
+ # : Multiplies each of the RGB color channels together.
434
+ #
435
+ # @param other [Number, Color] The right-hand side of the operator
436
+ # @return [Color] The resulting color
437
+ # @raise [Sass::SyntaxError] if `other` is a number with units
438
+ def times(other)
439
+ if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
440
+ piecewise(other, :*)
441
+ else
442
+ raise NoMethodError.new(nil, :times)
443
+ end
444
+ end
445
+
446
+ # The SassScript `/` operation.
447
+ # Its functionality depends on the type of its argument:
448
+ #
449
+ # {Number}
450
+ # : Divides each of the RGB color channels by the number.
451
+ #
452
+ # {Color}
453
+ # : Divides each of this color's RGB color channels by the other color's.
454
+ #
455
+ # {Literal}
456
+ # : See {Literal#div}.
457
+ #
458
+ # @param other [Literal] The right-hand side of the operator
459
+ # @return [Color] The resulting color
460
+ # @raise [Sass::SyntaxError] if `other` is a number with units
461
+ def div(other)
462
+ if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
463
+ piecewise(other, :/)
464
+ else
465
+ super
466
+ end
467
+ end
468
+
469
+ # The SassScript `%` operation.
470
+ # Its functionality depends on the type of its argument:
471
+ #
472
+ # {Number}
473
+ # : Takes each of the RGB color channels module the number.
474
+ #
475
+ # {Color}
476
+ # : Takes each of this color's RGB color channels modulo the other color's.
477
+ #
478
+ # @param other [Number, Color] The right-hand side of the operator
479
+ # @return [Color] The resulting color
480
+ # @raise [Sass::SyntaxError] if `other` is a number with units
481
+ def mod(other)
482
+ if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
483
+ piecewise(other, :%)
484
+ else
485
+ raise NoMethodError.new(nil, :mod)
486
+ end
487
+ end
488
+
489
+ # Returns a string representation of the color.
490
+ # This is usually the color's hex value,
491
+ # but if the color has a name that's used instead.
492
+ #
493
+ # @return [String] The string representation
494
+ def to_s(opts = {})
495
+ return rgba_str if alpha?
496
+ return smallest if options[:style] == :compressed
497
+ return COLOR_NAMES_REVERSE[rgb] if COLOR_NAMES_REVERSE[rgb]
498
+ hex_str
499
+ end
500
+ alias_method :to_sass, :to_s
501
+
502
+ # Returns a string representation of the color.
503
+ #
504
+ # @return [String] The hex value
505
+ def inspect
506
+ alpha? ? rgba_str : hex_str
507
+ end
508
+
509
+ private
510
+
511
+ def smallest
512
+ small_hex_str = hex_str.gsub(/^#(.)\1(.)\2(.)\3$/, '#\1\2\3')
513
+ return small_hex_str unless (color = COLOR_NAMES_REVERSE[rgb]) &&
514
+ color.size <= small_hex_str.size
515
+ return color
516
+ end
517
+
518
+ def rgba_str
519
+ split = options[:style] == :compressed ? ',' : ', '
520
+ "rgba(#{rgb.join(split)}#{split}#{Number.round(alpha)})"
521
+ end
522
+
523
+ def hex_str
524
+ red, green, blue = rgb.map { |num| num.to_s(16).rjust(2, '0') }
525
+ "##{red}#{green}#{blue}"
526
+ end
527
+
528
+ def piecewise(other, operation)
529
+ other_num = other.is_a? Number
530
+ if other_num && !other.unitless?
531
+ raise Sass::SyntaxError.new("Cannot add a number with units (#{other}) to a color (#{self}).")
532
+ end
533
+
534
+ result = []
535
+ for i in (0...3)
536
+ res = rgb[i].send(operation, other_num ? other.value : other.rgb[i])
537
+ result[i] = [ [res, 255].min, 0 ].max
538
+ end
539
+
540
+ if !other_num && other.alpha != alpha
541
+ raise Sass::SyntaxError.new("Alpha channels must be equal: #{self} #{operation} #{other}")
542
+ end
543
+
544
+ with(:red => result[0], :green => result[1], :blue => result[2])
545
+ end
546
+
547
+ def hsl_to_rgb!
548
+ return if @attrs[:red] && @attrs[:blue] && @attrs[:green]
549
+
550
+ h = @attrs[:hue] / 360.0
551
+ s = @attrs[:saturation] / 100.0
552
+ l = @attrs[:lightness] / 100.0
553
+
554
+ # Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
555
+ m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
556
+ m1 = l * 2 - m2
557
+ @attrs[:red], @attrs[:green], @attrs[:blue] = [
558
+ hue_to_rgb(m1, m2, h + 1.0/3),
559
+ hue_to_rgb(m1, m2, h),
560
+ hue_to_rgb(m1, m2, h - 1.0/3)
561
+ ].map {|c| (c * 0xff).round}
562
+ end
563
+
564
+ def hue_to_rgb(m1, m2, h)
565
+ h += 1 if h < 0
566
+ h -= 1 if h > 1
567
+ return m1 + (m2 - m1) * h * 6 if h * 6 < 1
568
+ return m2 if h * 2 < 1
569
+ return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
570
+ return m1
571
+ end
572
+
573
+ def rgb_to_hsl!
574
+ return if @attrs[:hue] && @attrs[:saturation] && @attrs[:lightness]
575
+ r, g, b = [:red, :green, :blue].map {|k| @attrs[k] / 255.0}
576
+
577
+ # Algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
578
+ max = [r, g, b].max
579
+ min = [r, g, b].min
580
+ d = max - min
581
+
582
+ h =
583
+ case max
584
+ when min; 0
585
+ when r; 60 * (g-b)/d
586
+ when g; 60 * (b-r)/d + 120
587
+ when b; 60 * (r-g)/d + 240
588
+ end
589
+
590
+ l = (max + min)/2.0
591
+
592
+ s =
593
+ if max == min
594
+ 0
595
+ elsif l < 0.5
596
+ d/(2*l)
597
+ else
598
+ d/(2 - 2*l)
599
+ end
600
+
601
+ @attrs[:hue] = h % 360
602
+ @attrs[:saturation] = s * 100
603
+ @attrs[:lightness] = l * 100
604
+ end
605
+ end
606
+ end
@@ -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, :space
26
+ alias_method :or_expr, :div
27
+ alias_method :unary_div, :ident
28
+ alias_method :paren, :string
29
+ end
30
+ end
31
+ end