lemonade 0.3.5 → 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,13 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- 1.0.1.deprecated.1, 0.3.5*
5
- --------------------------
4
+ TODO
5
+ ----
6
6
 
7
- * Deprecated everything
8
-
9
- \* just to be sure that the deprication will be installed
7
+ * Rebuild sprite image only if images have been modified (including integration specs)
8
+ * Horizontal sprites
9
+ * Add more validations and error messages
10
+ * Rebuild Sass files when images but not Sass sources have been updated
10
11
 
11
12
 
12
13
  1.0.0.beta.1
@@ -70,4 +71,4 @@ Changelog
70
71
  0.1.0
71
72
  -----
72
73
 
73
- * Initial release
74
+ * Initial release
data/README.md CHANGED
@@ -1,28 +1,141 @@
1
- Lemonade is deprecated
2
- ======================
1
+ Lemonade—On the fly sprite generator for Sass/Compass
2
+ =====================================================
3
3
 
4
- Lemonade was a [cool way to create sprites](http://www.hagenburger.net/BLOG/Lemonade-CSS-Sprites-for-Sass-Compass.html).
5
- Now I [merged Lemonade into Compass](http://chriseppstein.github.com/blog/2010/09/11/compass-merging-with-lemonade/) in [v0.11](https://github.com/chriseppstein/compass/tree/v0.11.0) and redefined the API with [Chris Eppstein](https://twitter.com/chriseppstein). Even better, [Scott Davis](https://twitter.com/jetviper21) made some enhancements.
4
+ Please read my [blog post on CSS sprites for Sass/Compass](http://www.hagenburger.net/BLOG/Lemonade-CSS-Sprites-for-Sass-Compass.html) or have a look at the presentation [3 steps to make better and faster frontends](http://www.hagenburger.net/BLOG/3-Steps-to-Make-Better-And-Faster-Frontends.html) (slides 23—37).
6
5
 
7
- So thanks for all the buzz for Lemonade and take a look at our Compass
8
- Sprites implementation:
9
6
 
10
- <http://compass-style.org/help/tutorials/spriting/>
7
+ **Usage ([SCSS or Sass](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html)):**
11
8
 
12
- Follow @[hagenburger](http://twitter.com/hagenburger) for updates.
9
+ .fanta {
10
+ background: sprite-image("bottles/fanta.png");
11
+ }
12
+ .seven-up {
13
+ background: sprite-image("bottles/seven-up.png");
14
+ }
15
+ .coke {
16
+ background: sprite-image("cans/coke.png") no-repeat;
17
+ }
13
18
 
14
- Cheers,
19
+ **Output (CSS):**
15
20
 
16
- Nico)
21
+ .fanta {
22
+ background: url('/images/bottles.png');
23
+ }
24
+ .seven-up {
25
+ background: url('/images/bottles.png') 0 -50px;
26
+ }
27
+ .coke {
28
+ background: url('/images/cans.png') no-repeat;
29
+ }
17
30
 
18
31
 
19
- Still need to use Lemonade?
20
- ---------------------------
32
+ Background
33
+ ----------
21
34
 
22
- Try to use an older implementation of ChunkyPNG if you get empty PNGs:
35
+ * Generates a sprite image for each folder (e. g. “bottles” and “cans”)
36
+ * Sets the background position (unless “0 0”)
37
+ * It uses the `images_dir` defined by Compass (just like `image-url()`)
38
+ * No Rake task needed
39
+ * No additional classes
40
+ * No configuration
41
+ * No RMagick required (but full support for PNG)
23
42
 
24
- # Gemfile
25
- gem 'lemonade'
26
- gem 'chunky_png', '0.11.1'
27
43
 
28
- You can find the old Lemonade code in the [master branch](https://github.com/hagenburger/lemonade/tree/master)
44
+ Installation
45
+ ------------
46
+
47
+ (1)
48
+
49
+ gem install lemonade
50
+
51
+ (2)
52
+ Now open your `config.rb` ([Compass cofiguration file](http://compass-style.org/docs/tutorials/configuration-reference/)) and add one line after this comment:
53
+
54
+ # Require any additional compass plugins here.
55
+ require "lemonade"
56
+
57
+
58
+ Current State
59
+ -------------
60
+
61
+ * Compass standalone finished
62
+ * Rails Sass integration finished
63
+ * Staticmatic integration finished
64
+ * Haml integration (with “:sass” filter): work in progress
65
+
66
+
67
+ Options
68
+ -------
69
+
70
+ You can pass an additional background position.
71
+ It will be added to the calculated position:
72
+
73
+ .seven-up {
74
+ background: sprite-image("bottles/seven-up.png", 12px, 3px);
75
+ }
76
+
77
+ Output (assuming the calculated position would be “0 -50px” as shown above):
78
+
79
+ .seven-up {
80
+ background: url('/images/bottles.png') 12px -47px;
81
+ }
82
+
83
+ If you need empty space around the current image, this will add 20px transparent space above and below.
84
+
85
+ .seven-up {
86
+ background: sprite-image("bottles/seven-up.png", 0, 0, 20px);
87
+ }
88
+
89
+ This one adds 20px above, 30px below:
90
+
91
+ .seven-up {
92
+ background: sprite-image("bottles/seven-up.png", 0, 0, 20px, 30px);
93
+ }
94
+
95
+ Right aligned images are possible:
96
+
97
+ .seven-up {
98
+ background: sprite-image("bottles/seven-up.png", 100%, 4px);
99
+ }
100
+
101
+ The original image will be placed on the right side of the sprite image.
102
+ Use this, if you have a link with an arrow on the right side (like Apple).
103
+
104
+
105
+ Note on Patches/Pull Requests
106
+ -----------------------------
107
+
108
+ * Fork the project.
109
+ * Make your feature addition or bug fix.
110
+ * Add tests for it. This is important so I don't break it in a
111
+ future version unintentionally.
112
+ * Commit, do not mess with rakefile, version, or history.
113
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
114
+ * Send me a pull request. Bonus points for topic branches.
115
+
116
+
117
+ Rails 3 Troubleshooting
118
+ -----------------------
119
+
120
+ If you want to use Lemonade with Rails 3 Please use this compass and haml versions in your Gemfile
121
+
122
+ gem 'compass', '~> 0.10.2'
123
+ gem 'haml-edge', '~> 3.1.49', :require => 'haml'
124
+
125
+
126
+ Contributors
127
+ ------------
128
+
129
+ * [Chris Hoffman](http://github.com/cehoffman)
130
+ * [Jason Weathered](http://github.com/jasoncodes) ([twitter](http://twitter.com/jasoncodes))
131
+ * [Tobias Kraze](http://github.com/kratob)
132
+ * [Sebastian Deutsch](http://github.com/sebastiandeutsch) ([twitter](http://twitter.com/sippndipp))
133
+ * [Nico Hagenburger](http://github.com/hagenburger) ([twitter](http://twitter.com/hagenburger))
134
+
135
+
136
+ Copyright
137
+ ---------
138
+
139
+ Copyright (c) 2010 [Nico Hagenburger](http://www.hagenburger.net).
140
+ See MIT-LICENSE for details.
141
+ [Follow me](http://twitter.com/hagenburger) on twitter.
@@ -0,0 +1,31 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+
5
+ begin
6
+ require 'spec/rake/spectask'
7
+ Spec::Rake::SpecTask.new(:spec) do |spec|
8
+ spec.libs << 'lib' << 'spec'
9
+ spec.spec_files = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
13
+ spec.libs << 'lib' << 'spec'
14
+ spec.pattern = 'spec/**/*_spec.rb'
15
+ spec.rcov = true
16
+ end
17
+
18
+ task :default => :spec
19
+ rescue LoadError
20
+ puts "Rspec (or a dependency) is not available. Try running bundler install"
21
+ end
22
+
23
+ require 'rake/rdoctask'
24
+ Rake::RDocTask.new do |rdoc|
25
+ require File.expand_path('../lib/lemonade/version', __FILE__)
26
+
27
+ rdoc.rdoc_dir = 'rdoc'
28
+ rdoc.title = "lemonade #{Lemonade::Version}"
29
+ rdoc.rdoc_files.include('README*')
30
+ rdoc.rdoc_files.include('lib/**/*.rb')
31
+ end
@@ -1,9 +1,137 @@
1
- require 'fileutils'
1
+ require 'chunky_png'
2
+ require 'lemonade/sprite_info.rb'
2
3
 
3
- raise <<-INFO
4
+ module Lemonade
5
+ @@sprites = {}
6
+ @@sprites_path = nil
7
+ @@images_path = nil
4
8
 
5
- Lemonade is deprecated now. You may want to use Compass Sprites from now on. More information:
6
- https://github.com/hagenburger/lemonade
7
- #{File.dirname(__FILE__)}/README.md
9
+ class << self
10
+
11
+ def sprites
12
+ @@sprites
13
+ end
14
+
15
+ def sprites_path
16
+ @@sprites_path || images_path
17
+ end
18
+
19
+ def sprites_path=(path)
20
+ @@sprites_path = path
21
+ end
22
+
23
+ def images_path
24
+ @@images_path || defined?(Compass) ? Compass.configuration.images_path : 'public/images'
25
+ end
26
+
27
+ def images_path=(path)
28
+ @@images_path = path
29
+ end
30
+
31
+ def reset
32
+ @@sprites = {}
33
+ end
34
+
35
+ def generate_sprites
36
+ sprites.each do |sprite_name, sprite|
37
+ calculate_sprite sprite
38
+ if sprite_changed?(sprite_name, sprite)
39
+ generate_sprite_image sprite
40
+ remember_sprite_info! sprite_name, sprite
41
+ end
42
+ end
43
+ end
44
+
45
+ def extend_sass!
46
+ require 'sass'
47
+ require 'sass/plugin'
48
+ require File.expand_path('../lemonade/sass_functions', __FILE__)
49
+ require File.expand_path('../lemonade/sass_extension', __FILE__)
50
+ end
51
+
52
+ def extend_compass!
53
+ base_directory = File.join(File.dirname(__FILE__), '..')
54
+ Compass::Frameworks.register('lemonade', :path => base_directory)
55
+ end
56
+
57
+ def sprite_changed?(sprite_name, sprite)
58
+ existing_sprite_info = YAML.load(File.read(sprite_info_file(sprite_name)))
59
+ existing_sprite_info[:sprite] != sprite or existing_sprite_info[:timestamps] != timestamps(sprite)
60
+ rescue
61
+ true
62
+ end
63
+
64
+ def remember_sprite_info!(sprite_name, sprite)
65
+ File.open(sprite_info_file(sprite_name), 'w') do |file|
66
+ file << {
67
+ :sprite => sprite,
68
+ :timestamps => timestamps(sprite),
69
+ }.to_yaml
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def sprite_info_file(sprite_name)
76
+ File.join(Compass.configuration.images_path, "#{sprite_name}.sprite_info.yml")
77
+ end
78
+
79
+ def timestamps(sprite)
80
+ result = {}
81
+ sprite[:images].each do |image|
82
+ file_name = image[:file]
83
+ result[file_name] = File.ctime(file_name)
84
+ end
85
+ result
86
+ end
87
+
88
+ def calculate_sprite(sprite)
89
+ width, margin_bottom, y = 0, 0, 0
90
+ sprite[:images].each do |sprite_item|
91
+ if sprite_item[:index] == 0
92
+ margin_top = 0
93
+ elsif sprite_item[:margin_top] > margin_bottom
94
+ margin_top = sprite_item[:margin_top]
95
+ else
96
+ margin_top = margin_bottom
97
+ end
98
+ y += margin_top
99
+ sprite_item[:y] = Sass::Script::Number.new(y, ['px'])
100
+ y += sprite_item[:height]
101
+ width = sprite_item[:width] if sprite_item[:width] > width
102
+ margin_bottom = sprite_item[:margin_bottom]
103
+ end
104
+ sprite[:height] = y
105
+ sprite[:width] = width
106
+ end
107
+
108
+ def generate_sprite_image(sprite)
109
+ sprite_image = ChunkyPNG::Image.new(sprite[:width], sprite[:height], ChunkyPNG::Color::TRANSPARENT)
110
+ sprite[:images].each do |sprite_item|
111
+ sprite_item_image = ChunkyPNG::Image.from_file(sprite_item[:file])
112
+ x = (sprite[:width] - sprite_item[:width]) * (sprite_item[:x].value / 100)
113
+ y = sprite_item[:y].value
114
+ sprite_image.replace sprite_item_image, x, y
115
+ end
116
+ sprite_image.save File.join(Lemonade.images_path, sprite[:file])
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+
123
+
124
+ if defined?(Compass)
125
+ Lemonade.extend_compass!
126
+ end
127
+
128
+
129
+ if defined?(ActiveSupport) and Haml::Util.has?(:public_method, ActiveSupport, :on_load)
130
+ # Rails 3.0
131
+ ActiveSupport.on_load :before_initialize do
132
+ Lemonade.extend_sass!
133
+ end
134
+ else
135
+ Lemonade.extend_sass!
136
+ end
8
137
 
9
- INFO
@@ -0,0 +1,21 @@
1
+ module Sass
2
+
3
+ module Tree
4
+
5
+ class RootNode < Node
6
+
7
+ alias_method :render_without_lemonade, :render
8
+ def render
9
+ if result = render_without_lemonade
10
+ Lemonade.generate_sprites
11
+ result = ERB.new(result).result(binding)
12
+ Lemonade.reset
13
+ return result
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,119 @@
1
+ module Sass::Script::Functions
2
+
3
+ def sprite_url(file)
4
+ dir, name, basename = extract_names(file)
5
+ sprite = sprite_for("#{dir}#{name}")
6
+ Sass::Script::SpriteInfo.new(:url, sprite)
7
+ end
8
+
9
+ def sprite_position(file, position_x = nil, position_y_shift = nil, margin_top_or_both = nil, margin_bottom = nil)
10
+ sprite, sprite_item = sprite_url_and_position(file, position_x, position_y_shift, margin_top_or_both, margin_bottom)
11
+ Sass::Script::SpriteInfo.new(:position, sprite, sprite_item, position_x, position_y_shift)
12
+ end
13
+
14
+ def sprite_image(file, position_x = nil, position_y_shift = nil, margin_top_or_both = nil, margin_bottom = nil)
15
+ sprite, sprite_item = sprite_url_and_position(file, position_x, position_y_shift, margin_top_or_both, margin_bottom)
16
+ Sass::Script::SpriteInfo.new(:both, sprite, sprite_item, position_x, position_y_shift)
17
+ end
18
+ alias_method :sprite_img, :sprite_image
19
+
20
+ def sprite_files_in_folder(folder)
21
+ assert_type folder, :String
22
+ count = sprite_file_list_from_folder(folder).length
23
+ Sass::Script::Number.new(count)
24
+ end
25
+
26
+ def sprite_file_from_folder(folder, n)
27
+ assert_type folder, :String
28
+ assert_type n, :Number
29
+ file = sprite_file_list_from_folder(folder)[n.to_i]
30
+ file = File.basename(file)
31
+ Sass::Script::String.new(File.join(folder.value, file))
32
+ end
33
+
34
+ def sprite_name(file)
35
+ dir, name, basename = extract_names(file)
36
+ Sass::Script::String.new(name)
37
+ end
38
+
39
+ def image_basename(file)
40
+ dir, name, basename = extract_names(file, :check_file => true)
41
+ Sass::Script::String.new(basename)
42
+ end
43
+
44
+ private
45
+
46
+ def sprite_file_list_from_folder(folder)
47
+ dir = File.join(Lemonade.sprites_path, folder.value)
48
+ Dir.glob(File.join(dir, '*.png')).sort
49
+ end
50
+
51
+ def sprite_url_and_position(file, position_x = nil, position_y_shift = nil, margin_top_or_both = nil, margin_bottom = nil)
52
+ dir, name, basename = extract_names(file, :check_file => true)
53
+ filestr = File.join(Lemonade.sprites_path, file.value)
54
+
55
+ sprite_file = "#{dir}#{name}.png"
56
+ sprite = sprite_for(sprite_file)
57
+ sprite_item = image_for(sprite, filestr, position_x, position_y_shift, margin_top_or_both, margin_bottom)
58
+
59
+ # Create a temporary destination file so compass doesn't complain about a missing image
60
+ FileUtils.touch File.join(Lemonade.images_path, sprite_file)
61
+
62
+ [sprite, sprite_item]
63
+ end
64
+
65
+ def extract_names(file, options = {})
66
+ assert_type file, :String
67
+ unless (file.value =~ %r(^(.+/)?([^\.]+?)(/(.+?)\.(png))?$)) == 0
68
+ raise Sass::SyntaxError, 'Please provide a file in a folder: e.g. sprites/button.png'
69
+ end
70
+ dir, name, basename = $1, $2, $4
71
+ if options[:check_file] and basename.nil?
72
+ raise Sass::SyntaxError, 'Please provide a file in a folder: e.g. sprites/button.png'
73
+ end
74
+ [dir, name, basename]
75
+ end
76
+
77
+ def sprite_for(file)
78
+ file = "#{file}.png" unless file =~ /\.png$/
79
+ Lemonade.sprites[file] ||= {
80
+ :file => "#{file}",
81
+ :height => 0,
82
+ :width => 0,
83
+ :images => [],
84
+ :margin_bottom => 0
85
+ }
86
+ end
87
+
88
+ def image_for(sprite, file, position_x, position_y_shift, margin_top_or_both, margin_bottom)
89
+ image = sprite[:images].detect{ |image| image[:file] == file }
90
+ margin_top_or_both ||= Sass::Script::Number.new(0)
91
+ margin_top = margin_top_or_both.value #calculate_margin_top(sprite, margin_top_or_both, margin_bottom)
92
+ margin_bottom = (margin_bottom || margin_top_or_both).value
93
+ if image
94
+ image[:margin_top] = margin_top if margin_top > image[:margin_top]
95
+ image[:margin_bottom] = margin_bottom if margin_bottom > image[:margin_bottom]
96
+ else
97
+ width, height = ChunkyPNG::Image.from_file(file).size
98
+ x = (position_x and position_x.numerator_units == %w(%)) ? position_x : Sass::Script::Number.new(0)
99
+ y = sprite[:height] + margin_top
100
+ y = Sass::Script::Number.new(y, y == 0 ? [] : ['px'])
101
+ image = {
102
+ :file => file,
103
+ :height => height,
104
+ :width => width,
105
+ :x => x,
106
+ :margin_top => margin_top,
107
+ :margin_bottom => margin_bottom,
108
+ :index => sprite[:images].length
109
+ }
110
+ sprite[:images] << image
111
+ end
112
+ image
113
+ rescue Errno::ENOENT
114
+ raise Sass::SyntaxError, "#{file} does not exist in sprites_dir #{Lemonade.sprites_path}"
115
+ rescue ChunkyPNG::SignatureMismatch
116
+ raise Sass::SyntaxError, "#{file} is not a recognized png file, can't use for sprite creation"
117
+ end
118
+
119
+ end
@@ -0,0 +1,63 @@
1
+ require 'sass/script/literal'
2
+
3
+ module Sass::Script
4
+
5
+ class SpriteInfo < Literal
6
+ attr_reader :sprite
7
+ attr_reader :sprite_item
8
+ attr_reader :type
9
+
10
+ def initialize(type, sprite, sprite_item = nil, position_x = nil, position_y_shift = nil)
11
+ super(nil)
12
+ @type = type
13
+ @sprite = sprite
14
+ @sprite_item = sprite_item
15
+ @position_x = position_x
16
+ @position_y_shift = position_y_shift
17
+ end
18
+
19
+ def to_s(opts = {})
20
+ case @type
21
+ when :position
22
+ position
23
+ when :url
24
+ url
25
+ when :both
26
+ pos = position
27
+ if pos == '0 0'
28
+ url
29
+ else
30
+ "#{url} #{pos}"
31
+ end
32
+ end
33
+ end
34
+
35
+ def to_sass
36
+ to_s
37
+ end
38
+
39
+ private
40
+
41
+ def position
42
+ x = @position_x || 0
43
+ if @sprite_item[:index] == 0 and (@position_y_shift.nil? or @position_y_shift.value == 0)
44
+ "#{x.inspect} 0"
45
+ else
46
+ expression = "Lemonade.sprites['#{@sprite[:file]}'][:images][#{@sprite_item[:index]}][:y].unary_minus"
47
+ expression << ".plus(Sass::Script::Number.new(#{@position_y_shift.value}, ['px']))" if @position_y_shift
48
+ "#{x.inspect} <%= #{expression} %>"
49
+ end
50
+ end
51
+
52
+ def url
53
+ if defined?(Compass)
54
+ compass = Class.new.extend(Compass::SassExtensions::Functions::Urls)
55
+ compass.image_url(Sass::Script::String.new(@sprite[:file])).to_s
56
+ else
57
+ "url('/#{@sprite[:file]}')"
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -1,3 +1,3 @@
1
1
  module Lemonade
2
- Version = "0.3.5"
2
+ Version = "1.0.0.beta.1"
3
3
  end
@@ -0,0 +1,66 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Lemonade do
4
+
5
+ before :each do
6
+ @sprite = {
7
+ :info => 'info',
8
+ :images => [
9
+ { :file => 'file1' },
10
+ { :file => 'file2' },
11
+ ]
12
+ }
13
+
14
+ @file = ""
15
+ File.stub!(:read => @file)
16
+ Compass.stub_chain(:configuration, :images_path).and_return('image_path')
17
+ File.stub!(:ctime => Time.parse('2010-01-01 12:00'))
18
+ end
19
+
20
+ ###
21
+
22
+ describe '#remember_sprite_info' do
23
+ subject { Lemonade }
24
+
25
+ it 'should save sprite info into a file' do
26
+ File.should_receive(:open).with(File.join('image_path', 'the_sprite.sprite_info.yml'), 'w').and_yield(@file)
27
+ @file.should_receive(:<<)
28
+ subject.remember_sprite_info!('the_sprite', @sprite)
29
+ end
30
+ end
31
+
32
+ ###
33
+
34
+ describe '#sprite_changed?' do
35
+ subject { Lemonade }
36
+
37
+ it 'should be false if nothing changed' do
38
+ File.should_receive(:open).and_yield(@file)
39
+ subject.remember_sprite_info!('the sprite', @sprite)
40
+ subject.sprite_changed?('the sprite', @sprite).should be_false
41
+ end
42
+
43
+ it 'should be true if the sprite info has changed' do
44
+ File.should_receive(:open).and_yield(@file)
45
+ subject.remember_sprite_info!('the sprite', @sprite)
46
+ @sprite[:info] = 'changed info'
47
+ subject.sprite_changed?('the sprite', @sprite).should be_true
48
+ end
49
+
50
+ it 'should be true if the images changed' do
51
+ File.should_receive(:open).and_yield(@file)
52
+ subject.remember_sprite_info!('the sprite', @sprite)
53
+ @sprite[:images] = []
54
+ subject.sprite_changed?('the sprite', @sprite).should be_true
55
+ end
56
+
57
+ it 'should be true if a images timestamp changed' do
58
+ File.should_receive(:open).and_yield(@file)
59
+ subject.remember_sprite_info!('the sprite', @sprite)
60
+ File.stub!(:ctime => Time.now)
61
+ subject.sprite_changed?('the sprite', @sprite).should be_true
62
+ end
63
+
64
+ end
65
+
66
+ end
@@ -0,0 +1,217 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Sass::Script::Functions do
4
+
5
+ before :each do
6
+ Lemonade.reset
7
+ FileUtils.cp_r File.dirname(__FILE__) + '/images', IMAGES_TMP_PATH
8
+ Compass.configuration.http_images_path = ''
9
+ end
10
+
11
+ after :each do
12
+ FileUtils.rm_r IMAGES_TMP_PATH
13
+ end
14
+
15
+ def image_size(file)
16
+ IO.read(IMAGES_TMP_PATH + '/' + file)[0x10..0x18].unpack('NN')
17
+ end
18
+
19
+ def evaluate(*values)
20
+ sass = 'div' + values.map{ |value| "\n background: #{value}" }.join
21
+ css = Sass::Engine.new(sass, :syntax => :sass).render
22
+ # find rendered CSS values
23
+ # strip selectors, porperty names, semicolons and whitespace
24
+ css = css.gsub(/div {\s*background: (.+?);\s*}\s*/m, '\\1').split(/;\s*background: /)
25
+ css = css.first if css.length == 1
26
+ return css
27
+ end
28
+
29
+ ###
30
+
31
+ it "should return the sprite file name" do
32
+ evaluate('sprite-image("sprites/30x30.png")').should == "url('/sprites.png')"
33
+ end
34
+
35
+ it "should also work with `sprite-img`" do
36
+ evaluate('sprite-img("sprites/30x30.png")').should == "url('/sprites.png')"
37
+ end
38
+
39
+ it "should work in folders with dashes and underscores" do
40
+ evaluate('sprite-image("other_images/more-images/sprites/test.png")').should ==
41
+ "url('/other_images/more-images/sprites.png')"
42
+ end
43
+
44
+ it "should not work without any folder" do
45
+ lambda { evaluate('sprite-image("test.png")') }.should raise_exception(Sass::SyntaxError)
46
+ end
47
+
48
+ it "should set the background position" do
49
+ evaluate('sprite-image("sprites/30x30.png") sprite-image("sprites/150x10.png")').should ==
50
+ "url('/sprites.png') url('/sprites.png') 0 -30px"
51
+ image_size('sprites.png').should == [150, 40]
52
+ end
53
+
54
+ it "should use the X position" do
55
+ evaluate('sprite-image("sprites/30x30.png", 5px, 0)').should == "url('/sprites.png') 5px 0"
56
+ image_size('sprites.png').should == [30, 30]
57
+ end
58
+
59
+ it "should include the Y position" do
60
+ evaluate('sprite-image("sprites/30x30.png", 0, 3px) sprite-image("sprites/150x10.png", 0, -6px)').should ==
61
+ "url('/sprites.png') 0 3px url('/sprites.png') 0 -36px"
62
+ end
63
+
64
+ it "should calculate 20px empty space between sprites" do
65
+ # Resulting sprite should look like (1 line = 10px height, X = placed image):
66
+
67
+ # X
68
+ #
69
+ #
70
+ # XX
71
+ # XX
72
+ #
73
+ #
74
+ # XXX
75
+ # XXX
76
+ # XXX
77
+
78
+ evaluate(
79
+ 'sprite-image("sprites/10x10.png")',
80
+ 'sprite-image("sprites/20x20.png", 0, 0, 20px)',
81
+ 'sprite-image("sprites/30x30.png", 0, 0, 20px)'
82
+ ).should == [
83
+ "url('/sprites.png')",
84
+ "url('/sprites.png') 0 -30px",
85
+ "url('/sprites.png') 0 -70px"
86
+ ]
87
+ image_size('sprites.png').should == [30, 100]
88
+ end
89
+
90
+ it "should calculate empty space between sprites and combine space like CSS margins" do
91
+ # Resulting sprite should look like (1 line = 10px height, X = placed image):
92
+
93
+ # X
94
+ #
95
+ #
96
+ #
97
+ # XX
98
+ # XX
99
+ #
100
+ # XXX
101
+ # XXX
102
+ # XXX
103
+
104
+ evaluate(
105
+ 'sprite-image("sprites/10x10.png", 0, 0, 0, 30px)',
106
+ 'sprite-image("sprites/20x20.png", 0, 0, 20px, 5px)',
107
+ 'sprite-image("sprites/30x30.png", 0, 0, 10px)'
108
+ ).should == [
109
+ "url('/sprites.png')",
110
+ "url('/sprites.png') 0 -40px",
111
+ "url('/sprites.png') 0 -70px"
112
+ ]
113
+ image_size('sprites.png').should == [30, 100]
114
+ end
115
+
116
+ it "should calculate empty space correctly when 2 output images are uses" do
117
+ evaluate(
118
+ 'sprite-image("sprites/10x10.png", 0, 0, 0, 30px)',
119
+ 'sprite-image("other_images/test.png")',
120
+ 'sprite-image("sprites/20x20.png", 0, 0, 20px, 5px)'
121
+ ).should == [
122
+ "url('/sprites.png')",
123
+ "url('/other_images.png')",
124
+ "url('/sprites.png') 0 -40px"
125
+ ]
126
+ end
127
+
128
+ it "should allow % for x positions" do
129
+ # Resulting sprite should look like (1 line = 10px height, X = placed image):
130
+
131
+ # XXXXXXXXXXXXXXX
132
+ # X
133
+
134
+ evaluate(
135
+ 'sprite-image("sprites/150x10.png")',
136
+ 'sprite-image("sprites/10x10.png", 100%)'
137
+ ).should == [
138
+ "url('/sprites.png')",
139
+ "url('/sprites.png') 100% -10px"
140
+ ]
141
+ end
142
+
143
+ it "should not compose the same image twice" do
144
+ evaluate(
145
+ 'sprite-image("sprites/10x10.png")',
146
+ 'sprite-image("sprites/20x20.png")',
147
+ 'sprite-image("sprites/20x20.png")' # reuse image from line above
148
+ ).should == [
149
+ "url('/sprites.png')",
150
+ "url('/sprites.png') 0 -10px",
151
+ "url('/sprites.png') 0 -10px"
152
+ ]
153
+ image_size('sprites.png').should == [20, 30]
154
+ end
155
+
156
+ it "should calculate the maximum spacing between images" do
157
+ evaluate(
158
+ 'sprite-image("sprites/10x10.png")',
159
+ 'sprite-image("sprites/20x20.png", 0, 0, 10px)',
160
+ 'sprite-image("sprites/20x20.png", 0, 0, 99px)' # 99px > 10px
161
+ ).should == [
162
+ "url('/sprites.png')",
163
+ "url('/sprites.png') 0 -109px", # use 99px spacing
164
+ "url('/sprites.png') 0 -109px"
165
+ ]
166
+ image_size('sprites.png').should == [20, 129]
167
+ end
168
+
169
+ it "should calculate the maximum spacing between images for margin bottom" do
170
+ evaluate(
171
+ 'sprite-image("sprites/10x10.png", 0, 0, 0, 10px)',
172
+ 'sprite-image("sprites/10x10.png", 0, 0, 0, 99px)', # 99px > 10px
173
+ 'sprite-image("sprites/20x20.png")',
174
+ ).should == [
175
+ "url('/sprites.png')",
176
+ "url('/sprites.png')",
177
+ "url('/sprites.png') 0 -109px" # use 99px spacing
178
+ ]
179
+ image_size('sprites.png').should == [20, 129]
180
+ end
181
+
182
+ it "should output the background-position" do
183
+ evaluate(
184
+ 'sprite-position("sprites/10x10.png")',
185
+ 'sprite-position("sprites/20x20.png")'
186
+ ).should == [
187
+ "0 0",
188
+ "0 -10px"
189
+ ]
190
+ end
191
+
192
+ it "should output the background-image URL" do
193
+ evaluate('sprite-url("sprites")').should == "url('/sprites.png')"
194
+ evaluate('sprite-url("sprites/10x10.png")').should == "url('/sprites.png')"
195
+ evaluate('sprite-url("sprites/20x20.png")').should == "url('/sprites.png')"
196
+ evaluate('sprite-url("other_images/test.png")').should == "url('/other_images.png')"
197
+ end
198
+
199
+ it "should count the PNG files in a folder" do
200
+ evaluate('sprite-files-in-folder("sprites")').to_i.should == 4
201
+ end
202
+
203
+ it "should output the n-th file in a folder" do
204
+ evaluate('sprite-file-from-folder("sprites", 0)').should == "sprites/10x10.png"
205
+ evaluate('sprite-file-from-folder("sprites", 1)').should == "sprites/150x10.png"
206
+ end
207
+
208
+ it "should output the filename without extention for the sprite" do
209
+ evaluate('sprite-name("sprites")').should == "sprites"
210
+ evaluate('sprite-name("sprites/10x10.png")').should == "sprites"
211
+ end
212
+
213
+ it "should output the filename without extention for the sprite item" do
214
+ evaluate('image-basename("sprites/10x10.png")').should == "10x10"
215
+ end
216
+
217
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format s
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems'
5
+ require 'compass'
6
+ require 'lemonade'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+
10
+ IMAGES_TMP_PATH = File.dirname(__FILE__) + '/images-tmp'
11
+ Compass.configuration.images_path = IMAGES_TMP_PATH
12
+
13
+ Spec::Runner.configure do |config|
14
+
15
+ end
@@ -0,0 +1,60 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Sass::Script::SpriteInfo do
4
+
5
+ before :each do
6
+ Compass.configuration.http_images_path = ''
7
+ end
8
+
9
+ def sprite_info(*args)
10
+ Sass::Script::SpriteInfo.new(*args).to_s
11
+ end
12
+
13
+ ##
14
+
15
+ it "should output the position for the first sprite" do
16
+ sprite = { :file => "sprites.png" }
17
+ sprite_item = { :y => Sass::Script::Number.new(20, ['px']), :index => 0 }
18
+ x = Sass::Script::Number.new(10, ['px'])
19
+ sprite_info(:position, sprite, sprite_item, x).should == "10px 0"
20
+ end
21
+
22
+ it "should output the position for the second+ sprite" do
23
+ sprite = { :file => "sprites.png" }
24
+ sprite_item = { :y => Sass::Script::Number.new(20, ['px']), :index => 1 }
25
+ x = Sass::Script::Number.new(10, ['px'])
26
+ sprite_info(:position, sprite, sprite_item, x).should ==
27
+ "10px <%= Lemonade.sprites['sprites.png'][:images][1][:y].unary_minus %>"
28
+ end
29
+
30
+ it "should output the position with y shift" do
31
+ sprite = { :file => "sprites.png" }
32
+ sprite_item = { :y => Sass::Script::Number.new(20, ['px']), :index => 1 }
33
+ x = Sass::Script::Number.new(10, ['px'])
34
+ y_shift = Sass::Script::Number.new(3, ['px'])
35
+ sprite_info(:position, sprite, sprite_item, x, y_shift).should ==
36
+ "10px <%= Lemonade.sprites['sprites.png'][:images][1][:y].unary_minus.plus(Sass::Script::Number.new(3, ['px'])) %>"
37
+ end
38
+
39
+ it "should output the position with percentage" do
40
+ sprite = { :file => "sprites.png" }
41
+ sprite_item = { :y => Sass::Script::Number.new(20, ['px']), :index => 2 }
42
+ x = Sass::Script::Number.new(100, ['%'])
43
+ sprite_info(:position, sprite, sprite_item, x).should ==
44
+ "100% <%= Lemonade.sprites['sprites.png'][:images][2][:y].unary_minus %>"
45
+ end
46
+
47
+ it "should output the url" do
48
+ sprite = { :file => "sprites.png" }
49
+ sprite_item = { }
50
+ sprite_info(:url, sprite, sprite_item).should == "url('/sprites.png')"
51
+ end
52
+
53
+ it "should output the url with compass path" do
54
+ sprite = { :file => "sprites.png" }
55
+ sprite_item = { }
56
+ Compass.configuration.http_images_path = '/louvre'
57
+ sprite_info(:url, sprite, sprite_item).should == "url('/louvre/sprites.png')"
58
+ end
59
+
60
+ end
@@ -0,0 +1,38 @@
1
+ @mixin image-dimensions($file) {
2
+ height: image-height($file);
3
+ width: image-width($file);
4
+ }
5
+
6
+ @mixin sprite-image($file) {
7
+ background: sprite-image($file) $repeat;
8
+ }
9
+
10
+ @mixin sized-sprite-image($file) {
11
+ background: sprite-image($file);
12
+ @include image-dimensions($file);
13
+ }
14
+
15
+ @mixin sprite-folder($folder, $image-dimensions: false) {
16
+ .#{$folder} {
17
+ @if $image-dimensions {
18
+ background: sprite-url($folder);
19
+ }
20
+ @else {
21
+ background: sprite-url($folder) no-repeat;
22
+ }
23
+ }
24
+ @for $i from 0 to sprite-files-in-folder($folder) {
25
+ $file: sprite-file-from-folder($folder, $i);
26
+ .#{$folder}-#{image-basename($file)} {
27
+ @extend .#{$folder};
28
+ background-position: sprite-position(sprite-file-from-folder($folder, $i));
29
+ @if $image-dimensions {
30
+ @include image-dimensions($file);
31
+ }
32
+ }
33
+ }
34
+ }
35
+
36
+ @mixin sized-sprite-folder($folder) {
37
+ @include sprite-folder($folder, true);
38
+ }
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lemonade
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
5
- prerelease:
4
+ prerelease: true
6
5
  segments:
6
+ - 1
7
7
  - 0
8
- - 3
9
- - 5
10
- version: 0.3.5
8
+ - 0
9
+ - beta
10
+ - 1
11
+ version: 1.0.0.beta.1
11
12
  platform: ruby
12
13
  authors:
13
14
  - Nico Hagenburger
@@ -15,10 +16,84 @@ autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-05-29 00:00:00 +02:00
19
+ date: 2010-08-29 00:00:00 +02:00
19
20
  default_executable:
20
- dependencies: []
21
-
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: haml
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 0
34
+ version: 3.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: chunky_png
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 0
47
+ - 9
48
+ - 0
49
+ version: 0.9.0
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ - !ruby/object:Gem::Dependency
53
+ name: rake
54
+ prerelease: false
55
+ requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ - 8
63
+ - 7
64
+ version: 0.8.7
65
+ type: :development
66
+ version_requirements: *id003
67
+ - !ruby/object:Gem::Dependency
68
+ name: compass
69
+ prerelease: false
70
+ requirement: &id004 !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ - 10
78
+ - 0
79
+ version: 0.10.0
80
+ type: :development
81
+ version_requirements: *id004
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec
84
+ prerelease: false
85
+ requirement: &id005 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ segments:
91
+ - 1
92
+ - 3
93
+ - 0
94
+ version: 1.3.0
95
+ type: :development
96
+ version_requirements: *id005
22
97
  description: "Generates sprites on the fly by using `background: sprite-image(\"sprites/logo.png\")`. No Photoshop, no RMagick, no Rake task, save your time and have a lemonade."
23
98
  email: gems@hagenburger.net
24
99
  executables: []
@@ -28,11 +103,27 @@ extensions: []
28
103
  extra_rdoc_files:
29
104
  - README.md
30
105
  files:
106
+ - lib/lemonade/sass_extension.rb
107
+ - lib/lemonade/sass_functions.rb
108
+ - lib/lemonade/sprite_info.rb
31
109
  - lib/lemonade/version.rb
32
110
  - lib/lemonade.rb
111
+ - stylesheets/lemonade.scss
33
112
  - CHANGELOG.md
34
113
  - MIT-LICENSE
114
+ - Rakefile
35
115
  - README.md
116
+ - spec/images/other_images/more-images/sprites/test.png
117
+ - spec/images/other_images/test.png
118
+ - spec/images/sprites/10x10.png
119
+ - spec/images/sprites/150x10.png
120
+ - spec/images/sprites/20x20.png
121
+ - spec/images/sprites/30x30.png
122
+ - spec/lemonade_spec.rb
123
+ - spec/sass_functions_spec.rb
124
+ - spec/spec.opts
125
+ - spec/spec_helper.rb
126
+ - spec/sprite_info_spec.rb
36
127
  has_rdoc: true
37
128
  homepage: http://github.com/hagenburger/lemonade
38
129
  licenses: []
@@ -47,25 +138,35 @@ required_ruby_version: !ruby/object:Gem::Requirement
47
138
  requirements:
48
139
  - - ">="
49
140
  - !ruby/object:Gem::Version
50
- hash: 3
51
141
  segments:
52
142
  - 0
53
143
  version: "0"
54
144
  required_rubygems_version: !ruby/object:Gem::Requirement
55
145
  none: false
56
146
  requirements:
57
- - - ">="
147
+ - - ~>
58
148
  - !ruby/object:Gem::Version
59
- hash: 3
60
149
  segments:
61
- - 0
62
- version: "0"
150
+ - 1
151
+ - 3
152
+ - 6
153
+ version: 1.3.6
63
154
  requirements: []
64
155
 
65
156
  rubyforge_project:
66
- rubygems_version: 1.6.1
157
+ rubygems_version: 1.3.7
67
158
  signing_key:
68
159
  specification_version: 3
69
160
  summary: On the fly sprite generator for Sass/Compass
70
- test_files: []
71
-
161
+ test_files:
162
+ - spec/images/other_images/more-images/sprites/test.png
163
+ - spec/images/other_images/test.png
164
+ - spec/images/sprites/10x10.png
165
+ - spec/images/sprites/150x10.png
166
+ - spec/images/sprites/20x20.png
167
+ - spec/images/sprites/30x30.png
168
+ - spec/lemonade_spec.rb
169
+ - spec/sass_functions_spec.rb
170
+ - spec/spec.opts
171
+ - spec/spec_helper.rb
172
+ - spec/sprite_info_spec.rb