compass-core-sass37 1.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 (147) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/VERSION +1 -0
  4. data/data/caniuse.json +1 -0
  5. data/data/caniuse_extras/css-placeholder.json +171 -0
  6. data/lib/compass/browser_support.rb +64 -0
  7. data/lib/compass/configuration/adapters.rb +109 -0
  8. data/lib/compass/configuration/data.rb +199 -0
  9. data/lib/compass/configuration/defaults.rb +207 -0
  10. data/lib/compass/configuration/inheritance.rb +307 -0
  11. data/lib/compass/configuration/paths.rb +19 -0
  12. data/lib/compass/configuration/watch.rb +38 -0
  13. data/lib/compass/configuration.rb +175 -0
  14. data/lib/compass/core/caniuse.rb +314 -0
  15. data/lib/compass/core/generated_version.rb +6 -0
  16. data/lib/compass/core/sass_extensions/functions/colors.rb +67 -0
  17. data/lib/compass/core/sass_extensions/functions/configuration.rb +173 -0
  18. data/lib/compass/core/sass_extensions/functions/constants.rb +56 -0
  19. data/lib/compass/core/sass_extensions/functions/cross_browser_support.rb +270 -0
  20. data/lib/compass/core/sass_extensions/functions/display.rb +32 -0
  21. data/lib/compass/core/sass_extensions/functions/enumerate.rb +7 -0
  22. data/lib/compass/core/sass_extensions/functions/env.rb +72 -0
  23. data/lib/compass/core/sass_extensions/functions/files.rb +33 -0
  24. data/lib/compass/core/sass_extensions/functions/font_files.rb +65 -0
  25. data/lib/compass/core/sass_extensions/functions/gradient_support.rb +859 -0
  26. data/lib/compass/core/sass_extensions/functions/image_size.rb +117 -0
  27. data/lib/compass/core/sass_extensions/functions/inline_image.rb +63 -0
  28. data/lib/compass/core/sass_extensions/functions/lists.rb +102 -0
  29. data/lib/compass/core/sass_extensions/functions/math.rb +105 -0
  30. data/lib/compass/core/sass_extensions/functions/selectors.rb +79 -0
  31. data/lib/compass/core/sass_extensions/functions/urls.rb +315 -0
  32. data/lib/compass/core/sass_extensions/functions.rb +41 -0
  33. data/lib/compass/core/sass_extensions/monkey_patches/browser_support.rb +119 -0
  34. data/lib/compass/core/sass_extensions/monkey_patches/traversal.rb +23 -0
  35. data/lib/compass/core/sass_extensions/monkey_patches.rb +3 -0
  36. data/lib/compass/core/sass_extensions.rb +10 -0
  37. data/lib/compass/core/version.rb +13 -0
  38. data/lib/compass/core.rb +78 -0
  39. data/lib/compass/error.rb +5 -0
  40. data/lib/compass/frameworks.rb +181 -0
  41. data/lib/compass/util.rb +19 -0
  42. data/lib/compass-core.rb +1 -0
  43. data/stylesheets/_compass.scss +3 -0
  44. data/stylesheets/_lemonade.scss +38 -0
  45. data/stylesheets/compass/_configuration.scss +54 -0
  46. data/stylesheets/compass/_css3.scss +21 -0
  47. data/stylesheets/compass/_layout.scss +3 -0
  48. data/stylesheets/compass/_reset-legacy.scss +3 -0
  49. data/stylesheets/compass/_reset.scss +3 -0
  50. data/stylesheets/compass/_support.scss +447 -0
  51. data/stylesheets/compass/_typography.scss +4 -0
  52. data/stylesheets/compass/_utilities.scss +9 -0
  53. data/stylesheets/compass/css3/_animation.scss +122 -0
  54. data/stylesheets/compass/css3/_appearance.scss +17 -0
  55. data/stylesheets/compass/css3/_background-clip.scss +35 -0
  56. data/stylesheets/compass/css3/_background-origin.scss +37 -0
  57. data/stylesheets/compass/css3/_background-size.scss +19 -0
  58. data/stylesheets/compass/css3/_border-radius.scss +107 -0
  59. data/stylesheets/compass/css3/_box-shadow.scss +88 -0
  60. data/stylesheets/compass/css3/_box-sizing.scss +21 -0
  61. data/stylesheets/compass/css3/_box.scss +85 -0
  62. data/stylesheets/compass/css3/_columns.scss +212 -0
  63. data/stylesheets/compass/css3/_deprecated-support.scss +272 -0
  64. data/stylesheets/compass/css3/_filter.scss +50 -0
  65. data/stylesheets/compass/css3/_flexbox.scss +156 -0
  66. data/stylesheets/compass/css3/_font-face.scss +48 -0
  67. data/stylesheets/compass/css3/_hyphenation.scss +71 -0
  68. data/stylesheets/compass/css3/_images.scss +152 -0
  69. data/stylesheets/compass/css3/_inline-block.scss +31 -0
  70. data/stylesheets/compass/css3/_opacity.scss +27 -0
  71. data/stylesheets/compass/css3/_pie.scss +1 -0
  72. data/stylesheets/compass/css3/_regions.scss +27 -0
  73. data/stylesheets/compass/css3/_selection.scss +59 -0
  74. data/stylesheets/compass/css3/_shared.scss +5 -0
  75. data/stylesheets/compass/css3/_text-shadow.scss +82 -0
  76. data/stylesheets/compass/css3/_transform.scss +590 -0
  77. data/stylesheets/compass/css3/_transition.scss +190 -0
  78. data/stylesheets/compass/css3/_user-interface.scss +71 -0
  79. data/stylesheets/compass/layout/_grid-background.scss +178 -0
  80. data/stylesheets/compass/layout/_sticky-footer.scss +23 -0
  81. data/stylesheets/compass/layout/_stretching.scss +24 -0
  82. data/stylesheets/compass/reset/_utilities-legacy.scss +135 -0
  83. data/stylesheets/compass/reset/_utilities.scss +142 -0
  84. data/stylesheets/compass/typography/_links.scss +3 -0
  85. data/stylesheets/compass/typography/_lists.scss +4 -0
  86. data/stylesheets/compass/typography/_text.scss +4 -0
  87. data/stylesheets/compass/typography/_units.scss +183 -0
  88. data/stylesheets/compass/typography/_vertical_rhythm.scss +300 -0
  89. data/stylesheets/compass/typography/links/_hover-link.scss +5 -0
  90. data/stylesheets/compass/typography/links/_link-colors.scss +28 -0
  91. data/stylesheets/compass/typography/links/_unstyled-link.scss +7 -0
  92. data/stylesheets/compass/typography/lists/_bullets.scss +34 -0
  93. data/stylesheets/compass/typography/lists/_horizontal-list.scss +63 -0
  94. data/stylesheets/compass/typography/lists/_inline-block-list.scss +50 -0
  95. data/stylesheets/compass/typography/lists/_inline-list.scss +47 -0
  96. data/stylesheets/compass/typography/text/_ellipsis.scss +25 -0
  97. data/stylesheets/compass/typography/text/_force-wrap.scss +12 -0
  98. data/stylesheets/compass/typography/text/_nowrap.scss +2 -0
  99. data/stylesheets/compass/typography/text/_replacement.scss +74 -0
  100. data/stylesheets/compass/utilities/_color.scss +1 -0
  101. data/stylesheets/compass/utilities/_general.scss +6 -0
  102. data/stylesheets/compass/utilities/_links.scss +5 -0
  103. data/stylesheets/compass/utilities/_lists.scss +6 -0
  104. data/stylesheets/compass/utilities/_print.scss +17 -0
  105. data/stylesheets/compass/utilities/_sass.scss +2 -0
  106. data/stylesheets/compass/utilities/_sprites.scss +2 -0
  107. data/stylesheets/compass/utilities/_tables.scss +3 -0
  108. data/stylesheets/compass/utilities/_text.scss +5 -0
  109. data/stylesheets/compass/utilities/color/_brightness.scss +20 -0
  110. data/stylesheets/compass/utilities/color/_contrast.scss +52 -0
  111. data/stylesheets/compass/utilities/general/_clearfix.scss +44 -0
  112. data/stylesheets/compass/utilities/general/_float.scss +38 -0
  113. data/stylesheets/compass/utilities/general/_hacks.scss +65 -0
  114. data/stylesheets/compass/utilities/general/_min.scss +16 -0
  115. data/stylesheets/compass/utilities/general/_reset.scss +2 -0
  116. data/stylesheets/compass/utilities/general/_tabs.scss +1 -0
  117. data/stylesheets/compass/utilities/general/_tag-cloud.scss +18 -0
  118. data/stylesheets/compass/utilities/links/_hover-link.scss +3 -0
  119. data/stylesheets/compass/utilities/links/_link-colors.scss +3 -0
  120. data/stylesheets/compass/utilities/links/_unstyled-link.scss +3 -0
  121. data/stylesheets/compass/utilities/lists/_bullets.scss +3 -0
  122. data/stylesheets/compass/utilities/lists/_horizontal-list.scss +3 -0
  123. data/stylesheets/compass/utilities/lists/_inline-block-list.scss +3 -0
  124. data/stylesheets/compass/utilities/lists/_inline-list.scss +3 -0
  125. data/stylesheets/compass/utilities/sass/_lists.scss +16 -0
  126. data/stylesheets/compass/utilities/sass/_maps.scss +19 -0
  127. data/stylesheets/compass/utilities/sprites/_base.scss +92 -0
  128. data/stylesheets/compass/utilities/sprites/_sprite-img.scss +81 -0
  129. data/stylesheets/compass/utilities/tables/_alternating-rows-and-columns.scss +22 -0
  130. data/stylesheets/compass/utilities/tables/_borders.scss +38 -0
  131. data/stylesheets/compass/utilities/tables/_scaffolding.scss +9 -0
  132. data/stylesheets/compass/utilities/text/_ellipsis.scss +3 -0
  133. data/stylesheets/compass/utilities/text/_nowrap.scss +3 -0
  134. data/stylesheets/compass/utilities/text/_replacement.scss +3 -0
  135. data/templates/ellipsis/ellipsis.sass +9 -0
  136. data/templates/ellipsis/manifest.rb +27 -0
  137. data/templates/ellipsis/xml/ellipsis.xml +14 -0
  138. data/templates/extension/manifest.rb +26 -0
  139. data/templates/extension/stylesheets/main.sass +1 -0
  140. data/templates/extension/templates/project/manifest.rb +2 -0
  141. data/templates/extension/templates/project/screen.sass +2 -0
  142. data/templates/project/USAGE.markdown +32 -0
  143. data/templates/project/ie.sass +6 -0
  144. data/templates/project/manifest.rb +4 -0
  145. data/templates/project/print.sass +6 -0
  146. data/templates/project/screen.sass +7 -0
  147. metadata +257 -0
