Class: Sprite

Inherits:
Object
  • Object
show all
Defined in:
lib/css_sprite/sprite.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Sprite

Returns a new instance of Sprite.



8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/css_sprite/sprite.rb', line 8

def initialize(options={})
  base_dir = Dir.pwd
  if File.exist?(File.join(base_dir, 'config/css_sprite.yml'))
    @config = YAML::load_file(File.join(base_dir, 'config/css_sprite.yml'))
  else
    @config = options
  end

  image_relative_path = @config['image_path'] || 'app/assets/images'
  @image_path = File.expand_path(File.join(base_dir, image_relative_path))
  @source_images_path = File.expand_path(File.join(base_dir, @config['source_images_path'] || image_relative_path))
  @stylesheet_path = File.expand_path(File.join(base_dir, @config['stylesheet_path'] || 'app/assets/stylesheets'))

  @css_images_path = @config['css_images_path'] ||= "assets"
  @format = @config['format'] ? @config['format'].downcase : "png"
  @engine = @config['engine'] || "css.scss"
end

Instance Method Details

#all_images(directory) ⇒ Object

read all images under the css sprite directory



255
256
257
258
259
260
261
262
263
# File 'lib/css_sprite/sprite.rb', line 255

def all_images(directory)
  images = []
  Find.find(directory) do |path|
    if path =~ /\.(png|gif|jpg|jpeg)$/
      images << path
    end
  end
  images
end

#buildObject

execute the css sprite operation



27
28
29
30
# File 'lib/css_sprite/sprite.rb', line 27

def build
  directories = css_sprite_directories
  directories.each { |directory| execute(directory) }
end

#checkObject

execute the css sprite operation if stylesheet is expired



33
34
35
36
# File 'lib/css_sprite/sprite.rb', line 33

def check
  directories = css_sprite_directories
  directories.each { |directory| execute(directory) if expire?(directory) }
end

#class_name(name) ⇒ Object

get the css class name from image name



250
251
252
# File 'lib/css_sprite/sprite.rb', line 250

def class_name(name)
  ".#{name.gsub('/', ' .').gsub(/[_-]hover\b/, ':hover').gsub(/[_-]active\b/, '.active')}"
end

#class_names(results, options = {}) ⇒ Object

get all the class names within the same css sprite image



239
240
241
242
243
244
245
246
247
# File 'lib/css_sprite/sprite.rb', line 239

