middleman-automatic-clowncar 0.0.1 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1 @@
1
- <%= automatic_clowncar_tag "photos/test-image.jpg", :host => "http://localhost:4567/" %>
1
+ <%= automatic_clowncar_tag "photos/test-image.jpg", :host => "http://localhost:4567/", :include_original => true, :prevent_upscaling => true %>
@@ -0,0 +1 @@
1
+ <%= thumbnail_url "photos/test-image.jpg", :medium %>
@@ -1,19 +1,23 @@
1
1
  require 'middleman-core'
2
2
 
3
3
  require 'middleman-automatic-clowncar/thumbnail-generator'
4
+ require 'middleman-automatic-clowncar/timestamp-resource'
5
+ require 'middleman-automatic-clowncar/thumbnail-resource'
6
+ require 'middleman-automatic-clowncar/sitemap-extension'
7
+ require 'middleman-automatic-clowncar/utils'
4
8
 
5
9
  require 'fastimage'
6
10
 
7
11
  module Middleman
8
12
  module AutomaticClowncar
9
13
  class Extension < Middleman::Extension
10
-
14
+
11
15
  SVG_TEMPLATE = "<svg viewBox='0 0 ::width:: ::height::' preserveAspectRatio='xMidYMid meet' xmlns='http://www.w3.org/2000/svg'><style>svg{background-size:100% 100%;background-repeat:no-repeat;}::media_queries::</style></svg>"
12
-
16
+
13
17
  option :sizes, {}, "The sizes of thumbnails to generate"
14
- option :namespace_directory, ["**"], "The directories inside of images that should be clowncared."
18
+ option :namespace_directory, ["**"], "The directories that should be clowncared. (Outside of the sprockets images dir.)"
15
19
  option :filetypes, [:jpg, :jpeg, :png], "The types of files to use for automatic clowncaring."
16
-
20
+ option :include_originals, false, "Always include original images. (Or not.)"
17
21
  cattr_accessor :options_hash
18
22
 
19
23
  def initialize(app, options_hash={}, &block)
@@ -29,31 +33,21 @@ module Middleman
29
33
  app.after_configuration do
30
34
 
31
35
  #stash the source images dir in options for the Rack middleware
32
- Extension.options_hash[:images_source_dir] = File.join(source_dir, images_dir)
33
- Extension.options_hash[:source_dir] = source_dir
36
+ Extension.options_hash[:source_dir] = app.source_dir
34
37
 
35
38
  sizes = Extension.options_hash[:sizes]
36
39
  namespace = Extension.options_hash[:namespace_directory].join(',')
37
40
 
38
- dir = Pathname.new(File.join(source_dir, images_dir))
39
- glob = "#{dir}/#{namespace}/*.{#{Extension.options_hash[:filetypes].join(',')}}"
41
+ dir = Pathname.new(app.source_dir)
42
+ glob = "#{dir}/{#{namespace}}/*.{#{Extension.options_hash[:filetypes].join(',')}}"
40
43
  files = Dir[glob]
41
-
42
- # don't build the files until after build
43
- after_build do |builder|
44
- files.each do |file|
45
- path = file.gsub(source_dir, '')
46
- specs = ThumbnailGenerator.specs(path, sizes)
47
- ThumbnailGenerator.generate(source_dir, File.join(root, build_dir), path, specs)
48
- end
49
- end
50
-
51
- sitemap.register_resource_list_manipulator(:thumbnailer, SitemapExtension.new(self), true)
52
-
53
- app.use Rack, Extension.options_hash
54
44
  end
55
45
  end
56
-
46
+
47
+ def manipulate_resource_list(resources)
48
+ SitemapExtension.new(self).manipulate_resource_list(resources)
49
+ end
50
+
57
51
  def after_configuration
58
52
  @ready = true
59
53
  end
@@ -70,7 +64,7 @@ module Middleman
70
64
  end
