image_bundle 0.0.1

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/.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