compass-core 1.0.0.alpha.13

Sign up to get free protection for your applications and to get access to all the features.
Files changed (141) 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-core.rb +1 -0
  7. data/lib/compass/browser_support.rb +62 -0
  8. data/lib/compass/configuration.rb +168 -0
  9. data/lib/compass/configuration/data.rb +178 -0
  10. data/lib/compass/configuration/defaults.rb +197 -0
  11. data/lib/compass/configuration/inheritance.rb +304 -0
  12. data/lib/compass/configuration/paths.rb +19 -0
  13. data/lib/compass/core.rb +64 -0
  14. data/lib/compass/core/caniuse.rb +282 -0
  15. data/lib/compass/core/sass_extensions.rb +10 -0
  16. data/lib/compass/core/sass_extensions/functions.rb +39 -0
  17. data/lib/compass/core/sass_extensions/functions/colors.rb +67 -0
  18. data/lib/compass/core/sass_extensions/functions/configuration.rb +162 -0
  19. data/lib/compass/core/sass_extensions/functions/constants.rb +74 -0
  20. data/lib/compass/core/sass_extensions/functions/cross_browser_support.rb +269 -0
  21. data/lib/compass/core/sass_extensions/functions/display.rb +32 -0
  22. data/lib/compass/core/sass_extensions/functions/enumerate.rb +7 -0
  23. data/lib/compass/core/sass_extensions/functions/env.rb +60 -0
  24. data/lib/compass/core/sass_extensions/functions/font_files.rb +41 -0
  25. data/lib/compass/core/sass_extensions/functions/gradient_support.rb +616 -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 +64 -0
  28. data/lib/compass/core/sass_extensions/functions/lists.rb +101 -0
  29. data/lib/compass/core/sass_extensions/functions/math.rb +92 -0
  30. data/lib/compass/core/sass_extensions/functions/selectors.rb +64 -0
  31. data/lib/compass/core/sass_extensions/functions/urls.rb +297 -0
  32. data/lib/compass/core/sass_extensions/monkey_patches.rb +3 -0
  33. data/lib/compass/core/sass_extensions/monkey_patches/browser_support.rb +118 -0
  34. data/lib/compass/core/sass_extensions/monkey_patches/traversal.rb +23 -0
  35. data/lib/compass/core/version.rb +5 -0
  36. data/lib/compass/error.rb +5 -0
  37. data/stylesheets/_compass.scss +3 -0
  38. data/stylesheets/_lemonade.scss +38 -0
  39. data/stylesheets/compass/_configuration.scss +54 -0
  40. data/stylesheets/compass/_css3.scss +21 -0
  41. data/stylesheets/compass/_layout.scss +3 -0
  42. data/stylesheets/compass/_reset-legacy.scss +3 -0
  43. data/stylesheets/compass/_reset.scss +3 -0
  44. data/stylesheets/compass/_support.scss +441 -0
  45. data/stylesheets/compass/_typography.scss +4 -0
  46. data/stylesheets/compass/_utilities.scss +9 -0
  47. data/stylesheets/compass/css3/_animation.scss +122 -0
  48. data/stylesheets/compass/css3/_appearance.scss +17 -0
  49. data/stylesheets/compass/css3/_background-clip.scss +35 -0
  50. data/stylesheets/compass/css3/_background-origin.scss +37 -0
  51. data/stylesheets/compass/css3/_background-size.scss +19 -0
  52. data/stylesheets/compass/css3/_border-radius.scss +107 -0
  53. data/stylesheets/compass/css3/_box-shadow.scss +88 -0
  54. data/stylesheets/compass/css3/_box-sizing.scss +15 -0
  55. data/stylesheets/compass/css3/_box.scss +85 -0
  56. data/stylesheets/compass/css3/_columns.scss +210 -0
  57. data/stylesheets/compass/css3/_deprecated-support.scss +272 -0
  58. data/stylesheets/compass/css3/_filter.scss +50 -0
  59. data/stylesheets/compass/css3/_flexbox.scss +156 -0
  60. data/stylesheets/compass/css3/_font-face.scss +48 -0
  61. data/stylesheets/compass/css3/_hyphenation.scss +71 -0
  62. data/stylesheets/compass/css3/_images.scss +139 -0
  63. data/stylesheets/compass/css3/_inline-block.scss +31 -0
  64. data/stylesheets/compass/css3/_opacity.scss +23 -0
  65. data/stylesheets/compass/css3/_pie.scss +1 -0
  66. data/stylesheets/compass/css3/_regions.scss +27 -0
  67. data/stylesheets/compass/css3/_selection.scss +59 -0
  68. data/stylesheets/compass/css3/_shared.scss +5 -0
  69. data/stylesheets/compass/css3/_text-shadow.scss +82 -0
  70. data/stylesheets/compass/css3/_transform.scss +590 -0
  71. data/stylesheets/compass/css3/_transition.scss +171 -0
  72. data/stylesheets/compass/css3/_user-interface.scss +71 -0
  73. data/stylesheets/compass/layout/_grid-background.scss +178 -0
  74. data/stylesheets/compass/layout/_sticky-footer.scss +23 -0
  75. data/stylesheets/compass/layout/_stretching.scss +24 -0
  76. data/stylesheets/compass/reset/_utilities-legacy.scss +135 -0
  77. data/stylesheets/compass/reset/_utilities.scss +142 -0
  78. data/stylesheets/compass/typography/_links.scss +3 -0
  79. data/stylesheets/compass/typography/_lists.scss +4 -0
  80. data/stylesheets/compass/typography/_text.scss +4 -0
  81. data/stylesheets/compass/typography/_units.scss +152 -0
  82. data/stylesheets/compass/typography/_vertical_rhythm.scss +300 -0
  83. data/stylesheets/compass/typography/links/_hover-link.scss +5 -0
  84. data/stylesheets/compass/typography/links/_link-colors.scss +28 -0
  85. data/stylesheets/compass/typography/links/_unstyled-link.scss +7 -0
  86. data/stylesheets/compass/typography/lists/_bullets.scss +34 -0
  87. data/stylesheets/compass/typography/lists/_horizontal-list.scss +63 -0
  88. data/stylesheets/compass/typography/lists/_inline-block-list.scss +50 -0
  89. data/stylesheets/compass/typography/lists/_inline-list.scss +47 -0
  90. data/stylesheets/compass/typography/text/_ellipsis.scss +25 -0
  91. data/stylesheets/compass/typography/text/_force-wrap.scss +12 -0
  92. data/stylesheets/compass/typography/text/_nowrap.scss +2 -0
  93. data/stylesheets/compass/typography/text/_replacement.scss +68 -0
  94. data/stylesheets/compass/utilities/_color.scss +1 -0
  95. data/stylesheets/compass/utilities/_general.scss +6 -0
  96. data/stylesheets/compass/utilities/_links.scss +5 -0
  97. data/stylesheets/compass/utilities/_lists.scss +6 -0
  98. data/stylesheets/compass/utilities/_print.scss +17 -0
  99. data/stylesheets/compass/utilities/_sass.scss +2 -0
  100. data/stylesheets/compass/utilities/_sprites.scss +2 -0
  101. data/stylesheets/compass/utilities/_tables.scss +3 -0
  102. data/stylesheets/compass/utilities/_text.scss +5 -0
  103. data/stylesheets/compass/utilities/color/_brightness.scss +12 -0
  104. data/stylesheets/compass/utilities/color/_contrast.scss +52 -0
  105. data/stylesheets/compass/utilities/general/_clearfix.scss +44 -0
  106. data/stylesheets/compass/utilities/general/_float.scss +38 -0
  107. data/stylesheets/compass/utilities/general/_hacks.scss +65 -0
  108. data/stylesheets/compass/utilities/general/_min.scss +16 -0
  109. data/stylesheets/compass/utilities/general/_reset.scss +2 -0
  110. data/stylesheets/compass/utilities/general/_tabs.scss +1 -0
  111. data/stylesheets/compass/utilities/general/_tag-cloud.scss +18 -0
  112. data/stylesheets/compass/utilities/links/_hover-link.scss +3 -0
  113. data/stylesheets/compass/utilities/links/_link-colors.scss +3 -0
  114. data/stylesheets/compass/utilities/links/_unstyled-link.scss +3 -0
  115. data/stylesheets/compass/utilities/lists/_bullets.scss +3 -0
  116. data/stylesheets/compass/utilities/lists/_horizontal-list.scss +3 -0
  117. data/stylesheets/compass/utilities/lists/_inline-block-list.scss +3 -0
  118. data/stylesheets/compass/utilities/lists/_inline-list.scss +3 -0
  119. data/stylesheets/compass/utilities/sass/_lists.scss +16 -0
  120. data/stylesheets/compass/utilities/sass/_maps.scss +19 -0
  121. data/stylesheets/compass/utilities/sprites/_base.scss +92 -0
  122. data/stylesheets/compass/utilities/sprites/_sprite-img.scss +81 -0
  123. data/stylesheets/compass/utilities/tables/_alternating-rows-and-columns.scss +22 -0
  124. data/stylesheets/compass/utilities/tables/_borders.scss +38 -0
  125. data/stylesheets/compass/utilities/tables/_scaffolding.scss +9 -0
  126. data/stylesheets/compass/utilities/text/_ellipsis.scss +3 -0
  127. data/stylesheets/compass/utilities/text/_nowrap.scss +3 -0
  128. data/stylesheets/compass/utilities/text/_replacement.scss +3 -0
  129. data/templates/ellipsis/ellipsis.sass +9 -0
  130. data/templates/ellipsis/manifest.rb +27 -0
  131. data/templates/ellipsis/xml/ellipsis.xml +14 -0
  132. data/templates/extension/manifest.rb +26 -0
  133. data/templates/extension/stylesheets/main.sass +1 -0
  134. data/templates/extension/templates/project/manifest.rb +2 -0
  135. data/templates/extension/templates/project/screen.sass +2 -0
  136. data/templates/project/USAGE.markdown +32 -0
  137. data/templates/project/ie.sass +6 -0
  138. data/templates/project/manifest.rb +4 -0
  139. data/templates/project/print.sass +6 -0
  140. data/templates/project/screen.sass +7 -0
  141. metadata +241 -0