71
65
 
72
66
  def get_image_path(name, path, is_relative, fallback_host)
73
- puts "@@@@@@@ calling get_image_path for #{path}"
67
+ #puts "@@@@@@@ calling get_image_path for name:#{name} path:#{path}, is_relative:#{is_relative}, fallback_host:#{fallback_host}"
74
68
  begin
75
69
  uri = URI(path)
76
70
  rescue URI::InvalidURIError
@@ -81,13 +75,14 @@ module Middleman
81
75
  if uri.host
82
76
  path
83
77
  else
84
-
78
+
85
79
  svg_path = File.join(File.dirname(name),File.basename(name,".*"), path)
86
80
 
87
81
  if is_relative
88
82
  url = app.asset_path(:images, svg_path)
89
-
90
- if fallback_host &&is_relative_url?(url)
83
+ images_dir = app.config[:images_dir] || 'images'
84
+ url = url.sub("/#{images_dir}/",'/')
85
+ if fallback_host && is_relative_url?(url)
91
86
  File.join(fallback_host, url)
92
87
  else
93
88
  url
@@ -128,11 +123,15 @@ module Middleman
128
123
  output.join("")
129
124
  end
130
125
 
126
+ def get_physical_image_size(name)
127
+ main_abs_path = File.join(app.source_dir,name)
128
+ FastImage.size(main_abs_path, :raise_on_failure => true)
129
+ end
130
+
131
131
  def get_image_sizes(name, options)
132
- puts "getting images sizes for #{name}"
132
+ #puts "getting images sizes for #{name}"
133
133
 
134
- main_path = File.join(app.images_dir,name)
135
- main_abs_path = File.join(app.source_dir,main_path)
134
+ main_abs_path = File.join(app.source_dir,name)
136
135
 
137
136
  extname = File.extname(name)
138
137
  basename = File.basename(name, ".*")
@@ -141,29 +140,36 @@ module Middleman
141
140
 
142
141
  width, height = ::FastImage.size(main_abs_path, :raise_on_failure => true)
143
142
 
144
-
143
+
145
144
  sizes = {}
146
- Extension.options_hash[:sizes].each_pair do |sname,width|
147
- sizes[width] = "#{basename}-#{sname}#{extname}"
145
+ Extension.options_hash[:sizes].each_pair do |sname,swidth|
146
+ next if swidth > width
147
+ sizes[swidth] = "#{basename}-#{sname}#{extname}"
148
+ end
149
+
150
+ if options[:include_original] || Extension.options_hash[:include_originals]
151
+ sizes[width] = "../#{basename}#{extname}"
148
152
  end
149
153
 
150
- puts "-"*30
151
- puts [sizes, width, height]
154
+ #puts "-"*30
155
+ #puts [sizes, width, height]
152
156
  [sizes, width, height]
153
157
  end
154
158
 
155
159
 
156
160
  def generate_svg(name, is_relative, options)
157
- puts "name for generate_svg = #{name}"
158
- puts "options for generate_svg = #{options}"
161
+ #puts "name for generate_svg = #{name}"
162
+ #puts "options for generate_svg = #{options}"
159
163
  sizes, width, height = get_image_sizes(name, options)
160
-
164
+
161
165
  fallback_host = false
162
- if is_relative
166
+ if is_relative
163
167
  test_path = app.asset_path(:images, "#{name}.svg")
164
168
  if is_relative_url?(test_path)
165
169
  if options.has_key?(:host)
166
170
  fallback_host = options[:host]
171
+ elsif app.config[:asset_host]
172
+ fallback_host = app.config[:asset_host]
167
173
  else
168
174
  warn "WARNING: Inline clowncar images require absolute paths. Please set a :host value"
169
175
  end
@@ -183,131 +189,53 @@ module Middleman
183
189
  Extension.svg_files_to_generate << [name, options]
184
190
  end
185
191
 
