Module: ImageBundleHelper
- Defined in:
- lib/image_bundle.rb
Overview
ImageBundleHelper adds view helper image_bundle
. A helper which bundles individual <strong>local images</strong> into a single CSS sprite thereby reducing the number of HTTP requests needed to render the page.
Yahoo’s Exceptional Performance team found that the number of HTTP requests has the biggest impact on page rendering speed. You can inspect your site’s performance with the excellent Firefox add-on YSlow.
Defined Under Namespace
Classes: Image
Instance Method Summary collapse
-
#image_bundle(css_class = nil, sprite_type = :png, content_target = :head, replacement_image = '/images/clear.gif', *args, &block) ⇒ Object
image_bundle
takes 4 optional parameters:.
Instance Method Details
#image_bundle(css_class = nil, sprite_type = :png, content_target = :head, replacement_image = '/images/clear.gif', *args, &block) ⇒ Object
image_bundle
takes 4 optional parameters:
css_class
-
When provided
css_class
restricts the bundling of images to<img>
tags of classcss_class
. sprite_type
-
By default
image_bundle
generates a PNG master image. Set sprite_type to the image type you’d like to use instead. E.g. GIF or JPEG. Any type supported by ImageMagick can be generated. All images being bundled will be converted tosprite_type
. content_target
-
By default
image_bundle
produces content for:head
usingcontent_for
. Provide a different target if you prefer a different name. Can be anything that can be converted to a symbol. replacement_image
-
By default
image_bundle
replaces thesrc
of bundled images with/images/clear.gif
. A 1x1 transparent image is included with theimage_bundle
plugin. You’ll find it in theimages
directory of the plugin. Providereplacement_image
if you prefer to use an image of different name.
image_bundle
does 4 things:
-
It creates a master image of all bundled images, if it doesn’t already exist.
-
It rewrites the
<img>
tags of all images included in the bundle to usereplacement_image
instead. -
Each included
<img>
gets a new class added to the image’sclass
attribute. The new class name is unique to the image’s size and content. -
image_bundle
creates matching CSS rules to display the portion of the master image equivalent to the<img>
tags’ original image. The CSS rules are collected incontent_target
and can be used later withyield
.
image_bundle
uses 1 environment variable:
By default image_bundle
creates sprites in a directory called sprites
. image_bundle
doesn’t use images
in order to eliminate the potential of overwriting your images. Create sprites
in your public
directory before using image_bundle
.
If you prefer to use a different directory set ENV['IMAGE_BUNDLE_SPRITE_BASE_DIR']
in your Rails environment. IMAGE_BUNDLE_SPRITE_BASE_DIR is relative to your
public= directory.
Example usages
Instruct your controller to user image_bundle
:
helper: image_bundle
Bundle all images included within image_bundle
‘s block.
<% image_bundle do %>
<p>+image_bundle+ can wrap any kind of content: HTML, JS, etc.</p>
<img src="/images/auflag.gif"/></br>
<p>Bundled images don't need to be adjacent to one another either.</p>
<img src="/images/nlflag.gif"/></br>
<img src="/images/frflag.gif"/></br>
<% end %>
Bundle only images of class :bundle
. image_bundle
scales resized images accordingly. It calculates the 2nd dimension if only one dimension is given. Bundled images don’t have to be of the same size either.
<% image_bundle(:bundle) do %>
<p>
Some static text with <strong>HTML</strong> <em>markup</em>.<br/>
Plus a dynamic date: <%= Time.now %><br/>
And an 16x16 <img alt="favicon" class="bundle" src="/favicon.ico" height="16" width="16"/><br/>
A 6x? <img alt="favicon" class="bundle" src="/favicon.ico" height="6"/><br/>
A ?x6 <img alt="favicon" class="bundle" src="/favicon.ico" width="6"/><br/>
An ?x? <img alt="favicon" class="bundle" src="/images/rails.png"/><br/>
A 6x16 <img alt="favicon" src="/favicon.ico" height="6" width="16"/> not of class bundle<br/>
My 160x16 multi line example <img alt="favicon" class="bundle" src="/favicon.ico"
height="160"
width="16"/><br/>
Single quote ?x? <img alt="favicon" class='bundle' src="/favicon.ico"/><br/>
Class 'some bundle' ?x? <img alt="favicon" class='some bundle' src="/favicon.ico"/><br/>
Src before class ?x160 <img alt="favicon" src="/favicon.ico" class='bundle' width="160"/><br/>
Src before class 'some bundle' ?x? <img alt="favicon" src="/favicon.ico" class='some bundle'/><br/>
Src = and id ?x? <img alt="favicon" src="/favicon.ico" id="bla" class = 'some bundle'/><br/>
Casper ?x? <img alt="favicon" class="bundle" src="/images/casper-1st-birthday.jpg"/><br/>
</p>
<p>
Some additional text.
</p>
<% end %>
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 |
# File 'lib/image_bundle.rb', line 121 def image_bundle(css_class = nil, sprite_type = :png, content_target = :head, replacement_image = '/images/clear.gif', *args, &block) # Bind buffer to the output buffer of the templates. buffer = @output_buffer # Mark the current position in the buffer pos = buffer.length # Render the block contained within the image_bundle tag. The # rendered output is appended to buffer. block.call(*args) # Extract the output produced by the block. block_output = buffer[pos..-1] buffer[pos..-1] = '' # Replace the img tags in the output with links to clear.gif and # styling to use the master image created from the individual # images. images = Hash.new re = (css_class == nil) ? /(<img\s*)([^>]*?)(\s*\/?>)/im : /(<img\s*)([^>]*?class\s*=\s*["']?[^"']*?#{css_class}[^"']*?["']?[^>]*?)(\s*\/?>)/im block_rewrite = '' while pos = (block_output =~ re) do # Store match data for later reference. img_match = $~.to_s img_tag = $1 attributes = $2 img_closing_tag = $3 # Remember where to continue searching from in the next # iteration. continue_pos = pos+img_match.length # Write out the content before the start of the tag block_rewrite << block_output[0..pos-1] if img_match =~ /src\=["']?https?:\/\//i then block_rewrite << img_match else # Write out the opening portion of the image tag (<img). block_rewrite << img_tag # Process all attributes of the img tag. height_given = width_given = nil classes = '' ping = ::ImageBundleHelper::Image.new while pos = (attributes =~ /([^ =]+?)\s*=\s*(("?([^"=]*?)")|('?([^'=]*?)'))/im) do attribute = $1 value = $4 || $6 attr_continue_pos = pos+$~.to_s.length case attribute when 'src' ping.path = value # Read only the image's meta data not its image content. ping.file = "#{Rails.root}/public#{ping.path}" image = ::Magick::Image.ping(ping.file)[0] ping.height = image.rows ping.width = image.columns ping.size = image.filesize # Return all the memory associated with the image to the # system. image.destroy! block_rewrite << "#{attribute}=\"#{replacement_image}\" " when 'height' height_given = value.to_i when 'width' width_given = value.to_i when 'class' # Prepend a space for later concatenation with bndl class. classes = " #{value}" else # Pass through all other attributes block_rewrite << "#{attribute}=\"#{value}\" " end attributes = attributes[attr_continue_pos..-1] end # Calculate the height and width of the image based on the # specified height/width and the source file's height and # width. Scaling needs to happen when the sprite is created if height_given == nil then if width_given != nil then ping.height = (ping.height * (width_given.to_f / ping.width.to_f)).to_i ping.width = width_given end else if width_given == nil then ping.width = (ping.width * (height_given.to_f / ping.height.to_f)).to_i ping.height = height_given else ping.width = width_given ping.height = height_given end end # Only add unique images and height/width combinations to the hash. key = "bndl#{::Digest::MD5.hexdigest("#{ping.path}:#{ping.size}#{ping.height}:#{ping.width}").hash}" images[key] ||= ping block_rewrite << "class =\"#{key}#{classes}\" " block_rewrite << "height=\"#{ping.height}\" " block_rewrite << "width=\"#{ping.width}\" " block_rewrite << img_closing_tag end block_output = block_output[continue_pos..-1] end # Create a sprite when there are source files and if it doesn't # already exists. if images.length > 0 then sprite_path = '/' + SPRITE_BASE_DIR + '/' + ::Digest::MD5.hexdigest(images.keys.inject do |concat_names, key| concat_names + '|' + key end) + ".#{sprite_type}" sprite_file = "#{Rails.root}/public/#{sprite_path}" if !File.exists?(sprite_file) then # Stack scaled source images left to right. sprite = images.values.inject(::Magick::ImageList.new) do |image_list, ping| image_list << ::Magick::ImageList.new(ping.file)[0].scale(ping.width, ping.height) end.append(false) sprite.write(sprite_file) # Return all the memory associated with the image to the # system. sprite.destroy! end # Construct style tag to be included in the header. current_y = 0 bundle_styles = "\n<style type=\"text/css\">\n" bundle_styles << images.keys.inject('') do |styles, key| images[key].x_pos = current_y current_y += images[key].width styles + ".#{key} {\n background-image:url(#{sprite_path});\n background-position: -#{images[key].x_pos}px 0px;\n}\n" end bundle_styles << "</style>\n" end # Write the remaining block output that follows the last img tag. block_rewrite << block_output buffer << raw(block_rewrite) if block_rewrite content_for content_target.to_sym, raw(bundle_styles ||= '') end |