compass 0.11.alpha.1 → 0.11.alpha.3

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 (39) hide show
  1. data/Rakefile +21 -1
  2. data/VERSION.yml +1 -1
  3. data/bin/compass +17 -2
  4. data/features/command_line.feature +15 -24
  5. data/features/step_definitions/command_line_steps.rb +3 -3
  6. data/frameworks/compass/stylesheets/_lemonade.scss +38 -0
  7. data/frameworks/compass/stylesheets/compass/css3/_background-size.scss +3 -1
  8. data/frameworks/compass/stylesheets/compass/css3/_box-shadow.scss +1 -1
  9. data/frameworks/compass/stylesheets/compass/css3/_images.scss +8 -6
  10. data/frameworks/compass/stylesheets/compass/css3/_transform.scss +3 -3
  11. data/frameworks/compass/stylesheets/compass/utilities/sprites/_base.scss +40 -0
  12. data/frameworks/compass/templates/pie/PIE.htc +69 -69
  13. data/frameworks/compass/templates/pie/manifest.rb +2 -2
  14. data/lib/compass.rb +3 -2
  15. data/lib/compass/app_integration/stand_alone/installer.rb +1 -1
  16. data/lib/compass/commands.rb +1 -1
  17. data/lib/compass/commands/sprite.rb +87 -0
  18. data/lib/compass/configuration/adapters.rb +3 -0
  19. data/lib/compass/dependencies.rb +7 -1
  20. data/lib/compass/installers/bare_installer.rb +1 -1
  21. data/lib/compass/quick_cache.rb +15 -0
  22. data/lib/compass/sass_extensions/functions.rb +3 -3
  23. data/lib/compass/sass_extensions/functions/gradient_support.rb +60 -21
  24. data/lib/compass/sass_extensions/functions/image_size.rb +11 -11
  25. data/lib/compass/sass_extensions/functions/sprites.rb +357 -0
  26. data/lib/compass/sass_extensions/functions/urls.rb +16 -3
  27. data/lib/compass/sass_extensions/monkey_patches/traversal.rb +1 -1
  28. data/lib/compass/sprites.rb +122 -0
  29. data/test/fixtures/stylesheets/compass/css/gradients.css +18 -1
  30. data/test/fixtures/stylesheets/compass/css/pie.css +1 -0
  31. data/test/fixtures/stylesheets/compass/css/transform.css +28 -0
  32. data/test/fixtures/stylesheets/compass/sass/gradients.sass +9 -0
  33. data/test/fixtures/stylesheets/image_urls/css/screen.css +2 -0
  34. data/test/fixtures/stylesheets/image_urls/sass/screen.sass +3 -0
  35. data/test/fixtures/stylesheets/valid/config.rb +9 -0
  36. data/test/fixtures/stylesheets/valid/sass/simple.sass +2 -0
  37. data/test/sass_extensions_test.rb +8 -0
  38. metadata +40 -8
  39. data/lib/compass/sass_extensions/functions/if.rb +0 -9
@@ -21,7 +21,7 @@ For more information see:
21
21
 
22
22
  CSS PIE is written by and copyright to: Jason Johnston
23
23
 
24
- Compass is using css3pie version 1.0-beta2. It can be upgraded by downloading
24
+ Compass is using css3pie version 1.0-beta3. It can be upgraded by downloading
25
25
  a newer behavior file and replacing the one that comes with compass.
26
26
  }
27
27
 
@@ -36,4 +36,4 @@ Unfornately, due to the suckiness of IE, PIE does not work with relative assets.
36
36
 
37
37
  For more information see:
38
38
  http://css3pie.com/
39
- }
39
+ }
@@ -1,7 +1,7 @@
1
1
  module Compass
2
2
  end
3
3
 
4
- %w(dependencies util sass_extensions core_ext version errors).each do |lib|
4
+ %w(dependencies util sass_extensions core_ext version errors quick_cache).each do |lib|
5
5
  require "compass/#{lib}"
6
6
  end
7
7
 
