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.
- data/Rakefile +21 -1
- data/VERSION.yml +1 -1
- data/bin/compass +17 -2
- data/features/command_line.feature +15 -24
- data/features/step_definitions/command_line_steps.rb +3 -3
- data/frameworks/compass/stylesheets/_lemonade.scss +38 -0
- data/frameworks/compass/stylesheets/compass/css3/_background-size.scss +3 -1
- data/frameworks/compass/stylesheets/compass/css3/_box-shadow.scss +1 -1
- data/frameworks/compass/stylesheets/compass/css3/_images.scss +8 -6
- data/frameworks/compass/stylesheets/compass/css3/_transform.scss +3 -3
- data/frameworks/compass/stylesheets/compass/utilities/sprites/_base.scss +40 -0
- data/frameworks/compass/templates/pie/PIE.htc +69 -69
- data/frameworks/compass/templates/pie/manifest.rb +2 -2
- data/lib/compass.rb +3 -2
- data/lib/compass/app_integration/stand_alone/installer.rb +1 -1
- data/lib/compass/commands.rb +1 -1
- data/lib/compass/commands/sprite.rb +87 -0
- data/lib/compass/configuration/adapters.rb +3 -0
- data/lib/compass/dependencies.rb +7 -1
- data/lib/compass/installers/bare_installer.rb +1 -1
- data/lib/compass/quick_cache.rb +15 -0
- data/lib/compass/sass_extensions/functions.rb +3 -3
- data/lib/compass/sass_extensions/functions/gradient_support.rb +60 -21
- data/lib/compass/sass_extensions/functions/image_size.rb +11 -11
- data/lib/compass/sass_extensions/functions/sprites.rb +357 -0
- data/lib/compass/sass_extensions/functions/urls.rb +16 -3
- data/lib/compass/sass_extensions/monkey_patches/traversal.rb +1 -1
- data/lib/compass/sprites.rb +122 -0
- data/test/fixtures/stylesheets/compass/css/gradients.css +18 -1
- data/test/fixtures/stylesheets/compass/css/pie.css +1 -0
- data/test/fixtures/stylesheets/compass/css/transform.css +28 -0
- data/test/fixtures/stylesheets/compass/sass/gradients.sass +9 -0
- data/test/fixtures/stylesheets/image_urls/css/screen.css +2 -0
- data/test/fixtures/stylesheets/image_urls/sass/screen.sass +3 -0
- data/test/fixtures/stylesheets/valid/config.rb +9 -0
- data/test/fixtures/stylesheets/valid/sass/simple.sass +2 -0
- data/test/sass_extensions_test.rb +8 -0
- metadata +40 -8
- 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-
|
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
|
+
}
|
data/lib/compass.rb
CHANGED
@@ -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
|
-
*
|
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
|
|
data/lib/compass/commands.rb
CHANGED
@@ -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
|
data/lib/compass/dependencies.rb
CHANGED
@@ -2,5 +2,11 @@ begin
|
|
2
2
|
require 'sass'
|
3
3
|
rescue LoadError
|
4
4
|
require 'rubygems'
|
5
|
-
|
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
|
-
*
|
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
|
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::
|
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!
|
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
|
-
|
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
|
-
|
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!
|
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
|