def class_names(results, options={})
  options = {:count_per_line => 5}.merge(options)
  class_names = []
  results = results.select { |result| result[:name] =~ %r|#{options[:suffix]}$| } if options[:suffix]
  results.each_slice(options[:count_per_line]) do |batch_results|
    class_names << batch_results.collect { |result| class_name(result[:name]) }.join(', ')
  end
  class_names
end

#css_sprite_directoriesObject

detect all the css sprite directories. e.g. app/assets/images/css_sprite, app/assets/images/widget_css_sprite



80
81
82
83
84
# File 'lib/css_sprite/sprite.rb', line 80

def css_sprite_directories
  Dir.entries(@source_images_path).collect do |d|
    File.join(@source_images_path, d) if File.directory?(File.join(@source_images_path, d)) and d =~ /css_sprite$/
  end.compact
end

#dest_image_name(directory) ⇒ Object

destination css sprite image name



271
272
273
# File 'lib/css_sprite/sprite.rb', line 271

def dest_image_name(directory)
  File.basename(directory) + "." + @format
end

#dest_image_path(directory) ⇒ Object

destination css sprite image path



266
267
268
# File 'lib/css_sprite/sprite.rb', line 266

def dest_image_path(directory)
  File.join(@image_path, File.basename(directory) + "." + @format)
end

#dest_stylesheet_path(directory) ⇒ Object

destination stylesheet file path



276
277
278
# File 'lib/css_sprite/sprite.rb', line 276

def dest_stylesheet_path(directory)
  File.join(@stylesheet_path, File.basename(directory) + "." + @engine)
end

#execute(directory) ⇒ Object

output the css sprite image and stylesheet



39
40
41
42
43
44
45
# File 'lib/css_sprite/sprite.rb', line 39

def execute(directory)
  results = output_image(directory)
  unless results.empty?
    optimize_image(directory)
    output_stylesheet(directory, results)
  end
end

#expire?(directory) ⇒ Boolean

detect if the stylesheet is expired or not?

Returns:

  • (Boolean)


48
49
50
51
52
53
54
55
56
# File 'lib/css_sprite/sprite.rb', line 48

def expire?(directory)
  stylesheet_path = dest_stylesheet_path(directory)
  return true unless File.exist?(stylesheet_path)
  stylesheet_mtime = File.new(stylesheet_path).mtime
  Dir["**/*"].each do |path|
    return true if path !~ /.*\..*/ and File.new(path).mtime > stylesheet_mtime
  end
  return false
end

#get_image(image_filename) ⇒ Object

get the Magick::Image



281
282
283
# File 'lib/css_sprite/sprite.rb', line 281

def get_image(image_filename)
  MiniMagick::Image.open(image_filename)
end

#get_image_name(image_path, directory) ⇒ Object

get the image name substracting base directory and extname



309
310
311
312
# File 'lib/css_sprite/sprite.rb', line 309

def get_image_name(image_path, directory)
  extname_length = File.extname(image_path).length
  image_path.slice(directory.length+1...-extname_length)
end

#image_properties(image_path, directory) ⇒ Object

get image properties, including name, width and height



286
287
288
289
290
# File 'lib/css_sprite/sprite.rb', line 286

def image_properties(image_path, directory)
  name = get_image_name(image_path, directory)
  image = get_image(image_path)
  need_wh?(image_path, directory) ? {:name => name, :width => image[:width], :height => image[:height]} : {:name => name}
end

#need_wh?(image_path, directory) ⇒ Boolean

check if the hover class needs width and height if the hover class has the same width and height property with not hover class, then the hover class does not need width and height

Returns:

  • (Boolean)


295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/css_sprite/sprite.rb', line 295

def need_wh?(image_path, directory)
  name = get_image_name(image_path, directory)
  if hover?(name) or active?(name)
    not_file = image_path.sub(/[_-](hover|active)\./, '.').sub(/[_-](hover|active)\//, '/')
    if File.exist?(not_file)
      image = get_image(image_path)
      not_image = get_image(not_file)
      return false if image[:width] == not_image[:width] and image[:height] == not_image[:height]
    end
  end
  return true
end

#optimize_image(directory) ⇒ Object

opitmize the css sprite image



119
120
121
122
123
124
125
126
# File 'lib/css_sprite/sprite.rb', line 119

def optimize_image(directory)
  if @config['optimization']
    dest_image_path = dest_image_path(directory)
    command  = @config['optimization'] === true ? "optipng -quiet #{dest_image_path}" : "#{@config['optimization']} #{dest_image_path}"
    result = system(command)
    puts %Q(Optimization command "#{command}" execute failed) unless result
  end
end

#output_css(directory, results) ⇒ Object

output the css sprite css



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
# File 'lib/css_sprite/sprite.rb', line 129

def output_css(directory, results)
  unless results.empty?
    dest_image_name = dest_image_name(directory)
    dest_stylesheet_path = dest_stylesheet_path(directory)
    dest_image_time = File.new(dest_image_path(directory)).mtime
    File.open(dest_stylesheet_path, 'w') do |f|
      if @config['suffix']
        @config['suffix'].each do |key, value|
          cns = class_names(results, :suffix => key)
          unless cns.empty?
            f.print cns.join(",\n")
            f.print " \{\n"
            f.print value.split("\n").collect { |text| "  " + text }.join("\n")
            f.print "\}\n"
          end
        end
      end

      f.print class_names(results).join(",\n")
      if @config['css_images_path_relative']
        f.print " \{\n  background: url('#{@css_images_path}/#{dest_image_name}?#{dest_image_time.to_i}') no-repeat;\n\}\n"
      else
        f.print " \{\n  background: url('/#{@css_images_path}/#{dest_image_name}?#{dest_image_time.to_i}') no-repeat;\n\}\n"
      end

      results.each do |result|
        f.print "#{class_name(result[:name])} \{"
        f.print " background-position: #{-result[:x]}px #{-result[:y]}px;"
        f.print " width: #{result[:width]}px;" if result[:width]
        f.print " height: #{result[:height]}px;" if result[:height]
        f.print " \}\n"
      end
    end
  end
end

#output_image(directory) ⇒ Object

output the css sprite image and return all the images properies.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/css_sprite/sprite.rb', line 87

def output_image(directory)
  results = []
  sources = all_images(directory)
  dest_image_path = dest_image_path(directory)
  return results if sources.empty?
  last_y = 0
  sources.each do |source|
    source_image = get_image(source)
    x = 0
    y = last_y
    results << image_properties(source, directory).merge(:x => x, :y => y)
    last_y = y + source_image[:height]
  end

  command = MiniMagick::Tool::Montage.new
  sources.each { |source| command << source }
  command << '-tile'
  command << '1x'
  command << '-geometry'
  command << '+0+0'
  command << '-background'
  command << 'None'
  command << '-gravity'
  command << 'West'
  command << '-format'
  command << @config['format'] || 'PNG'
  command << dest_image_path
  command.call
  results
end

#output_sass(directory, results) ⇒ Object

output the css sprite sass file



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
# File 'lib/css_sprite/sprite.rb', line 166

def output_sass(directory, results)
  unless results.empty?
    dest_image_name = dest_image_name(directory)
    dest_stylesheet_path = dest_stylesheet_path(directory)
    dest_image_time = File.new(dest_image_path(directory)).mtime
    File.open(dest_stylesheet_path, 'w') do |f|
      if @config['suffix']
        @config['suffix'].each do |key, value|
          cns = class_names(results, :suffix => key)
          unless cns.empty?
            f.print cns.join(",\n")
            f.print "\n"
            f.print value.split("\n").collect { |text| "  " + text }.join("\n")
            f.print "\n"
          end
        end
      end

      f.print class_names(results).join(",\n")
      if @config['use_asset_url']
        f.print " \n  background: asset-url('#{dest_image_name}') no-repeat\n"
      else
        f.print " \n  background: url('/#{@css_images_path}/#{dest_image_name}?#{dest_image_time.to_i}') no-repeat\n"
      end

      results.each do |result|
        f.print "#{class_name(result[:name])}\n"
        f.print "  background-position: #{-result[:x]}px #{-result[:y]}px\n"
        f.print "  width: #{result[:width]}px\n" if result[:width]
        f.print "  height: #{result[:height]}px\n" if result[:height]
      end
    end
  end
end

#output_scss(directory, results) ⇒ Object

output the css sprite scss file



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
# File 'lib/css_sprite/sprite.rb', line 202

def output_scss(directory, results)
  unless results.empty?
    dest_image_name = dest_image_name(directory)
    dest_stylesheet_path = dest_stylesheet_path(directory)
    dest_image_time = File.new(dest_image_path(directory)).mtime
    File.open(dest_stylesheet_path, 'w') do |f|
      if @config['suffix']
        @config['suffix'].each do |key, value|
          cns = class_names(results, :suffix => key)
          unless cns.empty?
            f.print cns.join(",\n")
            f.print "\{\n"
            f.print value.split("\n").collect { |text| "  " + text }.join("\n")
            f.print "\}\n"
          end
        end
      end

      f.print class_names(results).join(",\n")
      if @config['use_asset_url']
        f.print " \{\n  background: asset-url('#{dest_image_name}') no-repeat;\n\}\n"
      else
        f.print " \{\n  background: url('/#{@css_images_path}/#{dest_image_name}?#{dest_image_time.to_i}') no-repeat;\n\}\n"
      end

      results.each do |result|
        f.print "#{class_name(result[:name])} \{\n"
        f.print "  background-position: #{-result[:x]}px #{-result[:y]}px;\n"
        f.print "  width: #{result[:width]}px;\n" if result[:width]
        f.print "  height: #{result[:height]}px;\n" if result[:height]
        f.print " \}\n"
      end
    end
  end
end

#output_stylesheet(directory, results) ⇒ Object

output stylesheet, sass, scss or css



59
60
61
62
63
64
65
66
67
# File 'lib/css_sprite/sprite.rb', line 59

def output_stylesheet(directory, results)
  if sass?
    output_sass(directory, results)
  elsif scss?
    output_scss(directory, results)
  else
    output_css(directory, results)
  end
end

#sass?Boolean

use sass

Returns:

  • (Boolean)


70
71
72
# File 'lib/css_sprite/sprite.rb', line 70

def sass?
  @engine =~ /sass$/
end

#scss?Boolean

use scss

Returns:

  • (Boolean)


75
76
77
# File 'lib/css_sprite/sprite.rb', line 75

def scss?
  @engine =~ /scss$/
end