@@ -13,8 +13,9 @@ module Compass
13
13
  File.expand_path(File.join(File.dirname(__FILE__)))
14
14
  end
15
15
  module_function :base_directory, :lib_directory
16
+ extend QuickCache
16
17
  end
17
18
 
18
- %w(configuration frameworks app_integration actions compiler).each do |lib|
19
+ %w(configuration frameworks app_integration actions compiler sprites).each do |lib|
19
20
  require "compass/#{lib}"
20
21
  end
@@ -61,7 +61,7 @@ This can be done in one of the following ways:
61
61
  compass watch [path/to/project]
62
62
 
63
63
  More Resources:
64
- * Wiki: http://wiki.github.com/chriseppstein/compass
64
+ * Website: http://compass-style.org/
65
65
  * Sass: http://sass-lang.com
66
66
  * Community: http://groups.google.com/group/compass-users/
67
67
 
@@ -5,7 +5,7 @@ require 'compass/commands/registry'
5
5
 
6
6
  %w(base generate_grid_background help list_frameworks project_base
7
7
  update_project watch_project create_project imports installer_command
8
- print_version project_stats stamp_pattern validate_project
8
+ print_version project_stats stamp_pattern sprite validate_project
9
9
  write_configuration interactive unpack_extension).each do |lib|
10
10
  require "compass/commands/#{lib}"
11
11
  end