@@ -0,0 +1,859 @@
1
+ module Compass::Core::SassExtensions::Functions::GradientSupport
2
+
3
+ GRADIENT_ASPECTS = %w(webkit moz svg css2 o owg).freeze
4
+
5
+ class CSS3AngleToSVGConverter
6
+ include Math
7
+
8
+ def initialize(angle)
9
+ @original_angle = angle
10
+ @angle = handle_keywords(angle)
11
+ @angle = in_radians(@angle) % (2 * PI)
12
+ @quadrant = (@angle * 2 / PI).to_i
13
+ @angle = case @quadrant
14
+ when 0
15
+ @angle
16
+ when 1
17
+ PI - @angle
18
+ when 2
19
+ @angle - PI
20
+ when 3
21
+ 2 * PI - @angle
22
+ end
23
+ end
24
+
25
+ TOP = 1
26
+ BOTTOM = 2
27
+ RIGHT = 4
28
+ LEFT = 8
29
+
30
+ DIR_KEYWORDS_TO_ANGLE = {
31
+ TOP => 0,
32
+ TOP | RIGHT => 45,
33
+ RIGHT => 90,
34
+ BOTTOM | RIGHT => 135,
35
+ BOTTOM => 180,
36
+ BOTTOM | LEFT => 225,
37
+ LEFT => 270,
38
+ TOP | LEFT => 315,
39
+ }
40
+
41
+ def handle_keywords(angle)
42
+ if angle.is_a?(Sass::Script::Value::List) || angle.is_a?(Sass::Script::Value::String)
43
+ direction = angle.to_sass
44
+ is_end_point = !!/\bto\b/i.match(direction)
45
+ dir = 0
46
+ dir |= TOP if /\btop\b/i.match(direction)
47
+ dir |= BOTTOM if /\bbottom\b/i.match(direction)
48
+ dir |= RIGHT if /\bright\b/i.match(direction)
49
+ dir |= LEFT if /\bleft\b/i.match(direction)
50
+ if (r = DIR_KEYWORDS_TO_ANGLE[dir])
51
+ r += 180 unless is_end_point
52
+ Sass::Script::Value::Number.new(r, %w(deg), [])
53
+ else
54
+ raise Sass::SyntaxError, "Unknown direction: #{angle.to_sass}"
55
+ end
56
+ else
57
+ angle
58
+ end
59
+ end
60
+
61
+ def in_radians(angle)
62
+ case angle.unit_str
63
+ when "deg"
64
+ angle.value * PI / 180.0
65
+ when "grad"
66
+ angle.value * PI / 200.0
67
+ when "rad"
68
+ angle.value
69
+ when "turn"
70
+ angle.value * PI * 2
71
+ else
72
+ raise Sass::SyntaxError.new("#{angle.unit_str} is not an angle")
73
+ end
74
+ end
75
+
76
+ def sin2(a)
77
+ v = sin(a)
78
+ v * v
79
+ end
80
+
81
+ def x
82
+ @x ||= if @angle > 1.570621793869697
83
+ 1.0 # avoid floating point rounding error at the asymptote
84
+ else
85
+ tan(@angle) + (1 - tan(@angle)) * sin2(@angle)
86
+ end
87
+ end
88
+
89
+ def y
90
+ @y ||= if @angle < 0.0001
91
+ 1.0 # the limit of the expression as we approach 0 is 1.
92
+ else
93
+ x / tan(@angle)
94
+ end
95
+ end
96
+
97
+ def x1
98
+ result case @quadrant
99
+ when 0, 1
100
+ -x
101
+ when 2, 3
102
+ x
103
+ end
104
+ end
105
+
106
+ def y1
107
+ result case @quadrant
108
+ when 0, 3
109
+ y
110
+ when 1, 2
111
+ -y
112
+ end
113
+ end
114
+
115
+ def x2
116
+ result case @quadrant
117
+ when 0, 1
118
+ x
119
+ when 2, 3
120
+ -x
121
+ end
122
+ end
123
+
124
+ def y2
125
+ result case @quadrant
126
+ when 0, 3
127
+ -y
128
+ when 1, 2
129
+ y
130
+ end
131
+ end
132
+
133
+ def scale(p)
134
+ (p + 1) / 2.0
135
+ end
136
+
137
+ def round6(v)
138
+ (v * 1_000_000).round / 1_000_000.0
139
+ end
140
+
141
+ def result(v)
142
+ round6(scale(v))
143
+ end
144
+ end
145
+
146
+ class ColorStop < Sass::Script::Value::Base
147
+ include Sass::Script::Value::Helpers
148
+ attr_accessor :color, :stop
149
+ def children
150
+ [color, stop].compact
151
+ end
152
+ def initialize(color, stop = nil)
153
+ assert_legal_color! color
154
+ assert_legal_color_stop! stop if stop
155
+ self.color, self.stop = color, stop
156
+ end
157
+ def inspect
158
+ to_s
159
+ end
160
+
161
+ def assert_legal_color!(color)
162
+ unless Sass::Script::Value::Color === color ||
163
+ Sass::Script::Tree::Funcall === color ||
164
+ (Sass::Script::Value::String === color && color.value == "currentColor")||
165
+ (Sass::Script::Value::String === color && color.value == "transparent")
166
+ raise Sass::SyntaxError, "Expected a color. Got: #{color}"
167
+ end
168
+ end
169
+ def assert_legal_color_stop!(stop)
170
+ case stop
171
+ when Sass::Script::Value::String
172
+ return stop.value.start_with?("calc(")
173
+ when Sass::Script::Value::Number
174
+ return true
175
+ end
176
+ raise Sass::SyntaxError, "Expected a number or numerical expression. Got: #{stop.inspect}"
177
+ end
178
+
179
+ def self.color_to_svg_s(c)
180
+ # svg doesn't support the "transparent" keyword; we need to manually
181
+ # refactor it into "transparent black"
182
+ if c.is_a?(Sass::Script::Value::String) && c.value == "transparent"
183
+ "black"
184
+ elsif c.is_a?(Sass::Script::Value::String)
185
+ c.value.dup
186
+ else
187
+ self.color_to_s(c.with(:alpha => 1))
188
+ end
189
+ end
190
+
191
+ def self.color_to_svg_alpha(c)
192
+ # svg doesn't support the "transparent" keyword; we need to manually
193
+ # refactor it into "transparent black"
194
+ if c.is_a?(Sass::Script::Value::String) && c.value == "transparent"
195
+ 0
196
+ elsif c.is_a?(Sass::Script::Value::String) && c.value == "currentColor"
197
+ 1
198
+ else
199
+ c.alpha
200
+ end
201
+ end
202
+
203
+ def self.color_to_s(c)
204
+ if c.is_a?(Sass::Script::Value::String)
205
+ c.value.dup
206
+ else
207
+ c.inspect.dup
208
+ end
209
+ end
210
+
211
+ def to_s(options = self.options)
212
+ s = self.class.color_to_s(color)
213
+ if stop
214
+ s << " "
215
+ if stop.respond_to?(:unitless?) && stop.unitless?
216
+ s << stop.times(number(100, "%")).inspect
217
+ else
218
+ s << stop.to_s
219
+ end
220
+ end
221
+ s
222
+ end
223
+
224
+ def to_sass(options = nil)
225
+ identifier("color-stop(#{color.to_sass rescue nil}, #{stop.to_sass rescue nil})")
226
+ end
227
+ end
228
+
229
+ module Gradient
230
+ include Sass::Script::Value::Helpers
231
+
232
+ def self.included(base)
233
+ base.extend ClassMethods
234
+ end
235
+
236
+ module ClassMethods
237
+ def standardized_prefix(prefix)
238
+ class_eval %Q<
239
+ def to_#{prefix}(options = self.options)
240
+ identifier("-#{prefix}-\#{to_s_prefixed(options)}")
241
+ end
242
+ >
243
+ end
244
+ end
245
+
246
+ def inspect
247
+ to_s
248
+ end
249
+
250
+ def supports?(aspect)
251
+ GRADIENT_ASPECTS.include?(aspect)
252
+ end
253
+
254
+ def has_aspect?
255
+ true
256
+ end
257
+
258
+ def is_position(pos)
259
+ pos.value =~ Compass::Core::SassExtensions::Functions::Constants::POSITIONS
260
+ end
261
+
262
+ def angle?(value)
263
+ value.is_a?(Sass::Script::Value::Number) &&
264
+ value.numerator_units.size == 1 &&
265
+ value.numerator_units.first == "deg" &&
266
+ value.denominator_units.empty?
267
+ end
268
+
269
+ end
270
+
271
+ class RadialGradient < Sass::Script::Value::Base
272
+ include Gradient
273
+
274
+ attr_accessor :position, :shape_and_size, :color_stops
275
+
276
+ def children
277
+ [color_stops, position, shape_and_size].compact
278
+ end
279
+
280
+ def initialize(position, shape_and_size, color_stops)
281
+ unless color_stops.value.size >= 2
282
+ raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
283
+ end
284
+ if angle?(position)
285
+ raise Sass::SyntaxError, "CSS no longer allows angles in radial-gradients."
286
+ end
287
+ self.position = position
288
+ self.shape_and_size = shape_and_size
289
+ self.color_stops = color_stops
290
+ end
291
+
292
+ def to_s(options = self.options)
293
+ to_official.to_s
294
+ end
295
+
296
+ def to_s_prefixed(options = self.options)
297
+ to_s(options)
298
+ end
299
+
300
+ def supports?(aspect)
301
+ # I don't know how to support radial old webkit gradients (owg)
302
+ if %w(owg).include?(aspect)
303
+ false
304
+ else
305
+ super
306
+ end
307
+ end
308
+
309
+ standardized_prefix :webkit
310
+ standardized_prefix :moz
311
+
312
+ def to_webkit(options = self.options)
313
+ s = "-webkit-radial-gradient("
314
+ s << old_standard_arguments(options)
315
+ s << ")"
316
+ identifier(s)
317
+ end
318
+
319
+ def to_moz(options = self.options)
320
+ s = "-moz-radial-gradient("
321
+ s << old_standard_arguments(options)
322
+ s << ")"
323
+ identifier(s)
324
+ end
325
+
326
+ def to_official
327
+ s = "radial-gradient("
328
+ s << new_standard_arguments(options)
329
+ s << ")"
330
+ identifier(s)
331
+ end
332
+
333
+ def new_standard_arguments(options = self.options)
334
+ if shape_and_size
335
+ "#{array_to_s(shape_and_size, options)} at #{array_to_s(position, options)}, #{array_to_s(color_stops, options)}"
336
+ elsif position
337
+ "#{array_to_s(position, options)}, #{array_to_s(color_stops, options)}"
338
+ else
339
+ array_to_s(color_stops, options)
340
+ end
341
+ end
342
+
343
+ def old_standard_arguments(options = self.options)
344
+ if shape_and_size
345
+ "#{array_to_s(position, options)}, #{array_to_s(shape_and_size, options)}, #{array_to_s(color_stops, options)}"
346
+ elsif position
347
+ "#{array_to_s(position, options)}, #{array_to_s(color_stops, options)}"
348
+ else
349
+ array_to_s(color_stops, options)
350
+ end
351
+ end
352
+
353
+ def to_svg(options = self.options)
354
+ # XXX Add shape support if possible
355
+ radial_svg_gradient(color_stops, position || _center_position)
356
+ end
357
+
358
+ def to_css2(options = self.options)
359
+ null
360
+ end
361
+
362
+ def array_to_s(array, opts)
363
+ if array.is_a?(Sass::Script::Value::List)
364
+ array.to_s
365
+ else
366
+ l = list(array, :space)
367
+ l.options = opts
368
+ l.to_s
369
+ end
370
+ end
371
+ end
372
+
373
+ class LinearGradient < Sass::Script::Value::Base
374
+ include Gradient
375
+
376
+ attr_accessor :color_stops, :position_or_angle, :legacy
377
+
378
+ def children
379
+ [color_stops, position_or_angle].compact
380
+ end
381
+
382
+ def initialize(position_or_angle, color_stops, legacy=false)
383
+ unless color_stops.value.size >= 2
384
+ raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
385
+ end
386
+ self.position_or_angle = position_or_angle
387
+ self.color_stops = color_stops
388
+ self.legacy = legacy
389
+ end
390
+
391
+ def to_s_prefixed(options = self.options)
392
+ s = "linear-gradient("
393
+ if legacy
394
+ s << position_or_angle.to_s(options) << ", " if position_or_angle
395
+ else
396
+ s << convert_to_or_from_legacy(position_or_angle, options) << ", " if position_or_angle
397
+ end
398
+ s << color_stops.to_s(options)
399
+ s << ")"
400
+ end
401
+
402
+ def convert_to_or_from_legacy(position_or_angle, options = self.options)
403
+ input = if position_or_angle.is_a?(Sass::Script::Value::Number)
404
+ position_or_angle
405
+ else
406
+ opts(list(position_or_angle.to_s.split(' ').map {|s| identifier(s) }, :space))
407
+ end
408
+ return convert_angle_from_offical(input).to_s(options)
409
+ end
410
+
411
+ def to_s(options = self.options)
412
+ s = 'linear-gradient('
413
+ if legacy
414
+ s << convert_to_or_from_legacy(position_or_angle, options) << ", " if position_or_angle
415
+ else
416
+ s << position_or_angle.to_s(options) << ", " if position_or_angle
417
+ end
418
+ s << color_stops.to_s(options)
419
+ s << ")"
420
+ end
421
+
422
+ standardized_prefix :webkit
423
+ standardized_prefix :moz
424
+ standardized_prefix :o
425
+
426
+ def supports?(aspect)
427
+ # I don't know how to support degree-based gradients in old webkit gradients (owg) or svg so we just disable them.
428
+ if %w(owg).include?(aspect) && position_or_angle.is_a?(Sass::Script::Value::Number) && position_or_angle.numerator_units.include?("deg")
429
+ false
430
+ elsif %w(owg svg).include?(aspect) && color_stops.value.any?{|cs| cs.stop.is_a?(Sass::Script::Value::String) }
431
+ # calc expressions cannot be represented in svg or owg
432
+ false
433
+ else
434
+ super
435
+ end
436
+ end
437
+
438
+ # Output the original webkit gradient syntax
439
+ def to_owg(options = self.options)
440
+ position_list = reverse_side_or_corner(position_or_angle)
441
+
442
+ start_point = grad_point(position_list)
443
+ args = []
444
+ args << start_point
445
+ args << linear_end_position(position_list, start_point, color_stops.value.last.stop)
446
+ args << grad_color_stops(color_stops)
447
+ args.each{|a| a.options = options}
448
+ Sass::Script::String.new("-webkit-gradient(linear, #{args.join(', ')})")
449
+ end
450
+
451
+ def to_svg(options = self.options)
452
+ linear_svg_gradient(color_stops, position_or_angle || identifier("top"))
453
+ end
454
+
455
+ def to_css2(options = self.options)
456
+ null
457
+ end
458
+ end
459
+
460
+ module Functions
461
+ include Sass::Script::Value::Helpers
462
+
463
+ def reverse_side_or_corner(position)
464
+ position_array = position.nil? ? [identifier('top')] : position.value.dup
465
+ if position_array.first == identifier('to')
466
+ # Remove the 'to' element from the array
467
+ position_array.shift
468
+
469
+ # Reverse all the positions
470
+ reversed_position = position_array.map do |pos|
471
+ opposite_position(pos)
472
+ end
473
+ else
474
+ # When the position does not have the 'to' element we don't need to
475
+ # reverse the direction of the gradient
476
+ reversed_position = position_array
477
+ end
478
+ opts(list(reversed_position, :space))
479
+ end
480
+
481
+ def convert_angle_from_offical(deg)
482
+ if deg.is_a?(Sass::Script::Value::Number)
483
+ return number((deg.value.to_f - 450).abs % 360, 'deg')
484
+ else
485
+ args = deg.value
486
+ direction = []
487
+ if args[0] == identifier('to')
488
+ if args.size < 2
489
+ direction = args
490
+ else
491
+ direction << opposite_position(args[1])
492
+ end
493
+ else
494
+ direction << identifier('to')
495
+ args.each do |pos|
496
+ direction << opposite_position(pos)
497
+ end
498
+ end
499
+ return opts(list(direction, :space))
500
+ end
501
+ end
502
+
503
+ # given a position list, return a corresponding position in percents
504
+ # otherwise, returns the original argument
505
+ def grad_point(position)
506
+ original_value = position
507
+ position = unless position.is_a?(Sass::Script::Value::List)
508
+ opts(list([position], :space))
509
+ else
510
+ opts(list(position.value.dup, position.separator))
511
+ end
512
+ # Handle unknown arguments by passing them along untouched.
513
+ unless position.value.all?{|p| is_position(p) }
514
+ return original_value
515
+ end
516
+ if (position.value.first.value =~ /top|bottom/) or (position.value.last.value =~ /left|right/)
517
+ # browsers are pretty forgiving of reversed positions so we are too.
518
+ position = opts(list(position.value.reverse, position.separator))
519
+ end
520
+ if position.value.size == 1
521
+ if position.value.first.value =~ /top|bottom/
522
+ position = opts(list(identifier("center"), position.value.first, position.separator))
523
+ elsif position.value.first.value =~ /left|right/
524
+ position = opts(list(position.value.first, identifier("center"), position.separator))
525
+ end
526
+ end
527
+ position = opts(list(position.value.map do |p|
528
+ case p.value
529
+ when /top|left/
530
+ number(0, "%")
531
+ when /bottom|right/
532
+ number(100, "%")
533
+ when /center/
534
+ number(50, "%")
535
+ else
536
+ p
537
+ end
538
+ end, position.separator))
539
+ position
540
+ end
541
+
542
+ def color_stops(*args)
543
+ opts(list(args.map do |arg|
544
+ if ColorStop === arg
545
+ arg
546
+ elsif Sass::Script::Value::Color === arg
547
+ ColorStop.new(arg)
548
+ elsif Sass::Script::Value::List === arg
549
+ ColorStop.new(*arg.value)
550
+ elsif Sass::Script::Value::String === arg && arg.value == "transparent"
551
+ ColorStop.new(arg)
552
+ elsif Sass::Script::Value::String === arg && arg.value == "currentColor"
553
+ ColorStop.new(arg)
554
+ else
555
+ raise Sass::SyntaxError, "Not a valid color stop: #{arg.class.name}: #{arg}"
556
+ end
557
+ end, :comma))
558
+ end
559
+
560
+ def radial_gradient(position_or_angle, shape_and_size, *color_stops)
561
+ # Have to deal with variable length/meaning arguments.
562
+ if color_stop?(shape_and_size)
563
+ color_stops.unshift(shape_and_size)
564
+ shape_and_size = nil
565
+ elsif list_of_color_stops?(shape_and_size)
566
+ # Support legacy use of the color-stops() function
567
+ color_stops = shape_and_size.value + color_stops
568
+ shape_and_size = nil
569
+ end
570
+ shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses
571
+ # ditto for position_or_angle
572
+ if color_stop?(position_or_angle)
573
+ color_stops.unshift(position_or_angle)
574
+ position_or_angle = nil
575
+ elsif list_of_color_stops?(position_or_angle)
576
+ color_stops = position_or_angle.value + color_stops
577
+ position_or_angle = nil
578
+ end
579
+ position_or_angle = nil if position_or_angle && !position_or_angle.to_bool
580
+
581
+ # Support legacy use of the color-stops() function
582
+ if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
583
+ color_stops = color_stops.first.value
584
+ end
585
+ if position_or_angle.is_a?(Sass::Script::Value::List) &&
586
+ (i = position_or_angle.value.index {|word| word.is_a?(Sass::Script::Value::String) && word.value == "at"})
587
+ shape_and_size = list(position_or_angle.value[0..(i-1)], :space)
588
+ shape_and_size.options = options
589
+ position_or_angle = list(position_or_angle.value[(i+1)..-1], :space)
590
+ position_or_angle.options = options
591
+ end
592
+ RadialGradient.new(position_or_angle, shape_and_size, send(:color_stops, *color_stops))
593
+ end
594
+
595
+ def _build_linear_gradient(position_or_angle, *color_stops)
596
+ if color_stop?(position_or_angle)
597
+ color_stops.unshift(position_or_angle)
598
+ position_or_angle = nil
599
+ elsif list_of_color_stops?(position_or_angle)
600
+ color_stops = position_or_angle.value + color_stops
601
+ position_or_angle = nil
602
+ end
603
+ position_or_angle = nil if position_or_angle && !position_or_angle.to_bool
604
+
605
+ # Support legacy use of the color-stops() function
606
+ if color_stops.size == 1 && (stops = list_of_color_stops?(color_stops.first))
607
+ color_stops = stops
608
+ end
609
+ return [position_or_angle, color_stops]
610
+ end
611
+
612
+ def _linear_gradient(position_or_angle, *color_stops)
613
+ position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
614
+ LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
615
+ end
616
+
617
+ def _linear_gradient_legacy(position_or_angle, *color_stops)
618
+ position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
619
+ LinearGradient.new(position_or_angle, send(:color_stops, *color_stops), true)
620
+ end
621
+
622
+ # returns color-stop() calls for use in webkit.
623
+ def grad_color_stops(color_list)
624
+ stops = color_stops_in_percentages(color_list).map do |stop, color|
625
+ Sass::Script::String.new("color-stop(#{stop.to_s}, #{ColorStop.color_to_s(color)})")
626
+ end
627
+ opts(list(stops, :comma))
628
+ end
629
+
630
+ def color_stops_in_percentages(color_list)
631
+ assert_type color_list, :List
632
+ color_list = normalize_stops(color_list)
633
+ max = color_list.value.last.stop
634
+ last_value = nil
635
+ color_list.value.map do |pos|
636
+ next [pos.stop, pos.color] if pos.stop.is_a?(Sass::Script::Value::String)
637
+ # have to convert absolute units to percentages for use in color stop functions.
638
+ stop = pos.stop
639
+ stop = stop.div(max).times(number(100, "%")) if stop.numerator_units == max.numerator_units && max.numerator_units != ["%"]
640
+ # Make sure the color stops are specified in the right order.
641
+ if last_value && stop.numerator_units == last_value.numerator_units && stop.denominator_units == last_value.denominator_units && (stop.value * 1000).round < (last_value.value * 1000).round
642
+ raise Sass::SyntaxError.new("Color stops must be specified in increasing order. #{stop.value} came after #{last_value.value}.")
643
+ end
644
+ last_value = stop
645
+ [stop, pos.color]
646
+ end
647
+ end
648
+
649
+ # only used for webkit
650
+ def linear_end_position(position_or_angle, start_point, end_target)
651
+ end_point = grad_point(opposite_position(position_or_angle))
652
+
653
+ if end_target && end_target.numerator_units == ["px"]
654
+ if start_point.value.first == end_point.value.first && start_point.value.last.value == 0
655
+ # this means top-to-bottom
656
+ new_end_point = end_point.value.dup
657
+ new_end_point[1] = number(end_target.value)
658
+
659
+ end_point = opts(list(new_end_point, end_point.separator))
660
+ elsif start_point.value.last == end_point.value.last && start_point.value.first.value == 0
661
+ # this implies left-to-right
662
+
663
+ new_end_point = end_point.value.dup
664
+ new_end_point[0] = number(end_target.value)
665
+
666
+ end_point = opts(list(new_end_point, end_point.separator))
667
+ end
668
+ end
669
+ end_point
670
+ end
671
+
672
+ # returns the end position of the gradient from the color stop
673
+ def grad_end_position(color_list, radial = bool(false))
674
+ assert_type color_list, :List
675
+ default = number(100)
676
+ grad_position(color_list, number(color_list.value.size), default, radial)
677
+ end
678
+
679
+ def grad_position(color_list, index, default, radial = bool(false))
680
+ assert_type color_list, :List
681
+ stop = color_list.value[index.value - 1].stop
682
+ if stop && radial.to_bool
683
+ orig_stop = stop
684
+ if stop.unitless?
685
+ if stop.value <= 1
686
+ # A unitless number is assumed to be a percentage when it's between 0 and 1
687
+ stop = stop.times(number(100, "%"))
688
+ else
689
+ # Otherwise, a unitless number is assumed to be in pixels
690
+ stop = stop.times(number(1, "px"))
691
+ end
692
+ end
693
+ if stop.numerator_units == ["%"] && color_list.value.last.stop && color_list.value.last.stop.numerator_units == ["px"]
694
+ stop = stop.times(color_list.value.last.stop).div(number(100, "%"))
695
+ end
696
+ Compass::Logger.new.record(:warning, "Webkit only supports pixels for the start and end stops for radial gradients. Got: #{orig_stop}") if stop.numerator_units != ["px"]
697
+ stop.div(Sass::Script::Value::Number.new(1, stop.numerator_units, stop.denominator_units))
698
+ elsif stop
699
+ stop
700
+ else
701
+ default
702
+ end
703
+ end
704
+
705
+ def linear_svg_gradient(color_stops, start)
706
+ converter = CSS3AngleToSVGConverter.new(start)
707
+ stops = color_stops_in_percentages(color_stops)
708
+
709
+ svg = linear_svg(stops, converter.x1, converter.y1, converter.x2, converter.y2)
710
+ inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
711
+ end
712
+
713
+ def radial_svg_gradient(color_stops, center)
714
+ cx, cy = *grad_point(center).value
715
+ r = grad_end_position(color_stops, bool(true))
716
+ stops = color_stops_in_percentages(color_stops)
717
+
718
+ svg = radial_svg(stops, cx, cy, r)
719
+ inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
720
+ end
721
+
722
+ private
723
+
724
+ def color_stop?(arg)
725
+ arg.is_a?(ColorStop) ||
726
+ (arg.is_a?(Sass::Script::Value::List) && ColorStop.new(*arg.value)) ||
727
+ ColorStop.new(arg)
728
+ rescue
729
+ nil
730
+ end
731
+
732
+ def normalize_stops(color_list)
733
+ positions = color_list.value.map{|obj| obj.dup}
734
+ # fill in the start and end positions, if unspecified
735
+ positions.first.stop = number(0) unless positions.first.stop
736
+ positions.last.stop = number(100, "%") unless positions.last.stop
737
+ # fill in empty values
738
+ for i in 0...positions.size
739
+ if positions[i].stop.nil?
740
+ num = 2.0
741
+ for j in (i+1)...positions.size
742
+ if positions[j].stop
743
+ positions[i].stop = positions[i-1].stop.plus((positions[j].stop.minus(positions[i-1].stop)).div(number(num)))
744
+ break
745
+ else
746
+ num += 1
747
+ end
748
+ end
749
+ end
750
+ end
751
+ # normalize unitless numbers
752
+ positions.each do |pos|
753
+ next pos if pos.stop.is_a?(Sass::Script::Value::String)
754
+ if pos.stop.unitless? && pos.stop.value <= 1
755
+ pos.stop = pos.stop.times(number(100, "%"))
756
+ elsif pos.stop.unitless?
757
+ pos.stop = pos.stop.times(number(1, "px"))
758
+ end
759
+ end
760
+ if (positions.last.stop.eq(number(0, "px")).to_bool ||
761
+ positions.last.stop.eq(number(0, "%")).to_bool)
762
+ raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
763
+ end
764
+ opts(list(positions, color_list.separator))
765
+ end
766
+
767
+ def parse_color_stop(arg)
768
+ return ColorStop.new(arg) if arg.is_a?(Sass::Script::Value::Color)
769
+ return nil unless arg.is_a?(Sass::Script::Value::String)
770
+ color = stop = nil
771
+ expr = Sass::Script::Parser.parse(arg.value, 0, 0)
772
+ case expr
773
+ when Sass::Script::Value::Color
774
+ color = expr
775
+ when Sass::Script::Tree::Funcall
776
+ color = expr
777
+ when Sass::Script::Tree::Operation
778
+ unless [:concat, :space].include?(expr.instance_variable_get("@operator"))
779
+ # This should never happen.
780
+ raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
781
+ end
782
+ color = expr.instance_variable_get("@operand1")
783
+ stop = expr.instance_variable_get("@operand2")
784
+ else
785
+ raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
786
+ end
787
+ ColorStop.new(color, stop)
788
+ end
789
+
790
+ def list_of_color_stops?(arg)
791
+ if arg.respond_to?(:value)
792
+ arg.value.is_a?(Array) && arg.value.all?{|a| color_stop?(a)} ? arg.value : nil
793
+ elsif arg.is_a?(Array)
794
+ arg.all?{|a| color_stop?(a)} ? arg : nil
795
+ end
796
+ end
797
+
798
+ def linear_svg(color_stops, x1, y1, x2, y2)
799
+ gradient = %Q{<linearGradient id="grad" gradientUnits="objectBoundingBox" x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}">#{color_stops_svg(color_stops)}</linearGradient>}
800
+ svg(gradient)
801
+ end
802
+
803
+ def radial_svg(color_stops, cx, cy, r)
804
+ gradient = %Q{<radialGradient id="grad" gradientUnits="userSpaceOnUse" cx="#{cx}" cy="#{cy}" r="#{r}%">#{color_stops_svg(color_stops)}</radialGradient>}
805
+ svg(gradient)
806
+ end
807
+
808
+ # color_stops = array of: [stop, color]
809
+ def color_stops_svg(color_stops)
810
+ color_stops.each.map{ |stop, color|
811
+ s = %{<stop offset="#{stop.to_s}"}
812
+ s << %{ stop-color="#{ColorStop.color_to_svg_s(color)}"}
813
+ alpha = ColorStop.color_to_svg_alpha(color)
814
+ s << %{ stop-opacity="#{alpha}"} if alpha != 1
815
+ s << "/>"
816
+ }.join
817
+ end
818
+
819
+ def svg(gradient)
820
+ svg = <<-EOS
821
+ <?xml version="1.0" encoding="utf-8"?>
822
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg"><defs>#{gradient}</defs><rect x="0" y="0" width="100%" height="100%" fill="url(#grad)" /></svg>
823
+ EOS
824
+ end
825
+
826
+ def _center_position
827
+ opts(list(identifier("center"), identifier("center"), :space))
828
+ end
829
+
830
+ def opts(v)
831
+ v.options = options
832
+ v
833
+ end
834
+
835
+ end
836
+
837
+ module Assertions
838
+ def assert_type(value, type, name = nil)
839
+ return if value.is_a?(Sass::Script.const_get(type))
840
+ err = "#{value.inspect} is not a #{type.to_s.downcase}"
841
+ err = "$#{name}: " + err if name
842
+ raise ArgumentError.new(err)
843
+ end
844
+ end
845
+
846
+ class LinearGradient < Sass::Script::Value::Base
847
+ include Assertions
848
+ include Functions
849
+ include Compass::Core::SassExtensions::Functions::Constants
850
+ include Compass::Core::SassExtensions::Functions::InlineImage
851
+ end
852
+
853
+ class RadialGradient < Sass::Script::Value::Base
854
+ include Assertions
855
+ include Functions
856
+ include Compass::Core::SassExtensions::Functions::Constants
857
+ include Compass::Core::SassExtensions::Functions::InlineImage
858
+ end
859
+ end