compass 0.11.beta.3 → 0.11.beta.4
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/VERSION.yml +1 -1
- data/features/extensions.feature +7 -0
- data/frameworks/compass/stylesheets/compass/css3/_transform.scss +4 -4
- data/lib/compass.rb +6 -3
- data/lib/compass/app_integration/rails/templates/compass-install-rails.rb +4 -4
- data/lib/compass/configuration.rb +2 -1
- data/lib/compass/configuration/defaults.rb +5 -0
- data/lib/compass/configuration/helpers.rb +14 -6
- data/lib/compass/sass_extensions.rb +1 -0
- data/lib/compass/sass_extensions/functions/sprites.rb +10 -229
- data/lib/compass/sass_extensions/sprites.rb +14 -0
- data/lib/compass/sass_extensions/sprites/base.rb +183 -0
- data/lib/compass/sass_extensions/sprites/engines.rb +1 -0
- data/lib/compass/sass_extensions/sprites/engines/chunky_png_engine.rb +33 -0
- data/lib/compass/sass_extensions/sprites/image.rb +89 -0
- data/lib/compass/{sprites.rb → sass_extensions/sprites/sprite_map.rb} +46 -52
- data/lib/compass/sass_extensions/sprites/sprites.rb +26 -0
- data/lib/compass/util.rb +11 -0
- data/test/fixtures/stylesheets/compass/css/transform.css +2 -2
- data/test/test_case_helper.rb +1 -1
- metadata +11 -8
- data/examples/compass/saved_stylesheets/PIE.htc +0 -77
- data/examples/compass/src/sprites/_emblem.scss +0 -90
- data/examples/compass/src/sprites/_flag.scss +0 -44
- data/lib/compass/core_ext.rb +0 -14
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'compass/sass_extensions/sprites/sprites'
|
3
|
+
require 'compass/sass_extensions/sprites/sprite_map'
|
4
|
+
require 'compass/sass_extensions/sprites/image'
|
5
|
+
require 'compass/sass_extensions/sprites/base'
|
6
|
+
require 'compass/sass_extensions/sprites/engines'
|
7
|
+
|
8
|
+
module Compass
|
9
|
+
module SassExtensions
|
10
|
+
module Sprites
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module Compass
|
2
|
+
module SassExtensions
|
3
|
+
module Sprites
|
4
|
+
class Base < Sass::Script::Literal
|
5
|
+
def self.from_uri(uri, context, kwargs)
|
6
|
+
sprite_map = ::Compass::SpriteMap.new(uri.value, {})
|
7
|
+
|
8
|
+
sprites = sprite_map.files.map do |sprite|
|
9
|
+
sprite.gsub(Compass.configuration.images_path+"/", "")
|
10
|
+
end
|
11
|
+
new(sprites, sprite_map.path, sprite_map.name, context, kwargs)
|
12
|
+
end
|
13
|
+
|
14
|
+
def require_engine!
|
15
|
+
self.class.send(:include, eval("::Compass::SassExtensions::Sprites::#{modulize}Engine"))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Changing this string will invalidate all previously generated sprite images.
|
19
|
+
# We should do so only when the packing algorithm changes
|
20
|
+
SPRITE_VERSION = "1"
|
21
|
+
|
22
|
+
attr_accessor :image_names, :path, :name, :options
|
23
|
+
attr_accessor :images, :width, :height
|
24
|
+
|
25
|
+
|
26
|
+
def initialize(image_names, path, name, context, options)
|
27
|
+
require_engine!
|
28
|
+
@image_names, @path, @name, @options = image_names, path, name, options
|
29
|
+
@images = nil
|
30
|
+
@width = nil
|
31
|
+
@height = nil
|
32
|
+
@evaluation_context = context
|
33
|
+
validate!
|
34
|
+
compute_image_metadata!
|
35
|
+
end
|
36
|
+
|
37
|
+
# Calculate the size of the sprite
|
38
|
+
def size
|
39
|
+
[width, height]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Calculates the overal image dimensions
|
43
|
+
# collects image sizes and input parameters for each sprite
|
44
|
+
def compute_image_metadata!
|
45
|
+
@width = 0
|
46
|
+
init_images
|
47
|
+
compute_image_positions!
|
48
|
+
@height = @images.last.top + @images.last.height
|
49
|
+
end
|
50
|
+
|
51
|
+
def init_images
|
52
|
+
@images = image_names.collect do |relative_file|
|
53
|
+
image = Compass::SassExtensions::Sprites::Image.new(self, relative_file, options)
|
54
|
+
@width = [ @width, image.width + image.offset ].max
|
55
|
+
image
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Calculates the overal image dimensions
|
60
|
+
# collects image sizes and input parameters for each sprite
|
61
|
+
def compute_image_positions!
|
62
|
+
@images.each_with_index do |image, index|
|
63
|
+
image.left = image.position.unit_str == "%" ? (@width - image.width) * (image.position.value / 100) : image.position.value
|
64
|
+
next if index == 0
|
65
|
+
last_image = @images[index-1]
|
66
|
+
image.top = last_image.top + last_image.height + [image.spacing, last_image.spacing].max
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def image_for(name)
|
71
|
+
@images.detect { |img| img.name == name}
|
72
|
+
end
|
73
|
+
|
74
|
+
def has_hover?(name)
|
75
|
+
!image_for("#{name}_hover").nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def has_target?(name)
|
79
|
+
!image_for("#{name}_target").nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
def has_active?(name)
|
83
|
+
!image_for("#{name}_active").nil?
|
84
|
+
end
|
85
|
+
|
86
|
+
def sprite_names
|
87
|
+
image_names.map { |f| File.basename(f, '.png') }
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate!
|
91
|
+
for sprite_name in sprite_names
|
92
|
+
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
|
93
|
+
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# The on-the-disk filename of the sprite
|
99
|
+
def filename
|
100
|
+
File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Generate a sprite image if necessary
|
104
|
+
def generate
|
105
|
+
if generation_required?
|
106
|
+
sprite_data = construct_sprite
|
107
|
+
save!(sprite_data)
|
108
|
+
Compass.configuration.run_callback(:sprite_generated, sprite_data)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def generation_required?
|
113
|
+
!File.exists?(filename) || outdated?
|
114
|
+
end
|
115
|
+
|
116
|
+
def uniqueness_hash
|
117
|
+
@uniqueness_hash ||= begin
|
118
|
+
sum = Digest::MD5.new
|
119
|
+
sum << SPRITE_VERSION
|
120
|
+
sum << path
|
121
|
+
images.each do |image|
|
122
|
+
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
|
123
|
+
sum << image.send(attr).to_s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
sum.hexdigest[0...10]
|
127
|
+
end
|
128
|
+
@uniqueness_hash
|
129
|
+
end
|
130
|
+
|
131
|
+
def save!(output_png)
|
132
|
+
saved = output_png.save filename
|
133
|
+
Compass.configuration.run_callback(:sprite_saved, filename)
|
134
|
+
saved
|
135
|
+
end
|
136
|
+
|
137
|
+
# All the full-path filenames involved in this sprite
|
138
|
+
def image_filenames
|
139
|
+
@images.map(&:file)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Checks whether this sprite is outdated
|
143
|
+
def outdated?
|
144
|
+
if File.exists?(filename)
|
145
|
+
return @images.map(&:mtime).any? { |mtime| mtime > self.mtime }
|
146
|
+
end
|
147
|
+
true
|
148
|
+
end
|
149
|
+
|
150
|
+
def mtime
|
151
|
+
File.mtime(filename)
|
152
|
+
end
|
153
|
+
|
154
|
+
def inspect
|
155
|
+
to_s
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_s(options = self.options)
|
159
|
+
sprite_url(self).value
|
160
|
+
end
|
161
|
+
|
162
|
+
def respond_to?(meth)
|
163
|
+
super || @evaluation_context.respond_to?(meth)
|
164
|
+
end
|
165
|
+
|
166
|
+
def method_missing(meth, *args, &block)
|
167
|
+
if @evaluation_context.respond_to?(meth)
|
168
|
+
@evaluation_context.send(meth, *args, &block)
|
169
|
+
else
|
170
|
+
super
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
def modulize
|
177
|
+
@modulize ||= Compass::configuration.sprite_engine.to_s.scan(/([^_.]+)/).flatten.map {|chunk| "#{chunk[0].chr.upcase}#{chunk[1..-1]}" }.join
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'compass/sass_extensions/sprites/engines/chunky_png_engine'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'oily_png'
|
3
|
+
rescue LoadError
|
4
|
+
require 'chunky_png'
|
5
|
+
end
|
6
|
+
|
7
|
+
module Compass
|
8
|
+
module SassExtensions
|
9
|
+
module Sprites
|
10
|
+
module ChunkyPngEngine
|
11
|
+
|
12
|
+
# Returns a PNG object
|
13
|
+
def construct_sprite
|
14
|
+
#require_png_library!
|
15
|
+
output_png = ChunkyPNG::Image.new(width, height, ChunkyPNG::Color::TRANSPARENT)
|
16
|
+
images.each do |image|
|
17
|
+
input_png = ChunkyPNG::Image.from_file(image.file)
|
18
|
+
if image.repeat == "no-repeat"
|
19
|
+
output_png.replace! input_png, image.left, image.top
|
20
|
+
else
|
21
|
+
x = image.left - (image.left / image.width).ceil * image.width
|
22
|
+
while x < width do
|
23
|
+
output_png.replace! input_png, x, image.top
|
24
|
+
x += image.width
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
output_png
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Compass
|
2
|
+
module SassExtensions
|
3
|
+
module Sprites
|
4
|
+
class Image
|
5
|
+
attr_reader :relative_file, :options, :base
|
6
|
+
attr_accessor :top, :left
|
7
|
+
|
8
|
+
def initialize(base, relative_file, options)
|
9
|
+
@base, @relative_file, @options = base, relative_file, options
|
10
|
+
@left = @top = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def file
|
14
|
+
File.join(Compass.configuration.images_path, relative_file)
|
15
|
+
end
|
16
|
+
|
17
|
+
def width
|
18
|
+
dimensions.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def height
|
22
|
+
dimensions.last
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
File.basename(relative_file, '.png')
|
27
|
+
end
|
28
|
+
|
29
|
+
def repeat
|
30
|
+
[ "#{name}-repeat", "repeat" ].each { |which|
|
31
|
+
if var = options.get_var(which)
|
32
|
+
return var.value
|
33
|
+
end
|
34
|
+
}
|
35
|
+
"no-repeat"
|
36
|
+
end
|
37
|
+
|
38
|
+
def position
|
39
|
+
options.get_var("#{name}-position") || options.get_var("position") || Sass::Script::Number.new(0, ["px"])
|
40
|
+
end
|
41
|
+
|
42
|
+
def offset
|
43
|
+
(position.unitless? || position.unit_str == "px") ? position.value : 0
|
44
|
+
end
|
45
|
+
|
46
|
+
def spacing
|
47
|
+
(options.get_var("#{name}-spacing") || options.get_var("spacing") || Sass::Script::Number.new(0)).value
|
48
|
+
end
|
49
|
+
|
50
|
+
def digest
|
51
|
+
Digest::MD5.file(file).hexdigest
|
52
|
+
end
|
53
|
+
|
54
|
+
def mtime
|
55
|
+
File.mtime(file)
|
56
|
+
end
|
57
|
+
|
58
|
+
def hover?
|
59
|
+
base.has_hover?(name)
|
60
|
+
end
|
61
|
+
|
62
|
+
def hover
|
63
|
+
base.image_for("#{name}_hover")
|
64
|
+
end
|
65
|
+
|
66
|
+
def target?
|
67
|
+
base.has_target?(name)
|
68
|
+
end
|
69
|
+
|
70
|
+
def target
|
71
|
+
base.image_for("#{name}_target")
|
72
|
+
end
|
73
|
+
|
74
|
+
def active?
|
75
|
+
base.has_active?(name)
|
76
|
+
end
|
77
|
+
|
78
|
+
def active
|
79
|
+
base.image_for("#{name}_active")
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def dimensions
|
84
|
+
@dimensions ||= Compass::SassExtensions::Functions::ImageSize::ImageProperties.new(file).size
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -1,41 +1,51 @@
|
|
1
1
|
module Compass
|
2
|
-
class
|
3
|
-
|
4
|
-
attr_accessor :path
|
5
|
-
|
6
|
-
class << self
|
7
|
-
def path_and_name(uri)
|
8
|
-
if uri =~ %r{((.+/)?(.+))/(.+?)\.png}
|
9
|
-
[$1, $3, $4]
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def discover_sprites(uri)
|
14
|
-
glob = File.join(Compass.configuration.images_path, uri)
|
15
|
-
Dir.glob(glob).sort
|
16
|
-
end
|
2
|
+
class SpriteMap
|
3
|
+
attr_reader :uri, :options
|
17
4
|
|
18
|
-
|
19
|
-
|
20
|
-
|
5
|
+
def initialize(uri, options)
|
6
|
+
@uri, @options = uri, options
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
ensure_path_and_name!
|
11
|
+
@name
|
12
|
+
end
|
21
13
|
|
14
|
+
def path
|
15
|
+
ensure_path_and_name!
|
16
|
+
@path
|
22
17
|
end
|
23
18
|
|
24
|
-
def
|
25
|
-
|
19
|
+
def files
|
20
|
+
@files ||= Dir[File.join(Compass.configuration.images_path, uri)].sort
|
26
21
|
end
|
27
22
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
def sprite_names
|
24
|
+
@sprite_names ||= files.collect { |file| File.basename(file, '.png') }
|
25
|
+
end
|
26
|
+
|
27
|
+
def sass_options
|
28
|
+
@sass_options ||= options.merge(:filename => name, :syntax => :scss, :importer => self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def mtime
|
32
|
+
Compass.quick_cache("mtime:#{uri}") do
|
33
|
+
files.collect { |file| File.mtime(file) }.max
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
|
-
def
|
37
|
+
def sass_engine
|
38
|
+
Sass::Engine.new(content_for_images, options)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def ensure_path_and_name!
|
43
|
+
return if @path && @name
|
44
|
+
uri =~ %r{((.+/)?(.+))/(.+?)\.png}
|
45
|
+
@path, @name = $1, $3
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_for_images(skip_overrides = false)
|
39
49
|
<<-SCSS
|
40
50
|
@import "compass/utilities/sprites/base";
|
41
51
|
|
@@ -46,8 +56,9 @@ $#{name}-sprite-dimensions: false !default;
|
|
46
56
|
$#{name}-position: 0% !default;
|
47
57
|
$#{name}-spacing: 0 !default;
|
48
58
|
$#{name}-repeat: no-repeat !default;
|
59
|
+
$#{name}-prefix: '' !default;
|
49
60
|
|
50
|
-
#{skip_overrides ? "$#{name}-sprites: sprite-map(\"#{uri}\");" : generate_overrides
|
61
|
+
#{skip_overrides ? "$#{name}-sprites: sprite-map(\"#{uri}\");" : generate_overrides }
|
51
62
|
|
52
63
|
// All sprites should extend this class
|
53
64
|
// The #{name}-sprite mixin will do so for you.
|
@@ -79,44 +90,26 @@ $#{name}-repeat: no-repeat !default;
|
|
79
90
|
|
80
91
|
// Generates a class for each sprited image.
|
81
92
|
@mixin all-#{name}-sprites($dimensions: $#{name}-sprite-dimensions, $prefix: sprite-map-name($#{name}-sprites)) {
|
82
|
-
@include #{name}-sprites(#{
|
93
|
+
@include #{name}-sprites(#{sprite_names.join(" ")}, $dimensions, $prefix);
|
83
94
|
}
|
84
95
|
SCSS
|
85
96
|
end
|
86
97
|
|
87
|
-
def
|
88
|
-
[self.class.name + ":" + File.dirname(File.expand_path(uri)),
|
89
|
-
File.basename(uri)]
|
90
|
-
end
|
91
|
-
|
92
|
-
def mtime(uri, options)
|
93
|
-
Compass.quick_cache("mtime:#{uri}") do
|
94
|
-
self.path, self.name = Compass::Sprites.path_and_name(uri)
|
95
|
-
glob = File.join(Compass.configuration.images_path, uri)
|
96
|
-
Dir.glob(glob).inject(Time.at(0)) do |max_time, file|
|
97
|
-
(t = File.mtime(file)) > max_time ? t : max_time
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def to_s
|
103
|
-
""
|
104
|
-
end
|
105
|
-
|
106
|
-
def generate_overrides(uri, name,images)
|
98
|
+
def generate_overrides
|
107
99
|
content = <<-TXT
|
108
100
|
// These variables control the generated sprite output
|
109
101
|
// You can override them selectively before you import this file.
|
110
102
|
TXT
|
111
|
-
|
103
|
+
sprite_names.map do |sprite_name|
|
112
104
|
content += <<-SCSS
|
113
105
|
$#{name}-#{sprite_name}-position: $#{name}-position !default;
|
114
106
|
$#{name}-#{sprite_name}-spacing: $#{name}-spacing !default;
|
115
107
|
$#{name}-#{sprite_name}-repeat: $#{name}-repeat !default;
|
116
108
|
SCSS
|
117
109
|
end.join
|
110
|
+
|
118
111
|
content += "\n$#{name}-sprites: sprite-map(\"#{uri}\",\n"
|
119
|
-
content +=
|
112
|
+
content += sprite_names.map do |sprite_name|
|
120
113
|
%Q{ $#{sprite_name}-position: $#{name}-#{sprite_name}-position,
|
121
114
|
$#{sprite_name}-spacing: $#{name}-#{sprite_name}-spacing,
|
122
115
|
$#{sprite_name}-repeat: $#{name}-#{sprite_name}-repeat}
|
@@ -125,3 +118,4 @@ $#{name}-#{sprite_name}-repeat: $#{name}-repeat !default;
|
|
125
118
|
end
|
126
119
|
end
|
127
120
|
end
|
121
|
+
|