@@ -0,0 +1,87 @@
1
+ require 'compass/commands/project_base'
2
+ require 'compass/commands/update_project'
3
+
4
+ module Compass
5
+ module Commands
6
+ module SpriteOptionsParser
7
+ def set_options(opts)
8
+ opts.on("-f SPRITE_FILE") do |output_file|
9
+ self.options[:output_file] = output_file
10
+ end
11
+ opts.banner = %Q{
12
+ Usage: compass sprite [options] "images/path/to/sprites/*.png"
13
+
14
+ Description:
15
+ Generate a sprite import based on the given sprite directory.
16
+ Alternatively, you can simply do this in your sass files:
17
+
18
+ @import "sprite-folder/*.png"
19
+
20
+ And a magical, custom made sprite file will be imported.
21
+
22
+ Options:
23
+ }.strip.split("\n").map{|l| l.gsub(/^ {0,10}/,'')}.join("\n")
24
+
25
+ super
26
+ end
27
+ end
28
+ class Sprite < ProjectBase
29
+
30
+ register :sprite
31
+
32
+ def initialize(working_path, options)
33
+ super
34
+ assert_project_directory_exists!
35
+ end
36
+
37
+ def perform
38
+ sprites = Compass::Sprites.new
39
+ relative_uri = options[:uri].gsub(/^#{Compass.configuration.images_dir}\//, '')
40
+ sprite_images = Compass::Sprites.discover_sprites(relative_uri)
41
+ image_names = sprite_images.map{|i| File.basename(i, '.png')}
42
+ sprites.path, sprites.name = Compass::Sprites.path_and_name(relative_uri)
43
+ options[:output_file] ||= File.join(Compass.configuration.sass_path, "sprites", "_#{sprites.name}.#{Compass.configuration.preferred_syntax}")
44
+ contents = sprites.content_for_images(relative_uri, sprites.name, image_names)
45
+ if options[:output_file][-4..-1] != "scss"
46
+ contents = Sass::Engine.new(contents, Compass.sass_engine_options.merge(:syntax => :scss)).to_tree.to_sass
47
+ end
48
+ directory File.dirname(options[:output_file])
49
+ write_file options[:output_file], contents
50
+ end
51
+
52
+ class << self
53
+
54
+ def option_parser(arguments)
55
+ parser = Compass::Exec::CommandOptionParser.new(arguments)
56
+ parser.extend(Compass::Exec::GlobalOptionsParser)
57
+ parser.extend(Compass::Exec::ProjectOptionsParser)
58
+ parser.extend(SpriteOptionsParser)
59
+ end
60
+
61
+ def usage
62
+ option_parser([]).to_s
63
+ end
64
+
65
+ def description(command)
66
+ "Generate an import for your sprites."
67
+ end
68
+
69
+ def parse!(arguments)
70
+ parser = option_parser(arguments)
71
+ parser.parse!
72
+ parse_arguments!(parser, arguments)
73
+ parser.options
74
+ end
75
+
76
+ def parse_arguments!(parser, arguments)
77
+ parser.options[:uri] = arguments.shift
78
+ unless arguments.size == 0
79
+ raise Compass::Error, "Please specify at least one image to sprite."
80
+ end
81
+ end
82
+
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -22,6 +22,8 @@ module Compass
22
22
  plugin_opts[:cache] = cache unless cache.nil?
23
23
  plugin_opts[:cache_location] = cache_path unless cache_path.nil?
24
24
  plugin_opts.merge!(sass_options || {})
25
+ plugin_opts[:load_paths] ||= []
26
+ plugin_opts[:load_paths] << Compass::Sprites.new
25
27
  plugin_opts
26
28
  end
27
29
 
@@ -57,6 +59,7 @@ module Compass
57
59
  load_paths << framework.stylesheets_directory if File.exists?(framework.stylesheets_directory)
58
60
  end
59
61
  load_paths += resolve_additional_import_paths
62
+ load_paths << Compass::Sprites.new
60
63
  load_paths
61
64
  end
62
65
  end
@@ -2,5 +2,11 @@ begin
2
2
  require 'sass'
3
3
  rescue LoadError
4
4
  require 'rubygems'
5
- require 'sass'
5
+ begin
6
+ require 'sass'
7
+ rescue LoadError
8
+ puts "Unable to load Sass. Please install it with one of the following commands:"
9
+ puts " gem install sass --pre"
10
+ raise
11
+ end
6
12
  end
@@ -47,7 +47,7 @@ This can be done in one of the following ways:
47
47
  compass watch [path/to/project]
48
48
 
49
49
  More Resources:
50
- * Wiki: http://wiki.github.com/chriseppstein/compass
50
+ * Website: http://compass-style.org/
51
51
  * Sass: http://sass-lang.com
52
52
  * Community: http://groups.google.com/group/compass-users/
53
53
  NEXTSTEPS
@@ -0,0 +1,15 @@
1
+ module QuickCache
2
+
3
+ # cache a value in memory for just a few seconds
4
+ # This can speed up reads of values that change relatively infrequently
5
+ # but might be read many times in a short burst of reads.
6
+ def quick_cache(key, ttl = 1)
7
+ @quick_cache ||= {}
8
+ if @quick_cache[key] && @quick_cache[key].first > Time.now - ttl
9
+ @quick_cache[key].last
10
+ else
11
+ (@quick_cache[key] = [Time.now, yield]).last
12
+ end
13
+ end
14
+
15
+ end
@@ -2,9 +2,9 @@ module Compass::SassExtensions::Functions
2
2
  end
3
3
 
4
4
  %w(
5
- selectors enumerate urls display if
5
+ selectors enumerate urls display
6
6
  inline_image image_size constants gradient_support
7
- font_files lists colors trig
7
+ font_files lists colors trig sprites
8
8
  ).each do |func|
9
9
  require "compass/sass_extensions/functions/#{func}"
10
10
  end
@@ -22,7 +22,7 @@ module Sass::Script::Functions
22
22
  include Compass::SassExtensions::Functions::Lists
23
23
  include Compass::SassExtensions::Functions::Colors
24
24
  include Compass::SassExtensions::Functions::Trig
25
- include Compass::SassExtensions::Functions::If
25
+ include Compass::SassExtensions::Functions::Sprites
26
26
  end
27
27
 
28
28
  # Wierd that this has to be re-included to pick up sub-modules. Ruby bug?
@@ -138,21 +138,29 @@ module Compass::SassExtensions::Functions::GradientSupport
138
138
  Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)})
139
139
  end
140
140
 
141
- %w(webkit moz o ms svg pie).each do |prefix|
141
+ %w(webkit moz o ms svg pie css2).each do |prefix|
142
142
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
143
143
  def _#{prefix}(*args)
