image_bundle 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.bzrignore ADDED
@@ -0,0 +1,2 @@
1
+ rdoc
2
+ .svn
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *~
2
+ pkg
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in image_bundle.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ ImageBundle, a Rails plugin to automatically bundle local images into CSS sprites.
2
+
3
+ Copyright (C) 2007 Bart Teeuwisse <bart [dot] teeuwisse [at] thecodemill.biz>
4
+
5
+ This library is free software; you can redistribute it and/or modify
6
+ it under the terms of the GNU Lesser General Public License as
7
+ published by the Free Software Foundation; either version 2.1 of the
8
+ License, or (at your option) any later version.
9
+
10
+ This library is distributed in the hope that it will be useful, but
11
+ WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ Lesser General Public License for more details.
14
+
15
+ You should have received a copy of the GNU Lesser General Public
16
+ License along with this library; if not, write to the Free Software
17
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
+ USA
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ =ImageBundle
2
+
3
+ ImageBundleHelper adds view helper +image_bundle+. A helper which
4
+ bundles individual local images into a single CSS sprite thereby
5
+ reducing the number of HTTP requests needed to render the page.
6
+
7
+ {Yahoo's Exceptional Performance
8
+ team}[http://developer.yahoo.com/performance/] found that the number
9
+ of HTTP requests has the biggest impact on page rendering speed. You
10
+ can inspect your site's performance with the excellent Firefox add-on
11
+ {YSlow}[http://developer.yahoo.com/yslow/].
12
+
13
+ == Usage
14
+
15
+ Use ImageBundle to automatically replace individual local images into
16
+ CSS sprites.
17
+
18
+ == Demo
19
+
20
+ A demo of ImageBundle is available at http://thecodemill.biz/image_bundle/.
21
+
22
+ == Note on <tt>image_tag</tt> helper
23
+
24
+ Rails' <tt>image_tag</tt> helper adds a query parameter to the image URL to
25
+ 'bust caches'. It ads either the <tt>RAILS_ASSET_ID</tt> environment variable
26
+ or the image's modification time. This is the *wrong* thing to
27
+ do. Caches should be managed through the use of HTTP headers such as
28
+ Cache-Control, Last-Modified, Expires and of course the HTTP response
29
+ code. Adding the modification time is the same as using as using
30
+ ETags in apache or IIS, see
31
+ http://developer.yahoo.com/performance/rules.html#etags for an
32
+ explanation why it is better to avoid using ETags in favor of proper
33
+ Expires headers.
34
+
35
+ This plugin doesn't accept image URLS that include query parameters as
36
+ they are either dynamically generated and shouldn't be part of a
37
+ sprite or they promote a bad practice.
38
+
39
+ If you like to use the <tt>image_tag</tt> helper I recommend you a) configure
40
+ the webserver of you static content to return proper headers and b) to
41
+ overwrite <tt>ActionView::Helpers::AssetTagsHelper#rewrite_asset_path!</tt>
42
+ like so:
43
+
44
+ module ActionView
45
+ module Helpers
46
+ module AssetTagHelper
47
+ def rewrite_asset_path!(source)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ Create a file <tt>lib/asset_tag_helper.rb</tt> and load it at the end of your
54
+ <tt>config/environment.rb</tt> file like so:
55
+
56
+ require File.join(File.dirname(__FILE__), '../lib/asset_tag_helper')
57
+
58
+ == License & Author
59
+
60
+ :include: LICENSE
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "image_bundle/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "image_bundle"
7
+ s.version = ImageBundle::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Bart Teeuwisse"]
10
+ s.email = ["bart@thecodemill.biz"]
11
+ s.homepage = "https://github.com/bartt/image_bundle"
12
+ s.summary = %q{ImageBundle bundles individual images into a single sprite and CSS rules to match}
13
+ s.description = %q{ImageBundle adds a helper to Ruby on Rails to create image sprites and matching CSS rules on the fly. Overhead is minimal as sprites are cached. ImageBundle is rendering framework agnostic.}
14
+
15
+ s.rubyforge_project = "image_bundle"
16
+ s.add_dependency "actionpack", "~> 3.0.7"
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
data/images/clear.gif ADDED
Binary file
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,3 @@
1
+ module ImageBundle
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,266 @@
1
+ # ImageBundleHelper adds view helper +image_bundle+. A helper which
2
+ # bundles individual <strong>local images</strong> into a single CSS
3
+ # sprite thereby reducing the number of HTTP requests needed to render
4
+ # the page.
5
+ #
6
+ # {Yahoo's Exceptional Performance
7
+ # team}[http://developer.yahoo.com/performance/] found that the number
8
+ # of HTTP requests has the biggest impact on page rendering speed. You
9
+ # can inspect your site's performance with the excellent Firefox
10
+ # add-on {YSlow}[http://developer.yahoo.com/yslow/].
11
+
12
+ module ImageBundleHelper
13
+ require 'rubygems'
14
+ require 'RMagick'
15
+ require 'digest/md5'
16
+
17
+ SPRITE_BASE_DIR ||= ENV['IMAGE_BUNDLE_SPRITE_BASE_DIR'] || 'sprites'
18
+
19
+ class Image #:nodoc:
20
+ attr_accessor :path, :file, :size, :height, :width, :x_pos
21
+ end
22
+
23
+ # === +image_bundle+ takes 4 optional parameters:
24
+ #
25
+ # <tt>css_class</tt>::
26
+ # When provided <tt>css_class</tt> restricts the bundling of
27
+ # images to <tt><img></tt> tags of class <tt>css_class</tt>.
28
+ #
29
+ # <tt>sprite_type</tt>::
30
+ # By default +image_bundle+ generates a PNG master image. Set
31
+ # sprite_type to the image type you'd like to use instead. E.g. GIF
32
+ # or JPEG. Any type supported by
33
+ # {ImageMagick}[http://www.imagemagick.org/] can be generated. All
34
+ # images being bundled will be converted to <tt>sprite_type</tt>.
35
+ #
36
+ # <tt>content_target</tt>::
37
+ # By default +image_bundle+ produces content for <tt>:head</tt>
38
+ # using <tt>content_for</tt>. Provide a different target if you
39
+ # prefer a different name. Can be anything that can be converted
40
+ # to a symbol.
41
+ #
42
+ # <tt>replacement_image</tt>::
43
+ # By default +image_bundle+ replaces the +src+ of bundled images
44
+ # with <tt>/images/clear.gif</tt>. A 1x1 transparent image is
45
+ # included with the +image_bundle+ plugin. You'll find it in the
46
+ # +images+ directory of the plugin. Provide
47
+ # <tt>replacement_image</tt> if you prefer to use an image of
48
+ # different name.
49
+ #
50
+ # === +image_bundle+ does 4 things:
51
+ # 1. It creates a master image of all bundled images, if it doesn't already exist.
52
+ # 1. It rewrites the <tt><img></tt> tags of all images included
53
+ # in the bundle to use <tt>replacement_image</tt> instead.
54
+ # 1. Each included <tt><img></tt> gets a new class <em>added</em> to the
55
+ # image's +class+ attribute. The new class name is unique to the
56
+ # image's size and content.
57
+ # 1. +image_bundle+ creates matching CSS rules to display the portion
58
+ # of the master image equivalent to the <tt><img></tt> tags'
59
+ # original image. The CSS rules are collected in
60
+ # <tt>content_target</tt> and can be used later with
61
+ # <tt>yield</tt>.
62
+ #
63
+ # === +image_bundle+ uses 1 environment variable:
64
+ #
65
+ # By default +image_bundle+ creates sprites in a directory called
66
+ # +sprites+. +image_bundle+ doesn't use +images+ in order to
67
+ # eliminate the potential of overwriting your images. Create
68
+ # +sprites+ in your +public+ directory before using
69
+ # +image_bundle+.
70
+ #
71
+ # If you prefer to use a different directory set
72
+ # <tt>ENV['IMAGE_BUNDLE_SPRITE_BASE_DIR']</tt> in your Rails
73
+ # environment. IMAGE_BUNDLE_SPRITE_BASE_DIR is relative to your
74
+ # =public= directory.
75
+ #
76
+ # === Example usages
77
+ #
78
+ # Instruct your controller to user +image_bundle+:
79
+ #
80
+ # helper: image_bundle
81
+ #
82
+ # Bundle all images included within +image_bundle+'s block.
83
+ #
84
+ # <% image_bundle do %>
85
+ # <p>+image_bundle+ can wrap any kind of content: HTML, JS, etc.</p>
86
+ # <img src="/images/auflag.gif"/></br>
87
+ # <p>Bundled images don't need to be adjacent to one another either.</p>
88
+ # <img src="/images/nlflag.gif"/></br>
89
+ # <img src="/images/frflag.gif"/></br>
90
+ # <% end %>
91
+ #
92
+ # Bundle only images of class <tt>:bundle</tt>. +image_bundle+ scales resized
93
+ # images accordingly. It calculates the 2nd dimension if only one
94
+ # dimension is given. Bundled images don't have to be of the same size
95
+ # either.
96
+ #
97
+ # <% image_bundle(:bundle) do %>
98
+ # <p>
99
+ # Some static text with <strong>HTML</strong> <em>markup</em>.<br/>
100
+ # Plus a dynamic date: <%= Time.now %><br/>
101
+ # And an 16x16 <img alt="favicon" class="bundle" src="/favicon.ico" height="16" width="16"/><br/>
102
+ # A 6x? <img alt="favicon" class="bundle" src="/favicon.ico" height="6"/><br/>
103
+ # A ?x6 <img alt="favicon" class="bundle" src="/favicon.ico" width="6"/><br/>
104
+ # An ?x? <img alt="favicon" class="bundle" src="/images/rails.png"/><br/>
105
+ # A 6x16 <img alt="favicon" src="/favicon.ico" height="6" width="16"/> not of class bundle<br/>
106
+ # My 160x16 multi line example <img alt="favicon" class="bundle" src="/favicon.ico"
107
+ # height="160"
108
+ # width="16"/><br/>
109
+ # Single quote ?x? <img alt="favicon" class='bundle' src="/favicon.ico"/><br/>
110
+ # Class 'some bundle' ?x? <img alt="favicon" class='some bundle' src="/favicon.ico"/><br/>
111
+ # Src before class ?x160 <img alt="favicon" src="/favicon.ico" class='bundle' width="160"/><br/>
112
+ # Src before class 'some bundle' ?x? <img alt="favicon" src="/favicon.ico" class='some bundle'/><br/>
113
+ # Src = and id ?x? <img alt="favicon" src="/favicon.ico" id="bla" class = 'some bundle'/><br/>
114
+ # Casper ?x? <img alt="favicon" class="bundle" src="/images/casper-1st-birthday.jpg"/><br/>
115
+ # </p>
116
+ # <p>
117
+ # Some additional text.
118
+ # </p>
119
+ # <% end %>
120
+
121
+ def image_bundle(css_class = nil, sprite_type = :png, content_target = :head, replacement_image = '/images/clear.gif', *args, &block)
122
+ # Bind buffer to the output buffer of the templates.
123
+ buffer = @output_buffer
124
+
125
+ # Mark the current position in the buffer
126
+ pos = buffer.length
127
+
128
+ # Render the block contained within the image_bundle tag. The
129
+ # rendered output is appended to buffer.
130
+ block.call(*args)
131
+
132
+ # Extract the output produced by the block.
133
+ block_output = buffer[pos..-1]
134
+ buffer[pos..-1] = ''
135
+
136
+ # Replace the img tags in the output with links to clear.gif and
137
+ # styling to use the master image created from the individual
138
+ # images.
139
+ images = Hash.new
140
+ re = (css_class == nil) ? /(<img\s*)([^>]*?)(\s*\/?>)/im : /(<img\s*)([^>]*?class\s*=\s*["']?[^"']*?#{css_class}[^"']*?["']?[^>]*?)(\s*\/?>)/im
141
+ block_rewrite = ''
142
+ while pos = (block_output =~ re) do
143
+
144
+ # Store match data for later reference.
145
+ img_match = $~.to_s
146
+ img_tag = $1
147
+ attributes = $2
148
+ img_closing_tag = $3
149
+
150
+ # Remember where to continue searching from in the next
151
+ # iteration.
152
+ continue_pos = pos+img_match.length
153
+
154
+ # Write out the content before the start of the tag
155
+ block_rewrite << block_output[0..pos-1]
156
+ if img_match =~ /src\=["']?https?:\/\//i then
157
+ block_rewrite << img_match
158
+ else
159
+
160
+ # Write out the opening portion of the image tag (<img).
161
+ block_rewrite << img_tag
162
+
163
+ # Process all attributes of the img tag.
164
+ height_given = width_given = nil
165
+ classes = ''
166
+ ping = ::ImageBundleHelper::Image.new
167
+ while pos = (attributes =~ /([^ =]+?)\s*=\s*(("?([^"=]*?)")|('?([^'=]*?)'))/im) do
168
+ attribute = $1
169
+ value = $4 || $6
170
+ attr_continue_pos = pos+$~.to_s.length
171
+ case attribute
172
+ when 'src'
173
+ ping.path = value
174
+ # Read only the image's meta data not its image content.
175
+ ping.file = "#{Rails.root}/public#{ping.path}"
176
+ image = ::Magick::Image.ping(ping.file)[0]
177
+ ping.height = image.rows
178
+ ping.width = image.columns
179
+ ping.size = image.filesize
180
+ # Return all the memory associated with the image to the
181
+ # system.
182
+ image.destroy!
183
+ block_rewrite << "#{attribute}=\"#{replacement_image}\" "
184
+ when 'height'
185
+ height_given = value.to_i
186
+ when 'width'
187
+ width_given = value.to_i
188
+ when 'class'
189
+
190
+ # Prepend a space for later concatenation with bndl class.
191
+ classes = " #{value}"
192
+ else
193
+
194
+ # Pass through all other attributes
195
+ block_rewrite << "#{attribute}=\"#{value}\" "
196
+ end
197
+ attributes = attributes[attr_continue_pos..-1]
198
+ end
199
+
200
+ # Calculate the height and width of the image based on the
201
+ # specified height/width and the source file's height and
202
+ # width. Scaling needs to happen when the sprite is created
203
+ if height_given == nil then
204
+ if width_given != nil then
205
+ ping.height = (ping.height * (width_given.to_f / ping.width.to_f)).to_i
206
+ ping.width = width_given
207
+ end
208
+ else
209
+ if width_given == nil then
210
+ ping.width = (ping.width * (height_given.to_f / ping.height.to_f)).to_i
211
+ ping.height = height_given
212
+ else
213
+ ping.width = width_given
214
+ ping.height = height_given
215
+ end
216
+ end
217
+
218
+ # Only add unique images and height/width combinations to the hash.
219
+ key = "bndl#{::Digest::MD5.hexdigest("#{ping.path}:#{ping.size}#{ping.height}:#{ping.width}").hash}"
220
+ images[key] ||= ping
221
+ block_rewrite << "class =\"#{key}#{classes}\" "
222
+ block_rewrite << "height=\"#{ping.height}\" "
223
+ block_rewrite << "width=\"#{ping.width}\" "
224
+ block_rewrite << img_closing_tag
225
+ end
226
+ block_output = block_output[continue_pos..-1]
227
+ end
228
+
229
+ # Create a sprite when there are source files and if it doesn't
230
+ # already exists.
231
+ if images.length > 0 then
232
+ sprite_path = '/' + SPRITE_BASE_DIR + '/' + ::Digest::MD5.hexdigest(images.keys.inject do |concat_names, key| concat_names + '|' + key end) + ".#{sprite_type}"
233
+ sprite_file = "#{Rails.root}/public/#{sprite_path}"
234
+ if !File.exists?(sprite_file) then
235
+
236
+ # Stack scaled source images left to right.
237
+ sprite = images.values.inject(::Magick::ImageList.new) do |image_list, ping|
238
+ image_list << ::Magick::ImageList.new(ping.file)[0].scale(ping.width, ping.height)
239
+ end.append(false)
240
+ sprite.write(sprite_file)
241
+ # Return all the memory associated with the image to the
242
+ # system.
243
+ sprite.destroy!
244
+ end
245
+
246
+ # Construct style tag to be included in the header.
247
+ current_y = 0
248
+ bundle_styles = "\n<style type=\"text/css\">\n"
249
+ bundle_styles << images.keys.inject('') do |styles, key|
250
+ images[key].x_pos = current_y
251
+ current_y += images[key].width
252
+ styles + ".#{key} {\n background-image:url(#{sprite_path});\n background-position: -#{images[key].x_pos}px 0px;\n}\n"
253
+ end
254
+ bundle_styles << "</style>\n"
255
+ end
256
+
257
+ # Write the remaining block output that follows the last img tag.
258
+ block_rewrite << block_output
259
+ buffer << raw(block_rewrite) if block_rewrite
260
+ content_for content_target.to_sym, raw(bundle_styles ||= '')
261
+ end
262
+ end
263
+
264
+ if defined?(ActionView::Base)
265
+ ActionView::Base.send(:include, ImageBundleHelper)
266
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :image_bundle do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,8 @@
1
+ require 'test/unit'
2
+
3
+ class ImageBundleTest < Test::Unit::TestCase
4
+ # Replace this with your real tests.
5
+ def test_this_plugin
6
+ flunk
7
+ end
8
+ end
data/uninstall.rb ADDED
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: image_bundle
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Bart Teeuwisse
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-06-05 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: actionpack
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 3.0.7
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: ImageBundle adds a helper to Ruby on Rails to create image sprites and matching CSS rules on the fly. Overhead is minimal as sprites are cached. ImageBundle is rendering framework agnostic.
28
+ email:
29
+ - bart@thecodemill.biz
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .bzrignore
38
+ - .gitignore
39
+ - Gemfile
40
+ - LICENSE
41
+ - README.md
42
+ - Rakefile
43
+ - image_bundle.gemspec
44
+ - images/clear.gif
45
+ - install.rb
46
+ - lib/image_bundle.rb
47
+ - lib/image_bundle/version.rb
48
+ - lib/tasks/image_bundle_tasks.rake
49
+ - test/image_bundle_test.rb
50
+ - uninstall.rb
51
+ has_rdoc: true
52
+ homepage: https://github.com/bartt/image_bundle
53
+ licenses: []
54
+
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project: image_bundle
75
+ rubygems_version: 1.6.2
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: ImageBundle bundles individual images into a single sprite and CSS rules to match
79
+ test_files:
80
+ - test/image_bundle_test.rb