middleman-automatic-clowncar 0.0.1 → 4.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.
@@ -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
+