144
- List.new(*args.map! do |a|
145
- if a.is_a?(List)
146
- a.class.new(*a.values.map{|v| v.respond_to?(:to_#{prefix}) ? v.to_#{prefix} : v})
147
- else
148
- a.respond_to?(:to_#{prefix}) ? a.to_#{prefix} : a
149
- end
150
- end)
144
+ List.new(*args.map! {|a| add_prefix(:to_#{prefix}, a)})
151
145
  end
152
146
  RUBY
153
147
  end
154
148
 
155
149
  protected
150
+
151
+ def add_prefix(prefix_method, object)
152
+ if object.is_a?(List)
153
+ object.class.new(object.value.map{|e|
154
+ add_prefix(prefix_method, e)
155
+ })
156
+ elsif object.respond_to?(prefix_method)
157
+ object.options = options
158
+ object.send(prefix_method)
159
+ else
160
+ object
161
+ end
162
+ end
163
+
156
164
  def color_stop?(arg)
157
165
  parse_color_stop(arg)
158
166
  rescue
@@ -219,7 +227,12 @@ module Compass::SassExtensions::Functions::GradientSupport
219
227
 
220
228
  # Returns a comma-delimited list after removing any non-true values
221
229
  def compact(*args)
222
- Sass::Script::List.new(args.reject{|a| !a.to_bool}, :comma)
230
+ sep = :comma
231
+ if args.size == 1 && args.first.is_a?(Sass::Script::List)
232
+ args = args.first.value
233
+ sep = args.first.separator
234
+ end
235
+ Sass::Script::List.new(args.reject{|a| !a.to_bool}, sep)
223
236
  end
224
237
 
225
238
  # Returns a list object from a value that was passed.
@@ -259,28 +272,35 @@ module Compass::SassExtensions::Functions::GradientSupport
259
272
  # Check if any of the arguments passed have a tendency towards vendor prefixing.
260
273
  def prefixed(prefix, *args)
261
274
  method = prefix.value.sub(/^-/,"to_").to_sym
262
- args.map!{|a| a.is_a?(Sass::Script::List) ? a.value : a}.flatten!
275
+ 2.times do
276
+ args.map!{|a| a.is_a?(Sass::Script::List) ? a.value : a}.flatten!
277
+ end
263
278
  Sass::Script::Bool.new(args.any?{|a| a.respond_to?(method)})
264
279
  end
265
280
 
266
- %w(webkit moz o ms svg pie).each do |prefix|
281
+ %w(webkit moz o ms svg pie css2).each do |prefix|
267
282
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
268
283
  def _#{prefix}(*args)
269
- Sass::Script::List.new(args.map! do |a|
270
- a.options = options
271
- if a.is_a?(Sass::Script::List)
272
- Sass::Script::List.new(a.value.map do |v|
273
- v.respond_to?(:to_#{prefix}) ? v.to_#{prefix} : v
274
- end, a.separator)
275
- else
276
- a.respond_to?(:to_#{prefix}) ? a.to_#{prefix} : a
277
- end
278
- end, :comma)
284
+ Sass::Script::List.new(args.map! {|a| add_prefix(:to_#{prefix}, a)}, :comma)
279
285
  end
280
286
  RUBY
281
287
  end
282
288
 
283
289
  protected
290
+
291
+ def add_prefix(prefix_method, object)
292
+ if object.is_a?(Sass::Script::List)
293
+ Sass::Script::List.new(object.value.map{|e|
294
+ add_prefix(prefix_method, e)
295
+ }, object.separator)
296
+ elsif object.respond_to?(prefix_method)
297
+ object.options = options
298
+ object.send(prefix_method)
299
+ else
300
+ object
301
+ end
302
+ end
303
+
284
304
  def color_stop?(arg)
285
305
  arg.is_a?(ColorStop) ||
286
306
  (arg.is_a?(Sass::Script::List) && ColorStop.new(*arg.value)) ||
@@ -375,6 +395,9 @@ module Compass::SassExtensions::Functions::GradientSupport
375
395
  Compass::Logger.new.record(:warning, "PIE does not support radial-gradient.")
376
396
  Sass::Script::String.new("-pie-radial-gradient(unsupported)")
377
397
  end
398
+ def to_css2(options = self.options)
399
+ Sass::Script::String.new("")
400
+ end
378
401
  end
379
402
 
380
403
  class LinearGradient < Sass::Script::Literal
@@ -417,6 +440,9 @@ module Compass::SassExtensions::Functions::GradientSupport
417
440
  # the presence of this attribute helps flag when to render a special rule.
418
441
  Sass::Script::String.new to_s(options)
419
442
  end
443
+ def to_css2(options = self.options)
444
+ Sass::Script::String.new("")
445
+ end
420
446
  end
421
447
 
422
448
  module Functions
@@ -562,6 +588,19 @@ module Compass::SassExtensions::Functions::GradientSupport
562
588
  end
563
589
  end
564
590
 
591
+ def blank(obj)
592
+ case obj
593
+ when Sass::Script::Bool
594
+ Sass::Script::Bool.new !obj.to_bool
595
+ when Sass::Script::String
596
+ Sass::Script::Bool.new obj.value.strip.size == 0
597
+ when Sass::Script::List
598
+ Sass::Script::Bool.new obj.value.size == 0 || obj.value.all?{|el| blank(el).to_bool}
599
+ else
600
+ Sass::Script::Bool.new false
601
+ end
602
+ end
603
+
565
604
  private
566
605
 
567
606
  # After using the sass script parser to parse a string, this reconstructs
@@ -13,17 +13,6 @@ module Compass::SassExtensions::Functions::ImageSize
13
13
  Sass::Script::Number.new(height, ["px"])
14
14
  end
15
15
 
16
- private
17
- def real_path(image_file)
18
- path = image_file.value
19
- # Compute the real path to the image on the file stystem if the images_dir is set.
20
- if Compass.configuration.images_path
21
- File.join(Compass.configuration.images_path, path)
22
- else
23
- File.join(Compass.configuration.project_path, path)
24
- end
25
- end
26
-
27
16
  class ImageProperties
28
17
  def initialize(file)
29
18
  @file = file
@@ -55,6 +44,17 @@ private
55
44
  end
56
45
  end
57
46
 
47
+ private
48
+ def real_path(image_file)
49
+ path = image_file.value
50
+ # Compute the real path to the image on the file stystem if the images_dir is set.
51
+ if Compass.configuration.images_path
52
+ File.join(Compass.configuration.images_path, path)
53
+ else
54
+ File.join(Compass.configuration.project_path, path)
55
+ end
56
+ end
57
+
58
58
  class JPEG
59
59
  attr_reader :width, :height, :bits
60
60
 
@@ -0,0 +1,357 @@
1
+ require 'digest/md5'
2
+
3
+ module Compass::SassExtensions::Functions::Sprites
4
+ ZERO = Sass::Script::Number::new(0)
5
+
6
+ # Provides a consistent interface for getting a variable in ruby
7
+ # from a keyword argument hash that accounts for underscores/dash equivalence
8
+ # and allows the caller to pass a symbol instead of a string.
9
+ module VariableReader
10
+ def get_var(variable_name)
11
+ self[variable_name.to_s.gsub(/-/,"_")]
12
+ end
13
+ end
14
+
15
+ class SpriteMap < Sass::Script::Literal
16
+
17
+ # Changing this string will invalidate all previously generated sprite images.
18
+ # We should do so only when the packing algorithm changes
19
+ SPRITE_VERSION = "1"
20
+
21
+ attr_accessor :image_names, :path, :name, :options
22
+ attr_accessor :images, :width, :height
23
+
24
+ def self.from_uri(uri, context, kwargs)
25
+ path, name = Compass::Sprites.path_and_name(uri.value)
26
+ sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite|
27
+ sprite.gsub(Compass.configuration.images_path+"/", "")
28
+ end
29
+ new(sprites, path, name, context, kwargs)
30
+ end
31
+
32
+ def initialize(image_names, path, name, context, options)
33
+ @image_names, @path, @name, @options = image_names, path, name, options
34
+ @images = nil
35
+ @width = nil
36
+ @height = nil
37
+ @evaluation_context = context
38
+ validate!
39
+ compute_image_metadata!
40
+ end
41
+
42
+ def sprite_names
43
+ image_names.map{|f| Compass::Sprites.sprite_name(f) }
44
+ end
45
+
46
+ def validate!
47
+ for sprite_name in sprite_names
48
+ unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
49
+ raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
50
+ end
51
+ end
52
+ end
53
+
54
+ # Calculates the overal image dimensions
55
+ # collects image sizes and input parameters for each sprite
56
+ def compute_image_metadata!
57
+ @images = []
58
+ @width = 0
59
+ image_names.each do |relative_file|
60
+ file = File.join(Compass.configuration.images_path, relative_file)
61
+ width, height = Compass::SassExtensions::Functions::ImageSize::ImageProperties.new(file).size
62
+ sprite_name = Compass::Sprites.sprite_name(relative_file)
63
+ @width = [@width, width].max
64
+ @images << {
65
+ :name => sprite_name,
66
+ :file => file,
67
+ :relative_file => relative_file,
68
+ :height => height,
69
+ :width => width,
70
+ :repeat => repeat_for(sprite_name),
71
+ :spacing => spacing_for(sprite_name),
72
+ :position => position_for(sprite_name),
73
+ :digest => MD5.file(file).hexdigest
74
+ }
75
+ end
76
+ @images.each_with_index do |image, index|
77
+ if index == 0
78
+ image[:top] = 0
79
+ else
80
+ last_image = @images[index-1]
81
+ image[:top] = last_image[:top] + last_image[:height] + [image[:spacing], last_image[:spacing]].max
82
+ end
83
+ if image[:position].unit_str == "%"
84
+ image[:left] = (@width - image[:width]) * (image[:position].value / 100)
85
+ else
86
+ image[:left] = image[:position].value
87
+ end
88
+ end
89
+ @height = @images.last[:top] + @images.last[:height]
90
+ end
91
+
92
+ def position_for(name)
93
+ options.get_var("#{name}-position") || options.get_var("position") || Sass::Script::Number.new(0, ["px"])
94
+ end
95
+
96
+ def repeat_for(name)
97
+ if (var = options.get_var("#{name}-repeat"))
98
+ var.value
99
+ elsif (var = options.get_var("repeat"))
100
+ var.value
101
+ else
102
+ "no-repeat"
103
+ end
104
+ end
105
+
106
+ def spacing_for(name)
107
+ (options.get_var("#{name}-spacing") ||
108
+ options.get_var("spacing") ||
109
+ ZERO).value
110
+ end
111
+
112
+ def image_for(name)
113
+ @images.detect{|img| img[:name] == name}
114
+ end
115
+
116
+ # Calculate the size of the sprite
117
+ def size
118
+ [width, height]
119
+ end
120
+
121
+ # Generate a sprite image if necessary
122
+ def generate
123
+ if generation_required?
124
+ save!(construct_sprite)
125
+ end
126
+ end
127
+
128
+ def generation_required?
129
+ !File.exists?(filename) || outdated?
130
+ end
131
+
132
+ def require_png_library!
133
+ begin
134
+ require 'oily_png'
135
+ rescue LoadError
136
+ require 'chunky_png'
137
+ end
138
+ end
139
+
140
+ # Returns a PNG object
141
+ def construct_sprite
142
+ require_png_library!
143
+ output_png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
144
+ images.each do |image|
145
+ input_png = ChunkyPNG::Image.from_file(image[:file])
146
+ if image[:repeat] == "no-repeat"
147
+ output_png.replace input_png, image[:left], image[:top]
148
+ else
149
+ x = image[:left] - (image[:left] / image[:width]).ceil * image[:width]
150
+ while x < width do
151
+ output_png.replace input_png, x, image[:top]
152
+ x += image[:width]
153
+ end
154
+ end
155
+ end
156
+ output_png
157
+ end
158
+
159
+ # The on-the-disk filename of the sprite
160
+ def filename
161
+ File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
162
+ end
163
+
164
+ def uniqueness_hash
165
+ @uniqueness_hash ||= begin
166
+ sum = MD5.md5(SPRITE_VERSION)
167
+ sum << path
168
+ images.each do |image|
169
+ [:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
170
+ sum << image[attr].to_s
171
+ end
172
+ end
173
+ sum.hexdigest[0...10]
174
+ end
175
+ @uniqueness_hash
176
+ end
177
+
178
+ # saves the sprite for later retrieval
179
+ def save!(output_png)
180
+ output_png.save filename
181
+ end
182
+
183
+ # All the full-path filenames involved in this sprite
184
+ def image_filenames
185
+ image_names.map do |image_name|
186
+ File.join(Compass.configuration.images_path, image_name)
187
+ end
188
+ end
189
+
190
+ # Checks whether this sprite is outdated
191
+ def outdated?
192
+ last_update = self.mtime
193
+ image_filenames.each do |image|
194
+ return true if File.mtime(image) > last_update
195
+ end
196
+ false
197
+ end
198
+
199
+ def mtime
200
+ File.mtime(filename)
201
+ end
202
+
203
+ def inspect
204
+ to_s
205
+ end
206
+
207
+ def to_s(options = self.options)
208
+ sprite_url(self).value
209
+ end
210
+
211
+ def respond_to?(meth)
212
+ super || @evaluation_context.respond_to?(meth)
213
+ end
214
+
215
+ def method_missing(meth, *args, &block)
216
+ if @evaluation_context.respond_to?(meth)
217
+ @evaluation_context.send(meth, *args, &block)
218
+ else
219
+ super
220
+ end
221
+ end
222
+
223
+ end
224
+
225
+ # Creates a SpriteMap object. A sprite map, when used in a property is the same
226
+ # as calling sprite-url. So the following background properties are equivalent:
227
+ #
228
+ # $icons: sprite-map("icons/*.png");
229
+ # background: sprite-url($icons) no-repeat;
230
+ # background: $icons no-repeat;
231
+ #
232
+ # The sprite map object will generate the sprite map image, if necessary,
233
+ # the first time it is converted to a url. Simply constructing it has no side-effects.
234
+ def sprite_map(glob, kwargs = {})
235
+ kwargs.extend VariableReader
236
+ SpriteMap.from_uri(glob, self, kwargs)
237
+ end
238
+ Sass::Script::Functions.declare :sprite_map, [:glob], :var_kwargs => true
239
+
240
+ # Returns the image and background position for use in a single shorthand property:
241
+ #
242
+ # $icons: sprite-map("icons/*.png"); // contains icons/new.png among others.
243
+ # background: sprite($icons, new) no-repeat;
244
+ #
245
+ # Becomes:
246
+ #
247
+ # background: url('/images/icons.png?12345678') 0 -24px no-repeat;
248
+ def sprite(map, sprite, offset_x = ZERO, offset_y = ZERO)
249
+ unless map.is_a?(SpriteMap)
250
+ missing_sprite!("sprite")
251
+ end
252
+ unless sprite.is_a?(Sass::Script::String)
253
+ raise Sass::SyntaxError, %Q(The second argument to sprite() must be a sprite name. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
254
+ end
255
+ url = sprite_url(map)
256
+ position = sprite_position(map, sprite, offset_x, offset_y)
257
+ Sass::Script::List.new([url] + position.value, :space)
258
+ end
259
+ Sass::Script::Functions.declare :sprite, [:map, :sprite]
260
+ Sass::Script::Functions.declare :sprite, [:map, :sprite, :offset_x]
261
+ Sass::Script::Functions.declare :sprite, [:map, :sprite, :offset_x, :offset_y]
262
+
263
+ # Returns the name of a sprite map
264
+ # The name is derived from the folder than contains the sprites.
265
+ def sprite_map_name(map)
266
+ unless map.is_a?(SpriteMap)
267
+ missing_sprite!("sprite-map-name")
268
+ end
269
+ Sass::Script::String.new(map.name)
270
+ end
271
+ Sass::Script::Functions.declare :sprite_name, [:sprite]
272
+
273
+ # Returns the path to the original image file for the sprite with the given name
274
+ def sprite_file(map, sprite)
275
+ unless map.is_a?(SpriteMap)
276
+ missing_sprite!("sprite-file")
277
+ end
278
+ if image = map.image_for(sprite.value)
279
+ Sass::Script::String.new(image[:relative_file])
280
+ else
281
+ missing_image!(map, sprite)
282
+ end
283
+ end
284
+ Sass::Script::Functions.declare :sprite_file, [:map, :sprite]
285
+
286
+ # Returns a url to the sprite image.
287
+ def sprite_url(map)
288
+ unless map.is_a?(SpriteMap)
289
+ missing_sprite!("sprite-url")
290
+ end
291
+ map.generate
292
+ image_url(Sass::Script::String.new("#{map.path}-#{map.uniqueness_hash}.png"),
293
+ Sass::Script::Bool.new(false),
294
+ Sass::Script::Bool.new(false))
295
+ end
296
+ Sass::Script::Functions.declare :sprite_url, [:map]
297
+
298
+ # Returns the position for the original image in the sprite.
299
+ # This is suitable for use as a value to background-position:
300
+ #
301
+ # $icons: sprite-map("icons/*.png");
302
+ # background-position: sprite-position($icons, new);
303
+ #
304
+ # Might generate something like:
305
+ #
306
+ # background-position: 0 -34px;
307
+ #
308
+ # You can adjust the background relative to this position by passing values for
309
+ # `$offset-x` and `$offset-y`:
310
+ #
311
+ # $icons: sprite-map("icons/*.png");
312
+ # background-position: sprite-position($icons, new, 3px, -2px);
313
+ #
314
+ # Would change the above output to:
315
+ #
316
+ # background-position: 3px -36px;
317
+ def sprite_position(map, sprite = nil, offset_x = ZERO, offset_y = ZERO)
318
+ unless map.is_a?(SpriteMap)
319
+ missing_sprite!("sprite-position")
320
+ end
321
+ unless sprite && sprite.is_a?(Sass::Script::String)
322
+ raise Sass::SyntaxError, %Q(The second argument to sprite-position must be a sprite name. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
323
+ end
324
+ image = map.image_for(sprite.value)
325
+ unless image
326
+ missing_image!(map, sprite)
327
+ end
328
+ if offset_x.unit_str == "%"
329
+ x = offset_x # CE: Shouldn't this be a percentage of the total width?
330
+ else
331
+ x = offset_x.value - image[:left]
332
+ x = Sass::Script::Number.new(x, x == 0 ? [] : ["px"])
333
+ end
334
+ y = offset_y.value - image[:top]
335
+ y = Sass::Script::Number.new(y, y == 0 ? [] : ["px"])
336
+ Sass::Script::List.new([x, y],:space)
337
+ end
338
+ Sass::Script::Functions.declare :sprite_position, [:map]
339
+ Sass::Script::Functions.declare :sprite_position, [:map, :sprite]
340
+ Sass::Script::Functions.declare :sprite_position, [:map, :sprite, :offset_x]
341
+ Sass::Script::Functions.declare :sprite_position, [:map, :sprite, :offset_x, :offset_y]
342
+
343
+ def sprite_image(*args)
344
+ raise Sass::SyntaxError, %Q(The sprite-image() function has been replaced by sprite(). See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
345
+ end
346
+
347
+ protected
348
+
349
+ def missing_image!(map, sprite)
350
+ raise Sass::SyntaxError, "No sprite called #{sprite} found in sprite map #{map.path}/#{map.name}. Did you mean one of: #{map.sprite_names.join(", ")}"
351
+ end
352
+
353
+ def missing_sprite!(function_name)
354
+ raise Sass::SyntaxError, %Q(The first argument to #{function_name}() must be a sprite map. See http://beta.compass-style.org/help/tutorials/spriting/ for more information.)
355
+ end
356
+
357
+ end