merge
This commit is contained in:
commit
da43418af0
@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
compass (0.11.beta.2.0dc38bc)
|
||||
compass (0.11.beta.2.fb61e54)
|
||||
chunky_png (~> 0.12.0)
|
||||
sass (>= 3.1.0.alpha.218)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
require 'digest/md5'
|
||||
require 'compass/sass_extensions/sprites/image'
|
||||
require 'compass/sass_extensions/sprites/base'
|
||||
|
||||
module Compass::SassExtensions::Functions::Sprites
|
||||
ZERO = Sass::Script::Number::new(0)
|
||||
@ -13,45 +14,7 @@ module Compass::SassExtensions::Functions::Sprites
|
||||
end
|
||||
end
|
||||
|
||||
class SpriteMap < Sass::Script::Literal
|
||||
|
||||
# Changing this string will invalidate all previously generated sprite images.
|
||||
# We should do so only when the packing algorithm changes
|
||||
SPRITE_VERSION = "1"
|
||||
|
||||
attr_accessor :image_names, :path, :name, :options
|
||||
attr_accessor :images, :width, :height
|
||||
|
||||
def self.from_uri(uri, context, kwargs)
|
||||
path, name = Compass::Sprites.path_and_name(uri.value)
|
||||
sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite|
|
||||
sprite.gsub(Compass.configuration.images_path+"/", "")
|
||||
end
|
||||
new(sprites, path, name, context, kwargs)
|
||||
end
|
||||
|
||||
def initialize(image_names, path, name, context, options)
|
||||
@image_names, @path, @name, @options = image_names, path, name, options
|
||||
@images = nil
|
||||
@width = nil
|
||||
@height = nil
|
||||
@evaluation_context = context
|
||||
validate!
|
||||
compute_image_metadata!
|
||||
end
|
||||
|
||||
def sprite_names
|
||||
image_names.map{|f| Compass::Sprites.sprite_name(f) }
|
||||
end
|
||||
|
||||
def validate!
|
||||
for sprite_name in sprite_names
|
||||
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
|
||||
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SpriteMap < Compass::SassExtensions::Sprites::Base
|
||||
# Calculates the overal image dimensions
|
||||
# collects image sizes and input parameters for each sprite
|
||||
def compute_image_metadata!
|
||||
@ -88,19 +51,6 @@ module Compass::SassExtensions::Functions::Sprites
|
||||
[width, height]
|
||||
end
|
||||
|
||||
# Generate a sprite image if necessary
|
||||
def generate
|
||||
if generation_required?
|
||||
sprite_data = construct_sprite
|
||||
save!(sprite_data)
|
||||
Compass.configuration.run_callback(:sprite_generated, sprite_data)
|
||||
end
|
||||
end
|
||||
|
||||
def generation_required?
|
||||
!File.exists?(filename) || outdated?
|
||||
end
|
||||
|
||||
def require_png_library!
|
||||
begin
|
||||
require 'oily_png'
|
||||
@ -127,74 +77,6 @@ module Compass::SassExtensions::Functions::Sprites
|
||||
end
|
||||
output_png
|
||||
end
|
||||
|
||||
# The on-the-disk filename of the sprite
|
||||
def filename
|
||||
File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
|
||||
end
|
||||
|
||||
def uniqueness_hash
|
||||
@uniqueness_hash ||= begin
|
||||
sum = Digest::MD5.new
|
||||
sum << SPRITE_VERSION
|
||||
sum << path
|
||||
images.each do |image|
|
||||
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
|
||||
sum << image.send(attr).to_s
|
||||
end
|
||||
end
|
||||
sum.hexdigest[0...10]
|
||||
end
|
||||
@uniqueness_hash
|
||||
end
|
||||
|
||||
# saves the sprite for later retrieval
|
||||
def save!(output_png)
|
||||
saved = output_png.save filename
|
||||
Compass.configuration.run_callback(:sprite_saved, filename)
|
||||
saved
|
||||
end
|
||||
|
||||
# All the full-path filenames involved in this sprite
|
||||
def image_filenames
|
||||
image_names.map do |image_name|
|
||||
File.join(Compass.configuration.images_path, image_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Checks whether this sprite is outdated
|
||||
def outdated?
|
||||
last_update = self.mtime
|
||||
image_filenames.each do |image|
|
||||
return true if File.mtime(image) > last_update
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def mtime
|
||||
File.mtime(filename)
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
|
||||
def to_s(options = self.options)
|
||||
sprite_url(self).value
|
||||
end
|
||||
|
||||
def respond_to?(meth)
|
||||
super || @evaluation_context.respond_to?(meth)
|
||||
end
|
||||
|
||||
def method_missing(meth, *args, &block)
|
||||
if @evaluation_context.respond_to?(meth)
|
||||
@evaluation_context.send(meth, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Creates a SpriteMap object. A sprite map, when used in a property is the same
|
||||
|
117
lib/compass/sass_extensions/sprites/base.rb
Normal file
117
lib/compass/sass_extensions/sprites/base.rb
Normal file
@ -0,0 +1,117 @@
|
||||
module Compass
|
||||
module SassExtensions
|
||||
module Sprites
|
||||
class Base < Sass::Script::Literal
|
||||
# Changing this string will invalidate all previously generated sprite images.
|
||||
# We should do so only when the packing algorithm changes
|
||||
SPRITE_VERSION = "1"
|
||||
|
||||
attr_accessor :image_names, :path, :name, :options
|
||||
attr_accessor :images, :width, :height
|
||||
|
||||
def self.from_uri(uri, context, kwargs)
|
||||
path, name = Compass::Sprites.path_and_name(uri.value)
|
||||
sprites = Compass::Sprites.discover_sprites(uri.value).map do |sprite|
|
||||
sprite.gsub(Compass.configuration.images_path+"/", "")
|
||||
end
|
||||
new(sprites, path, name, context, kwargs)
|
||||
end
|
||||
|
||||
def initialize(image_names, path, name, context, options)
|
||||
@image_names, @path, @name, @options = image_names, path, name, options
|
||||
@images = nil
|
||||
@width = nil
|
||||
@height = nil
|
||||
@evaluation_context = context
|
||||
validate!
|
||||
compute_image_metadata!
|
||||
end
|
||||
|
||||
def sprite_names
|
||||
image_names.map{|f| Compass::Sprites.sprite_name(f) }
|
||||
end
|
||||
|
||||
def validate!
|
||||
for sprite_name in sprite_names
|
||||
unless sprite_name =~ /\A#{Sass::SCSS::RX::IDENT}\Z/
|
||||
raise Sass::SyntaxError, "#{sprite_name} must be a legal css identifier"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The on-the-disk filename of the sprite
|
||||
def filename
|
||||
File.join(Compass.configuration.images_path, "#{path}-#{uniqueness_hash}.png")
|
||||
end
|
||||
|
||||
# Calculates the overal image dimensions
|
||||
# collects image sizes and input parameters for each sprite
|
||||
def compute_image_metadata!
|
||||
end
|
||||
|
||||
# Generate a sprite image if necessary
|
||||
def generate
|
||||
if generation_required?
|
||||
sprite_data = construct_sprite
|
||||
save!(sprite_data)
|
||||
Compass.configuration.run_callback(:sprite_generated, sprite_data)
|
||||
end
|
||||
end
|
||||
|
||||
def generation_required?
|
||||
!File.exists?(filename) || outdated?
|
||||
end
|
||||
|
||||
def uniqueness_hash
|
||||
@uniqueness_hash ||= begin
|
||||
sum = Digest::MD5.new
|
||||
sum << SPRITE_VERSION
|
||||
sum << path
|
||||
images.each do |image|
|
||||
[:relative_file, :height, :width, :repeat, :spacing, :position, :digest].each do |attr|
|
||||
sum << image[attr].to_s
|
||||
end
|
||||
end
|
||||
sum.hexdigest[0...10]
|
||||
end
|
||||
@uniqueness_hash
|
||||
end
|
||||
|
||||
# saves the sprite for later retrieval
|
||||
def save!(output_png)
|
||||
saved = output_png.save filename
|
||||
Compass.configuration.run_callback(:sprite_saved, filename)
|
||||
saved
|
||||
end
|
||||
|
||||
# All the full-path filenames involved in this sprite
|
||||
def image_filenames
|
||||
image_names.map do |image_name|
|
||||
File.join(Compass.configuration.images_path, image_name)
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_s
|
||||
end
|
||||
|
||||
def to_s(options = self.options)
|
||||
sprite_url(self).value
|
||||
end
|
||||
|
||||
def respond_to?(meth)
|
||||
super || @evaluation_context.respond_to?(meth)
|
||||
end
|
||||
|
||||
def method_missing(meth, *args, &block)
|
||||
if @evaluation_context.respond_to?(meth)
|
||||
@evaluation_context.send(meth, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
62
lib/compass/sass_extensions/sprites/image.rb
Normal file
62
lib/compass/sass_extensions/sprites/image.rb
Normal file
@ -0,0 +1,62 @@
|
||||
require 'digest/md5'
|
||||
|
||||
module Compass
|
||||
module SassExtensions
|
||||
module Sprites
|
||||
class Image
|
||||
attr_reader :relative_file, :options
|
||||
attr_accessor :top, :left
|
||||
|
||||
def initialize(relative_file, options)
|
||||
@relative_file, @options = relative_file, options
|
||||
end
|
||||
|
||||
def file
|
||||
File.join(Compass.configuration.images_path, relative_file)
|
||||
end
|
||||
|
||||
def width
|
||||
dimensions.first
|
||||
end
|
||||
|
||||
def height
|
||||
dimensions.last
|
||||
end
|
||||
|
||||
def name
|
||||
Compass::Sprites.sprite_name(relative_file)
|
||||
end
|
||||
|
||||
def repeat
|
||||
[ "#{name}-repeat", "repeat" ].each { |which|
|
||||
if var = options.get_var(which)
|
||||
return var.value
|
||||
end
|
||||
}
|
||||
"no-repeat"
|
||||
end
|
||||
|
||||
def position
|
||||
options.get_var("#{name}-position") || options.get_var("position") || Sass::Script::Number.new(0, ["px"])
|
||||
end
|
||||
|
||||
def offset
|
||||
(position.unitless? || position.unit_str == "px") ? position.value : 0
|
||||
end
|
||||
|
||||
def spacing
|
||||
(options.get_var("#{name}-spacing") || options.get_var("spacing") || Sass::Script::Number.new(0)).value
|
||||
end
|
||||
|
||||
def digest
|
||||
Digest::MD5.file(file).hexdigest
|
||||
end
|
||||
|
||||
private
|
||||
def dimensions
|
||||
@dimensions ||= Compass::SassExtensions::Functions::ImageSize::ImageProperties.new(file).size
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
136
spec/compass/sass_extensions/sprites/image_spec.rb
Normal file
136
spec/compass/sass_extensions/sprites/image_spec.rb
Normal file
@ -0,0 +1,136 @@
|
||||
require 'spec_helper'
|
||||
require 'compass/sass_extensions/sprites/image'
|
||||
|
||||
describe Compass::SassExtensions::Sprites::Image do
|
||||
let(:sprite_filename) { 'squares/ten-by-ten.png' }
|
||||
let(:sprite_path) { File.join(images_src_path, sprite_filename) }
|
||||
let(:sprite_name) { File.basename(sprite_filename, '.png') }
|
||||
let(:image) { self.class.describes.new(File.join(sprite_filename), options)}
|
||||
let(:digest) { Digest::MD5.file(sprite_path).hexdigest }
|
||||
|
||||
subject { image }
|
||||
|
||||
before {
|
||||
file = StringIO.new("images_path = #{images_src_path.inspect}\n")
|
||||
Compass.add_configuration(file, "sprite_config")
|
||||
}
|
||||
|
||||
describe '#initialize' do
|
||||
its(:name) { should == sprite_name }
|
||||
its(:file) { should == sprite_path }
|
||||
its(:relative_file) { should == sprite_filename }
|
||||
its(:width) { should == 10 }
|
||||
its(:height) { should == 10 }
|
||||
its(:digest) { should == digest }
|
||||
end
|
||||
|
||||
let(:options) {
|
||||
options = Object.new
|
||||
options.stub(:get_var) { |which| (which == get_var_expects) ? get_var_return : nil }
|
||||
options
|
||||
}
|
||||
|
||||
describe '#repeat' do
|
||||
let(:get_var_return) { OpenStruct.new(:value => type) }
|
||||
|
||||
context 'specific image' do
|
||||
let(:type) { 'specific' }
|
||||
let(:get_var_expects) { "#{sprite_name}-repeat" }
|
||||
|
||||
its(:repeat) { should == type }
|
||||
end
|
||||
|
||||
context 'global' do
|
||||
let(:type) { 'global' }
|
||||
let(:get_var_expects) { 'repeat' }
|
||||
|
||||
its(:repeat) { should == type }
|
||||
end
|
||||
|
||||
context 'default' do
|
||||
let(:get_var_expects) { nil }
|
||||
|
||||
its(:repeat) { should == "no-repeat" }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#position' do
|
||||
let(:get_var_return) { type }
|
||||
|
||||
context 'specific image' do
|
||||
let(:type) { 'specific' }
|
||||
let(:get_var_expects) { "#{sprite_name}-position" }
|
||||
|
||||
its(:position) { should == type }
|
||||
end
|
||||
|
||||
context 'global' do
|
||||
let(:type) { 'global' }
|
||||
let(:get_var_expects) { 'position' }
|
||||
|
||||
its(:position) { should == type }
|
||||
end
|
||||
|
||||
context 'default' do
|
||||
let(:get_var_expects) { nil }
|
||||
|
||||
its(:position) { should == Sass::Script::Number.new(0, ["px"]) }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#spacing' do
|
||||
let(:get_var_return) { OpenStruct.new(:value => type) }
|
||||
|
||||
context 'specific image' do
|
||||
let(:type) { 'specific' }
|
||||
let(:get_var_expects) { "#{sprite_name}-spacing" }
|
||||
|
||||
its(:spacing) { should == type }
|
||||
end
|
||||
|
||||
context 'global' do
|
||||
let(:type) { 'global' }
|
||||
let(:get_var_expects) { 'spacing' }
|
||||
|
||||
its(:spacing) { should == type }
|
||||
end
|
||||
|
||||
context 'default' do
|
||||
let(:get_var_expects) { nil }
|
||||
|
||||
its(:spacing) { should == Sass::Script::Number.new(0).value }
|
||||
end
|
||||
end
|
||||
|
||||
describe '#offset' do
|
||||
before { image.stub(:position) { stub_position } }
|
||||
|
||||
let(:offset) { 100 }
|
||||
let(:stub_position) {
|
||||
stub = double
|
||||
stub.stub(:value) { offset }
|
||||
stub
|
||||
}
|
||||
|
||||
context 'unitless' do
|
||||
before { stub_position.stub(:unitless?) { true } }
|
||||
before { stub_position.stub(:unit_str) { 'em' } }
|
||||
|
||||
its(:offset) { should == offset }
|
||||
end
|
||||
|
||||
context 'pixels' do
|
||||
before { stub_position.stub(:unitless?) { false } }
|
||||
before { stub_position.stub(:unit_str) { 'px' } }
|
||||
|
||||
its(:offset) { should == offset }
|
||||
end
|
||||
|
||||
context 'neither, use 0' do
|
||||
before { stub_position.stub(:unitless?) { false } }
|
||||
before { stub_position.stub(:unit_str) { 'em' } }
|
||||
|
||||
its(:offset) { should == 0 }
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user