@@ -0,0 +1,32 @@
1
+ module Compass::Core::SassExtensions::Functions::Display
2
+ DEFAULT_DISPLAY = {
3
+ "block" => %w{address article aside blockquote center dir div dd details dl dt fieldset
4
+ figcaption figure form footer frameset h1 h2 h3 h4 h5 h6 hr header hgroup
5
+ isindex menu nav noframes noscript ol p pre section summary ul},
6
+ "inline" => %w{a abbr acronym audio b basefont bdo big br canvas cite code command
7
+ datalist dfn em embed font i img input keygen kbd label mark meter output
8
+ progress q rp rt ruby s samp select small span strike strong sub
9
+ sup textarea time tt u var video wbr},
10
+ "inline-block" => %w{img},
11
+ "table" => %w{table},
12
+ "list-item" => %w{li},
13
+ "table-row-group" => %w{tbody},
14
+ "table-header-group" => %w{thead},
15
+ "table-footer-group" => %w{tfoot},
16
+ "table-row" => %w{tr},
17
+ "table-cell" => %w{th td},
18
+ "html5-block" => %w{article aside details figcaption figure footer header hgroup menu nav
19
+ section summary},
20
+ "html5-inline" => %w{audio canvas command datalist embed keygen mark meter output progress rp
21
+ rt ruby time video wbr},
22
+ "text-input" => %w{input textarea}
23
+ }
24
+
25
+ DEFAULT_DISPLAY["html5"] = (DEFAULT_DISPLAY["html5-block"] + DEFAULT_DISPLAY["html5-inline"]).sort.uniq
26
+
27
+ # returns a comma delimited string for all the
28
+ # elements according to their default css3 display value.
29
+ def elements_of_type(display)
30
+ Sass::Script::String.new(DEFAULT_DISPLAY.fetch(display.value.to_s).join(", "))
31
+ end
32
+ end
@@ -0,0 +1,7 @@
1
+ module Compass::Core::SassExtensions::Functions::Enumerate
2
+ def enumerate(prefix, from, through, separator = nil)
3
+ separator ||= Sass::Script::String.new("-", :string)
4
+ selectors = (from.value..through.value).map{|i| "#{prefix.value}#{separator.value}#{i}"}.join(", ")
5
+ Sass::Script::String.new(selectors)
6
+ end
7
+ end
@@ -0,0 +1,60 @@
1
+ module Compass::Core::SassExtensions::Functions::Env
2
+ extend Compass::Core::SassExtensions::Functions::SassDeclarationHelper
3
+
4
+ def compass_env
5
+ compass_opts = options[:compass] || {}
6
+ Sass::Script::String.new((compass_opts[:environment] || "development").to_s)
7
+ end
8
+ declare :compass_env, []
9
+
10
+ DEFAULT_TIME = Sass::Script::String.new("%T%:z")
11
+ def current_time(format = DEFAULT_TIME)
12
+ assert_type format, :String
13
+ Sass::Script::String.new(Time.now.strftime(format.value))
14
+ end
15
+ declare :current_time, []
16
+ declare :current_time, [:format]
17
+
18
+ DEFAULT_DATE = Sass::Script::String.new("%F")
19
+ def current_date(format = DEFAULT_DATE)
20
+ current_time(format)
21
+ end
22
+ declare :current_date, []
23
+ declare :current_date, [:format]
24
+
25
+ NOT_ABSOLUTE = Sass::Script::Bool.new(false)
26
+ def current_source_file(absolute = NOT_ABSOLUTE)
27
+ if absolute.to_bool
28
+ Sass::Script::String.new(options[:original_filename].to_s)
29
+ else
30
+ filename = Pathname.new(options[:original_filename].to_s)
31
+ sass_path = Pathname.new(Compass.configuration.sass_path)
32
+ relative_filename = filename.relative_path_from(sass_path).to_s rescue filename
33
+ Sass::Script::String.new(relative_filename.to_s)
34
+ end
35
+ end
36
+ declare :current_source_file, []
37
+ declare :current_source_file, [:absolute]
38
+
39
+ def current_output_file(absolute = NOT_ABSOLUTE)
40
+ Sass::Script::String.new(options[:css_filename].to_s)
41
+ if absolute.to_bool
42
+ Sass::Script::String.new(options[:css_filename].to_s)
43
+ else
44
+ filename = Pathname.new(options[:css_filename].to_s)
45
+ css_path = Pathname.new(Compass.configuration.css_path)
46
+ relative_filename = filename.relative_path_from(css_path).to_s rescue filename
47
+ Sass::Script::String.new(relative_filename.to_s)
48
+ end
49
+ end
50
+ declare :current_output_file, []
51
+ declare :current_output_file, [:absolute]
52
+
53
+ unless Sass::Util.has?(:public_instance_method, Sass::Script::Functions, :inspect)
54
+ # This is going to be in sass 3.3, just here temporarily.
55
+ def inspect(value)
56
+ unquoted_string(value.to_sass)
57
+ end
58
+ declare :inspect, [:value]
59
+ end
60
+ end
@@ -0,0 +1,41 @@
1
+ module Compass::Core::SassExtensions::Functions::FontFiles
2
+ FONT_TYPES = {
3
+ :woff => 'woff',
4
+ :otf => 'opentype',
5
+ :opentype => 'opentype',
6
+ :ttf => 'truetype',
7
+ :truetype => 'truetype',
8
+ :svg => 'svg',
9
+ :eot => 'embedded-opentype'
10
+ }
11
+
12
+ def font_files(*args)
13
+ files = []
14
+ args_length = args.length
15
+ skip_next = false
16
+
17
+ args.each_with_index do |arg, index|
18
+ if skip_next
19
+ skip_next = false
20
+ next
21
+ end
22
+
23
+ type = (args_length > (index + 1)) ? args[index + 1].value.to_sym : :wrong
24
+
25
+ if FONT_TYPES.key? type
26
+ skip_next = true
27
+ else
28
+ # let pass url like font.type?thing#stuff
29
+ type = arg.to_s.split('.').last.gsub(/(\?(.*))?(#(.*))?"/, '').to_sym
30
+ end
31
+
32
+ if FONT_TYPES.key? type
33
+ files << "#{font_url(arg)} format('#{FONT_TYPES[type]}')"
34
+ else
35
+ raise Sass::SyntaxError, "Could not determine font type for #{arg}"
36
+ end
37
+ end
38
+
39
+ Sass::Script::String.new(files.join(", "))
40
+ end
41
+ end
@@ -0,0 +1,616 @@
1
+ module Compass::Core::SassExtensions::Functions::GradientSupport
2
+
3
+ GRADIENT_ASPECTS = %w(webkit moz svg css2 o owg).freeze
4
+
5
+ class ColorStop < Sass::Script::Literal
6
+ attr_accessor :color, :stop
7
+ def children
8
+ [color, stop].compact
9
+ end
10
+ def initialize(color, stop = nil)
11
+ unless Sass::Script::Color === color ||
12
+ Sass::Script::Funcall === color ||
13
+ (Sass::Script::String === color && color.value == "currentColor")||
14
+ (Sass::Script::String === color && color.value == "transparent")
15
+ raise Sass::SyntaxError, "Expected a color. Got: #{color}"
16
+ end
17
+ if stop && !stop.is_a?(Sass::Script::Number)
18
+ raise Sass::SyntaxError, "Expected a number. Got: #{stop}"
19
+ end
20
+ self.color, self.stop = color, stop
21
+ end
22
+ def inspect
23
+ to_s
24
+ end
25
+
26
+ def self.color_to_svg_s(c)
27
+ # svg doesn't support the "transparent" keyword; we need to manually
28
+ # refactor it into "transparent black"
29
+ if c.is_a?(Sass::Script::String) && c.value == "transparent"
30
+ "black"
31
+ elsif c.is_a?(Sass::Script::String)
32
+ c.value.dup
33
+ else
34
+ self.color_to_s(c.with(:alpha => 1))
35
+ end
36
+ end
37
+
38
+ def self.color_to_svg_alpha(c)
39
+ # svg doesn't support the "transparent" keyword; we need to manually
40
+ # refactor it into "transparent black"
41
+ if c.is_a?(Sass::Script::String) && c.value == "transparent"
42
+ 0
43
+ elsif c.is_a?(Sass::Script::String) && c.value == "currentColor"
44
+ 1
45
+ else
46
+ c.alpha
47
+ end
48
+ end
49
+
50
+ def self.color_to_s(c)
51
+ if c.is_a?(Sass::Script::String)
52
+ c.value.dup
53
+ else
54
+ c.inspect.dup
55
+ end
56
+ end
57
+
58
+ def to_s(options = self.options)
59
+ s = self.class.color_to_s(color)
60
+ if stop
61
+ s << " "
62
+ if stop.unitless?
63
+ s << stop.times(Sass::Script::Number.new(100, ["%"])).inspect
64
+ else
65
+ s << stop.inspect
66
+ end
67
+ end
68
+ s
69
+ end
70
+
71
+ def to_sass(options = nil)
72
+ Sass::Script::String.new("color-stop(#{color.to_sass rescue nil}, #{stop.to_sass rescue nil})")
73
+ end
74
+ end
75
+
76
+ module Gradient
77
+
78
+ def self.included(base)
79
+ base.extend ClassMethods
80
+ end
81
+
82
+ module ClassMethods
83
+ def standardized_prefix(prefix)
84
+ class_eval %Q{
85
+ def to_#{prefix}(options = self.options)
86
+ Sass::Script::String.new("-#{prefix}-\#{to_s_prefixed(options)}")
87
+ end
88
+ }
89
+ end
90
+ end
91
+
92
+ def inspect
93
+ to_s
94
+ end
95
+
96
+ def supports?(aspect)
97
+ GRADIENT_ASPECTS.include?(aspect)
98
+ end
99
+
100
+ def has_aspect?
101
+ true
102
+ end
103
+
104
+ def is_position(pos)
105
+ pos.value =~ Compass::Core::SassExtensions::Functions::Constants::POSITIONS
106
+ end
107
+
108
+ def angle?(value)
109
+ value.is_a?(Sass::Script::Number) &&
110
+ value.numerator_units.size == 1 &&
111
+ value.numerator_units.first == "deg" &&
112
+ value.denominator_units.empty?
113
+ end
114
+
115
+ end
116
+
117
+ class RadialGradient < Sass::Script::Literal
118
+ include Gradient
119
+
120
+ attr_accessor :position, :shape_and_size, :color_stops
121
+
122
+ def children
123
+ [color_stops, position, shape_and_size].compact
124
+ end
125
+
126
+ def initialize(position, shape_and_size, color_stops)
127
+ unless color_stops.value.size >= 2
128
+ raise Sass::SyntaxError, "At least two color stops are required for a radial-gradient"
129
+ end
130
+ if angle?(position)
131
+ raise Sass::SyntaxError, "CSS no longer allows angles in radial-gradients."
132
+ end
133
+ self.position = position
134
+ self.shape_and_size = shape_and_size
135
+ self.color_stops = color_stops
136
+ end
137
+
138
+ def to_s(options = self.options)
139
+ s = "radial-gradient("
140
+ s << position.to_s(options) << ", " if position
141
+ s << shape_and_size.to_s(options) << ", " if shape_and_size
142
+ s << color_stops.to_s(options)
143
+ s << ")"
144
+ end
145
+
146
+ def to_s_prefixed(options = self.options)
147
+ to_s(options)
148
+ end
149
+
150
+ standardized_prefix :webkit
151
+ standardized_prefix :moz
152
+ standardized_prefix :o
153
+
154
+ def to_svg(options = self.options)
155
+ # XXX Add shape support if possible
156
+ radial_svg_gradient(color_stops, position || _center_position)
157
+ end
158
+
159
+ def to_css2(options = self.options)
160
+ Sass::Script::String.new("")
161
+ end
162
+ end
163
+
164
+ class LinearGradient < Sass::Script::Literal
165
+ include Gradient
166
+
167
+ attr_accessor :color_stops, :position_or_angle, :legacy
168
+
169
+ def children
170
+ [color_stops, position_or_angle].compact
171
+ end
172
+
173
+ def initialize(position_or_angle, color_stops, legacy=false)
174
+ unless color_stops.value.size >= 2
175
+ raise Sass::SyntaxError, "At least two color stops are required for a linear-gradient"
176
+ end
177
+ self.position_or_angle = position_or_angle
178
+ self.color_stops = color_stops
179
+ self.legacy = legacy
180
+ end
181
+
182
+ def to_s_prefixed(options = self.options)
183
+ s = "linear-gradient("
184
+ if legacy
185
+ s << position_or_angle.to_s(options) << ", " if position_or_angle
186
+ else
187
+ s << convert_to_or_from_legacy(position_or_angle, options) << ", " if position_or_angle
188
+ end
189
+ s << color_stops.to_s(options)
190
+ s << ")"
191
+ end
192
+
193
+ def convert_to_or_from_legacy(position_or_angle, options = self.options)
194
+ input = if position_or_angle.is_a?(Sass::Script::Number)
195
+ position_or_angle
196
+ else
197
+ opts(Sass::Script::List.new(position_or_angle.to_s.split(' ').map {|s| Sass::Script::String.new(s) }, :space))
198
+ end
199
+ return convert_angle_from_offical(input).to_s(options)
200
+ end
201
+
202
+ def to_s(options = self.options)
203
+ s = 'linear-gradient('
204
+ if legacy
205
+ s << convert_to_or_from_legacy(position_or_angle, options) << ", " if position_or_angle
206
+ else
207
+ s << position_or_angle.to_s(options) << ", " if position_or_angle
208
+ end
209
+ s << color_stops.to_s(options)
210
+ s << ")"
211
+ end
212
+
213
+ standardized_prefix :webkit
214
+ standardized_prefix :moz
215
+ standardized_prefix :o
216
+
217
+ def supports?(aspect)
218
+ # I don't know how to support degree-based gradients in old webkit gradients (owg) or svg so we just disable them.
219
+ if %w(owg svg).include?(aspect) && position_or_angle.is_a?(Sass::Script::Number) && position_or_angle.numerator_units.include?("deg")
220
+ false
221
+ else
222
+ super
223
+ end
224
+ end
225
+
226
+ def to_svg(options = self.options)
227
+ linear_svg_gradient(color_stops, position_or_angle || Sass::Script::String.new("top"))
228
+ end
229
+
230
+ def to_css2(options = self.options)
231
+ Sass::Script::String.new("")
232
+ end
233
+ end
234
+
235
+ module Functions
236
+
237
+ def convert_angle_from_offical(deg)
238
+ if deg.is_a?(Sass::Script::Number)
239
+ return Sass::Script::Number.new((deg.value.to_f - 450).abs % 360, ['deg'])
240
+ else
241
+ args = deg.value
242
+ direction = []
243
+ if args[0] == Sass::Script::String.new('to')
244
+ if args.size < 2
245
+ direction = args
246
+ else
247
+ direction << opposite_position(args[1])
248
+ end
249
+ else
250
+ direction << Sass::Script::String.new('to')
251
+ args.each do |pos|
252
+ direction << opposite_position(pos)
253
+ end
254
+ end
255
+ return Sass::Script::String.new(direction.join(' '))
256
+ end
257
+ end
258
+
259
+ # given a position list, return a corresponding position in percents
260
+ # otherwise, returns the original argument
261
+ def grad_point(position)
262
+ original_value = position
263
+ position = unless position.is_a?(Sass::Script::List)
264
+ opts(Sass::Script::List.new([position], :space))
265
+ else
266
+ opts(Sass::Script::List.new(position.value.dup, position.separator))
267
+ end
268
+ # Handle unknown arguments by passing them along untouched.
269
+ unless position.value.all?{|p| is_position(p) }
270
+ return original_value
271
+ end
272
+ if (position.value.first.value =~ /top|bottom/) or (position.value.last.value =~ /left|right/)
273
+ # browsers are pretty forgiving of reversed positions so we are too.
274
+ position = Sass::Script::List.new(position.value.reverse, position.separator)
275
+ end
276
+ if position.value.size == 1
277
+ if position.value.first.value =~ /top|bottom/
278
+ position = Sass::Script::List.new(
279
+ [Sass::Script::String.new("center"), position.value.first], position.separator)
280
+ elsif position.value.first.value =~ /left|right/
281
+ position = Sass::Script::List.new(
282
+ [position.value.first, Sass::Script::String.new("center")], position.separator)
283
+ end
284
+ end
285
+ position = Sass::Script::List.new(position.value.map do |p|
286
+ case p.value
287
+ when /top|left/
288
+ Sass::Script::Number.new(0, ["%"])
289
+ when /bottom|right/
290
+ Sass::Script::Number.new(100, ["%"])
291
+ when /center/
292
+ Sass::Script::Number.new(50, ["%"])
293
+ else
294
+ p
295
+ end
296
+ end, position.separator)
297
+ position
298
+ end
299
+
300
+ def color_stops(*args)
301
+ opts(Sass::Script::List.new(args.map do |arg|
302
+ if ColorStop === arg
303
+ arg
304
+ elsif Sass::Script::Color === arg
305
+ ColorStop.new(arg)
306
+ elsif Sass::Script::List === arg
307
+ ColorStop.new(*arg.value)
308
+ elsif Sass::Script::String === arg && arg.value == "transparent"
309
+ ColorStop.new(arg)
310
+ elsif Sass::Script::String === arg && arg.value == "currentColor"
311
+ ColorStop.new(arg)
312
+ else
313
+ raise Sass::SyntaxError, "Not a valid color stop: #{arg.class.name}: #{arg}"
314
+ end
315
+ end, :comma))
316
+ end
317
+
318
+ def radial_gradient(position_or_angle, shape_and_size, *color_stops)
319
+ # Have to deal with variable length/meaning arguments.
320
+ if color_stop?(shape_and_size)
321
+ color_stops.unshift(shape_and_size)
322
+ shape_and_size = nil
323
+ elsif list_of_color_stops?(shape_and_size)
324
+ # Support legacy use of the color-stops() function
325
+ color_stops = shape_and_size.value + color_stops
326
+ shape_and_size = nil
327
+ end
328
+ shape_and_size = nil if shape_and_size && !shape_and_size.to_bool # nil out explictly passed falses
329
+ # ditto for position_or_angle
330
+ if color_stop?(position_or_angle)
331
+ color_stops.unshift(position_or_angle)
332
+ position_or_angle = nil
333
+ elsif list_of_color_stops?(position_or_angle)
334
+ color_stops = position_or_angle.value + color_stops
335
+ position_or_angle = nil
336
+ end
337
+ position_or_angle = nil if position_or_angle && !position_or_angle.to_bool
338
+
339
+ # Support legacy use of the color-stops() function
340
+ if color_stops.size == 1 && list_of_color_stops?(color_stops.first)
341
+ color_stops = color_stops.first.value
342
+ end
343
+ RadialGradient.new(position_or_angle, shape_and_size, send(:color_stops, *color_stops))
344
+ end
345
+
346
+ def _build_linear_gradient(position_or_angle, *color_stops)
347
+ if color_stop?(position_or_angle)
348
+ color_stops.unshift(position_or_angle)
349
+ position_or_angle = nil
350
+ elsif list_of_color_stops?(position_or_angle)
351
+ color_stops = position_or_angle.value + color_stops
352
+ position_or_angle = nil
353
+ end
354
+ position_or_angle = nil if position_or_angle && !position_or_angle.to_bool
355
+
356
+ # Support legacy use of the color-stops() function
357
+ if color_stops.size == 1 && (stops = list_of_color_stops?(color_stops.first))
358
+ color_stops = stops
359
+ end
360
+ return [position_or_angle, color_stops]
361
+ end
362
+
363
+ def _linear_gradient(position_or_angle, *color_stops)
364
+ position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
365
+ LinearGradient.new(position_or_angle, send(:color_stops, *color_stops))
366
+ end
367
+
368
+ def _linear_gradient_legacy(position_or_angle, *color_stops)
369
+ position_or_angle, color_stops = _build_linear_gradient(position_or_angle, *color_stops)
370
+ LinearGradient.new(position_or_angle, send(:color_stops, *color_stops), true)
371
+ end
372
+
373
+ # returns color-stop() calls for use in webkit.
374
+ def grad_color_stops(color_list)
375
+ stops = color_stops_in_percentages(color_list).map do |stop, color|
376
+ "color-stop(#{stop.inspect}, #{ColorStop.color_to_s(color)})"
377
+ end
378
+ Sass::Script::String.new(stops.join(", "))
379
+ end
380
+
381
+ def color_stops_in_percentages(color_list)
382
+ assert_type color_list, :List
383
+ color_list = normalize_stops(color_list)
384
+ max = color_list.value.last.stop
385
+ last_value = nil
386
+ color_stops = color_list.value.map do |pos|
387
+ # have to convert absolute units to percentages for use in color stop functions.
388
+ stop = pos.stop
389
+ stop = stop.div(max).times(Sass::Script::Number.new(100,["%"])) if stop.numerator_units == max.numerator_units && max.numerator_units != ["%"]
390
+ # Make sure the color stops are specified in the right order.
391
+ 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
392
+ raise Sass::SyntaxError.new("Color stops must be specified in increasing order. #{stop.value} came after #{last_value.value}.")
393
+ end
394
+ last_value = stop
395
+ [stop, pos.color]
396
+ end
397
+ end
398
+
399
+ # only used for webkit
400
+ def linear_end_position(position_or_angle, color_list)
401
+ start_point = grad_point(position_or_angle || Sass::Script::String.new("top"))
402
+ end_point = grad_point(opposite_position(position_or_angle || Sass::Script::String.new("top")))
403
+ end_target = color_list.value.last.stop
404
+
405
+ if color_list.value.last.stop && color_list.value.last.stop.numerator_units == ["px"]
406
+ new_end = color_list.value.last.stop.value
407
+ if start_point.value.first == end_point.value.first && start_point.value.last.value == 0
408
+ # this means top-to-bottom
409
+ end_point.value[1] = Sass::Script::Number.new(end_target.value)
410
+ elsif start_point.value.last == end_point.value.last && start_point.value.first.value == 0
411
+ # this implies left-to-right
412
+ end_point.value[0] = Sass::Script::Number.new(end_target.value)
413
+ end
414
+ end
415
+ end_point
416
+ end
417
+
418
+ # returns the end position of the gradient from the color stop
419
+ def grad_end_position(color_list, radial = Sass::Script::Bool.new(false))
420
+ assert_type color_list, :List
421
+ default = Sass::Script::Number.new(100)
422
+ grad_position(color_list, Sass::Script::Number.new(color_list.value.size), default, radial)
423
+ end
424
+
425
+ def grad_position(color_list, index, default, radial = Sass::Script::Bool.new(false))
426
+ assert_type color_list, :List
427
+ stop = color_list.value[index.value - 1].stop
428
+ if stop && radial.to_bool
429
+ orig_stop = stop
430
+ if stop.unitless?
431
+ if stop.value <= 1
432
+ # A unitless number is assumed to be a percentage when it's between 0 and 1
433
+ stop = stop.times(Sass::Script::Number.new(100, ["%"]))
434
+ else
435
+ # Otherwise, a unitless number is assumed to be in pixels
436
+ stop = stop.times(Sass::Script::Number.new(1, ["px"]))
437
+ end
438
+ end
439
+ if stop.numerator_units == ["%"] && color_list.value.last.stop && color_list.value.last.stop.numerator_units == ["px"]
440
+ stop = stop.times(color_list.value.last.stop).div(Sass::Script::Number.new(100, ["%"]))
441
+ end
442
+ 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"]
443
+ stop.div(Sass::Script::Number.new(1, stop.numerator_units, stop.denominator_units))
444
+ elsif stop
445
+ stop
446
+ else
447
+ default
448
+ end
449
+ end
450
+
451
+ def linear_svg_gradient(color_stops, start)
452
+ x1, y1 = *grad_point(start).value
453
+ x2, y2 = *grad_point(opposite_position(start)).value
454
+ stops = color_stops_in_percentages(color_stops)
455
+
456
+ svg = linear_svg(stops, x1, y1, x2, y2)
457
+ inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
458
+ end
459
+
460
+ def radial_svg_gradient(color_stops, center)
461
+ cx, cy = *grad_point(center).value
462
+ r = grad_end_position(color_stops, Sass::Script::Bool.new(true))
463
+ stops = color_stops_in_percentages(color_stops)
464
+
465
+ svg = radial_svg(stops, cx, cy, r)
466
+ inline_image_string(svg.gsub(/\s+/, ' '), 'image/svg+xml')
467
+ end
468
+
469
+ private
470
+
471
+ def color_stop?(arg)
472
+ arg.is_a?(ColorStop) ||
473
+ (arg.is_a?(Sass::Script::List) && ColorStop.new(*arg.value)) ||
474
+ ColorStop.new(arg)
475
+ rescue
476
+ nil
477
+ end
478
+
479
+ def normalize_stops(color_list)
480
+ positions = color_list.value.map{|obj| obj.dup}
481
+ # fill in the start and end positions, if unspecified
482
+ positions.first.stop = Sass::Script::Number.new(0) unless positions.first.stop
483
+ positions.last.stop = Sass::Script::Number.new(100, ["%"]) unless positions.last.stop
484
+ # fill in empty values
485
+ for i in 0...positions.size
486
+ if positions[i].stop.nil?
487
+ num = 2.0
488
+ for j in (i+1)...positions.size
489
+ if positions[j].stop
490
+ positions[i].stop = positions[i-1].stop.plus((positions[j].stop.minus(positions[i-1].stop)).div(Sass::Script::Number.new(num)))
491
+ break
492
+ else
493
+ num += 1
494
+ end
495
+ end
496
+ end
497
+ end
498
+ # normalize unitless numbers
499
+ positions.each do |pos|
500
+ if pos.stop.unitless? && pos.stop.value <= 1
501
+ pos.stop = pos.stop.times(Sass::Script::Number.new(100, ["%"]))
502
+ elsif pos.stop.unitless?
503
+ pos.stop = pos.stop.times(Sass::Script::Number.new(1, ["px"]))
504
+ end
505
+ end
506
+ if (positions.last.stop.eq(Sass::Script::Number.new(0, ["px"])).to_bool ||
507
+ positions.last.stop.eq(Sass::Script::Number.new(0, ["%"])).to_bool)
508
+ raise Sass::SyntaxError.new("Color stops must be specified in increasing order")
509
+ end
510
+ if defined?(Sass::Script::List)
511
+ opts(Sass::Script::List.new(positions, color_list.separator))
512
+ else
513
+ color_list.class.new(*positions)
514
+ end
515
+ end
516
+
517
+ def parse_color_stop(arg)
518
+ return ColorStop.new(arg) if arg.is_a?(Sass::Script::Color)
519
+ return nil unless arg.is_a?(Sass::Script::String)
520
+ color = stop = nil
521
+ expr = Sass::Script::Parser.parse(arg.value, 0, 0)
522
+ case expr
523
+ when Sass::Script::Color
524
+ color = expr
525
+ when Sass::Script::Funcall
526
+ color = expr
527
+ when Sass::Script::Operation
528
+ unless [:concat, :space].include?(expr.instance_variable_get("@operator"))
529
+ # This should never happen.
530
+ raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
531
+ end
532
+ color = expr.instance_variable_get("@operand1")
533
+ stop = expr.instance_variable_get("@operand2")
534
+ else
535
+ raise Sass::SyntaxError, "Couldn't parse a color stop from: #{arg.value}"
536
+ end
537
+ ColorStop.new(color, stop)
538
+ end
539
+
540
+ def list_of_color_stops?(arg)
541
+ if arg.respond_to?(:value)
542
+ arg.value.is_a?(Array) && arg.value.all?{|a| color_stop?(a)} ? arg.value : nil
543
+ elsif arg.is_a?(Array)
544
+ arg.all?{|a| color_stop?(a)} ? arg : nil
545
+ end
546
+ end
547
+
548
+ def linear_svg(color_stops, x1, y1, x2, y2)
549
+ transform = ''
550
+ if angle?(position_or_angle)
551
+ transform = %Q{ gradientTransform = "rotate(#{position_or_angle.value})"}
552
+ end
553
+ gradient = %Q{<linearGradient id="grad" gradientUnits="userSpaceOnUse" x1="#{x1}" y1="#{y1}" x2="#{x2}" y2="#{y2}"#{transform}>#{color_stops_svg(color_stops)}</linearGradient>}
554
+ svg(gradient)
555
+ end
556
+
557
+ def radial_svg(color_stops, cx, cy, r)
558
+ gradient = %Q{<radialGradient id="grad" gradientUnits="userSpaceOnUse" cx="#{cx}" cy="#{cy}" r="#{r}">#{color_stops_svg(color_stops)}</radialGradient>}
559
+ svg(gradient)
560
+ end
561
+
562
+ # color_stops = array of: [stop, color]
563
+ def color_stops_svg(color_stops)
564
+ color_stops.each.map{ |stop, color|
565
+ s = %{<stop offset="#{stop.to_s}"}
566
+ s << %{ stop-color="#{ColorStop.color_to_svg_s(color)}"}
567
+ alpha = ColorStop.color_to_svg_alpha(color)
568
+ s << %{ stop-opacity="#{alpha}"} if alpha != 1
569
+ s << "/>"
570
+ }.join
571
+ end
572
+
573
+ def svg(gradient)
574
+ svg = <<-EOS
575
+ <?xml version="1.0" encoding="utf-8"?>
576
+ <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>
577
+ EOS
578
+ end
579
+
580
+ def _center_position
581
+ opts(Sass::Script::List.new([
582
+ Sass::Script::String.new("center"),
583
+ Sass::Script::String.new("center")
584
+ ],:space))
585
+ end
586
+
587
+ def opts(v)
588
+ v.options = options
589
+ v
590
+ end
591
+
592
+ end
593
+
594
+ module Assertions
595
+ def assert_type(value, type, name = nil)
596
+ return if value.is_a?(Sass::Script.const_get(type))
597
+ err = "#{value.inspect} is not a #{type.to_s.downcase}"
598
+ err = "$#{name}: " + err if name
599
+ raise ArgumentError.new(err)
600
+ end
601
+ end
602
+
603
+ class LinearGradient < Sass::Script::Literal
604
+ include Assertions
605
+ include Functions
606
+ include Compass::Core::SassExtensions::Functions::Constants
607
+ include Compass::Core::SassExtensions::Functions::InlineImage
608
+ end
609
+
610
+ class RadialGradient < Sass::Script::Literal
611
+ include Assertions
612
+ include Functions
613
+ include Compass::Core::SassExtensions::Functions::Constants
614
+ include Compass::Core::SassExtensions::Functions::InlineImage
615
+ end
616
+ end