186
-
187
-
188
-
189
192
 
190
-
191
193
  helpers do
192
194
  def automatic_clowncar_tag(name, options={})
193
195
  internal = ""
194
196
 
195
197
  if options[:fallback]
196
-
197
198
  fallback = File.basename thumbnail_url(name,:small)
198
199
  fallback_path = extensions[:automatic_clowncar].get_image_path(name, fallback, true, false)
199
200
  internal = %{<!--[if lte IE 8]><img src="#{fallback_path}"><![endif]-->}
200
201
  end
201
202
 
203
+ width, height = extensions[:automatic_clowncar].get_physical_image_size(name)
204
+ object_style = ""
205
+ if options.has_key?(:prevent_upscaling) && options[:prevent_upscaling]
206
+ #if options.has_key?(:include_original) && options[:include_original]
207
+ #else
208
+ # width = extensions[:automatic_clowncar].options.sizes.map{|k,v| v }.sort.last
209
+ #end
210
+
211
+ object_style = "max-width:#{width}px;"
212
+ end
213
+
202
214
  if options.has_key?(:inline) && (options[:inline] === false)
203
215
  url = asset_path(:images, "#{name}.svg")
204
- %Q{<object type="image/svg+xml" data="#{url}">#{internal}</object>}
216
+ %Q{<object type="image/svg+xml" style="#{object_style}" data-aspect-ratio="#{width.to_f/height.to_f}" data="#{url}">#{internal}</object>}
205
217
  else
206
218
  data = extensions[:automatic_clowncar].generate_svg(name, true, options)
207
- %Q{<object type="image/svg+xml" data="data:image/svg+xml,#{::URI.escape(data)}">#{internal}</object>}
219
+ %Q{<object type="image/svg+xml" style="#{object_style}" data-aspect-ratio="#{width.to_f/height.to_f}" data="data:image/svg+xml,#{::URI.escape(data)}">#{internal}</object>}
208
220
  end
209
221
  end
210
222
 
211
223
  def thumbnail_specs(image, name)
212
224
  sizes = Extension.options_hash[:sizes]
213
- ThumbnailGenerator.specs(image, sizes)
225
+ ThumbnailGenerator.specs(image, sizes, app.source_dir)
214
226
  end
215
227
 
216
228
  def thumbnail_url(image, name, options = {})
217
229
  include_images_dir = options.delete :include_images_dir
218
230
 
219
231
  url = thumbnail_specs(image, name)[name][:name]
220
- url = File.join(images_dir, url) if include_images_dir
232
+ url = File.join(url) if include_images_dir
221
233
 
222
234
  url
223
235
  end
224
236
 
225
237
  end # helpers
226
238
 
227
- class SitemapExtension
228
- def initialize(app)
229
- @app = app
230
- end
231
-
232
- # Add sitemap resource for every image in the sprockets load path
233
- def manipulate_resource_list(resources)
234
-
235
- images_dir_abs = File.join(@app.source_dir, @app.images_dir)
236
-
237
- images_dir = @app.images_dir
238
-
239
- options = Extension.options_hash
240
- sizes = options[:sizes]
241
- namespace = options[:namespace_directory].join(',')
242
-
243
- files = Dir["#{images_dir_abs}/#{namespace}/*.{#{Extension.options_hash[:filetypes].join(',')}}"]
244
-
245
- resource_list = files.map do |file|
246
- path = file.gsub(@app.source_dir + File::SEPARATOR, '')
247
- specs = ::Middleman::AutomaticClowncar::ThumbnailGenerator.specs(path, sizes)
248
- specs.map do |name, spec|
249
- resource = nil
250
- # puts "#{path}: #{spec[:name]}: #{file}"
251
- resource = Middleman::Sitemap::Resource.new(@app.sitemap, spec[:name], file) unless name == :original
252
- end
253
- end.flatten.reject {|resource| resource.nil? }
254
-
255
- resources + resource_list
256
- end
257
- end # SitemapExtension
258
-
259
-
260
- # Rack middleware to convert images on the fly
261
- class Rack
262
-
263
- # Init
264
- # @param [Class] app
265
- # @param [Hash] options
266
- def initialize(app, options={})
267
- @app = app
268
- @options = options
269
-
270
- files = Dir["#{options[:images_source_dir]}/**/*.{#{options[:filetypes].join(',')}}"]
271
-
272
- @original_map = ThumbnailGenerator.original_map_for_files(files, options[:sizes])
273
-
274
- end
275
-
276
- # Rack interface
277
- # @param [Rack::Environmemt] env
278
- # @return [Array]
279
- def call(env)
280
- status, headers, response = @app.call(env)
281
-
282
- path = env["PATH_INFO"]
283
-
284
- path_on_disk = File.join(@options[:source_dir], path)
285
-
286
- #TODO: caching
287
- if original_specs = @original_map[path_on_disk]
288
- original_file = original_specs[:original]
289
- spec = original_specs[:spec]
290
- if spec.has_key? :sizes
291
- image = ::Magick::Image.read(original_file).first
292
- blob = nil
293
- image.change_geometry(spec[:sizes]) do |cols, rows, img|
294
- img = img.resize(cols, rows)
295
- img = img.sharpen(0.5, 0.5)
296
- blob = img.to_blob
297
- end
298
-
299
- unless blob.nil?
300
- status = 200
301
- headers["Content-Length"] = ::Rack::Utils.bytesize(blob).to_s
302
- headers["Content-Type"] = image.mime_type
303
- response = [blob]
304
- end
305
- end
306
- end
307
-
308
- [status, headers, response]
309
- end
310
- end
311
239
 
312
240
 
313
241
 
@@ -0,0 +1,45 @@
1
+ module Middleman
2
+ module AutomaticClowncar
3
+ class SitemapExtension
4
+
5
+ def initialize(extension)
6
+ @extension = extension
7
+ @app = extension.app
8
+ end
9
+
10
+ # Add sitemap resource for every image in the sprockets load path
11
+ def manipulate_resource_list(resources)
12
+ puts "resources = #{resources.class}"
13
+ options = Extension.options_hash
14
+ sizes = options[:sizes]
15
+ namespace = options[:namespace_directory].join(',')
16
+ glob = "#{@app.source_dir}/{#{namespace}}/*.{#{Extension.options_hash[:filetypes].join(',')}}"
17
+ files = Dir[glob]
18
+ resource_list = []
19
+ files.each do |file|
20
+ path = Utils.naked_origin(@app.source_dir,file)
21
+ specs = ::Middleman::AutomaticClowncar::ThumbnailGenerator.specs(path, sizes,@app.source_dir)
22
+ specs.each_pair do |name, spec|
23
+ # Remove the default resource and use a ThumbnailResource that will avoid repeat imgoptim
24
+ if name == :original
25
+ default_resource = resources.select{|r| r.destination_path == spec[:name]}
26
+ resources = resources - default_resource
27
+ end
28
+ dest_path = File.join(@app.root_path,'build', spec[:name])
29
+ source = File.exists?(dest_path) ? dest_path : file
30
+ resource_list << Middleman::AutomaticClowncar::ThumbnailResource.new(@app.sitemap,spec[:name],spec[:dimensions],file,@app.root_path,'build',@app.source_dir)
31
+ #resource_list << Middleman::Sitemap::Resource.new(@app.sitemap, spec[:name], source) unless name == :original
32
+ end
33
+ fname = specs.first[1][:name]
34
+ timestampDir = File.join(File.dirname(fname),File.basename(fname,'.*'))
35
+ timestampPath = File.join(timestampDir,'timestamp.txt')
36
+ resource_list << Middleman::AutomaticClowncar::TimestampResource.new(@app.sitemap, timestampPath, path, @app.source_dir)
37
+ end
38
+ resource_list.flatten.reject {|resource| resource.nil? }
39
+
40
+ resources + resource_list
41
+ end
42
+
43
+ end # SitemapExtension
44
+ end
45
+ end
@@ -1,4 +1,3 @@
1
- require 'RMagick'
2
1
 
3
2
  module Middleman
4
3
  module AutomaticClowncar
@@ -6,7 +5,12 @@ module Middleman
6
5
  class ThumbnailGenerator
7
6
  class << self
8
7
 
9
- def specs(origin, dimensions)
8
+ def specs(origin, dimensions, source_dir)
9
+ #puts "origin = #{origin}"
10
+ #puts "source_dir = #{source_dir}"
11
+ origin = Utils.naked_origin(source_dir,origin) # just in case
12
+ width, height = FastImage.size(File.join(source_dir,origin))
13
+
10
14
  dir = File.dirname(origin)
11
15
  filename = File.basename(origin,'.*')
12
16
  ext = File.extname(origin)
@@ -16,35 +20,14 @@ module Middleman
16
20
  ret = {original: {name: origin}}
17
21
 
18
22
  dimensions.each do |name, dimension|
23
+ next if dimension > width
19
24
  location = File.join(dir,"#{filename}-#{name}#{ext}")
20
25
  ret[name] = {name:location , dimensions: dimension}
21
26
  end
22
-
23
27
  ret
24
28
  end
25
-
26
- def generate(source_dir, output_dir, origin, specs)
27
- image = ::Magick::Image.read(File.join(source_dir, origin)).first
28
- specs.each do |name, spec|
29
- if spec.has_key? :dimensions then
30
- image.change_geometry(spec[:dimensions]) do |cols, rows, img|
31
- img = img.resize(cols, rows)
32
- img = img.sharpen(0.5, 0.5)
33
- img.write File.join(output_dir, spec[:name])
34
- end
35
- end
36
- end
37
- end
38
-
39
- def original_map_for_files(files, specs)
40
- map = files.inject({}) do |memo, file|
41
- generated_specs = self.specs(file, specs)
42
- generated_specs.each do |name, spec|
43
- memo[spec[:name]] = {:original => generated_specs[:original][:name], :spec => spec}
44
- end
45
- memo
46
- end
47
- end
29
+
30
+
48
31
  end
49
32
  end
50
33
  end
@@ -0,0 +1,54 @@
1
+ module Middleman
2
+ module AutomaticClowncar
3
+ class ThumbnailResource < ::Middleman::Sitemap::Resource
4
+
5
+ require 'mini_magick'
6
+
7
+ attr_accessor :output
8
+
9
+ def initialize(store, path, dimensions, origin, root_path, build_dir, source_dir)
10
+ @dimensions = dimensions
11
+ @origin = origin
12
+ @root_path = root_path
13
+ @build_dir = build_dir
14
+ @source_dir = source_dir
15
+ super(store, path, source_dir.to_s)
16
+ end
17
+
18
+ def template?
19
+ false
20
+ end
21
+
22
+ def source_file
23
+ nil
24
+ end
25
+
26
+ def render(*args, &block)
27
+ output_dir = File.join(@root_path,@build_dir)
28
+ dest_path = File.join(output_dir,@path)
29
+ img = nil
30
+ if Utils.timestamp_current?(@source_dir,@build_dir,@origin) && File.exist?(dest_path)
31
+ img = MiniMagick::Image.open(dest_path)
32
+ else
33
+ img = MiniMagick::Image.open(@origin)
34
+ img.resize(@dimensions) unless @dimensions.blank?
35
+ end
36
+ img.to_blob
37
+ end
38
+
39
+ def binary?
40
+ false
41
+ end
42
+
43
+ def raw_data
44
+ {}
45
+ end
46
+
47
+ def ignored?
48
+ false
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+