require 'middleman-core' require 'middleman-automatic-clowncar/thumbnail-generator' require 'middleman-automatic-clowncar/timestamp-resource' require 'middleman-automatic-clowncar/thumbnail-resource' require 'middleman-automatic-clowncar/sitemap-extension' require 'middleman-automatic-clowncar/utils' require 'fastimage' module Middleman module AutomaticClowncar class Extension < Middleman::Extension SVG_TEMPLATE = "" option :sizes, {}, "The sizes of thumbnails to generate" option :namespace_directory, ["**"], "The directories that should be clowncared. (Outside of the sprockets images dir.)" option :filetypes, [:jpg, :jpeg, :png], "The types of files to use for automatic clowncaring." option :include_originals, false, "Always include original images. (Or not.)" cattr_accessor :options_hash def initialize(app, options_hash={}, &block) super options_hash[:filetypes] ||= [:jpg, :jpeg, :png] Extension.options_hash = options_hash # Clowncar bits require 'uri' require 'pathname' @ready = false #Thumbnailer app.after_configuration do #stash the source images dir in options for the Rack middleware Extension.options_hash[:source_dir] = app.source_dir sizes = Extension.options_hash[:sizes] namespace = Extension.options_hash[:namespace_directory].join(',') dir = Pathname.new(app.source_dir) glob = "#{dir}/{#{namespace}}/*.{#{Extension.options_hash[:filetypes].join(',')}}" files = Dir[glob] end end def manipulate_resource_list(resources) SitemapExtension.new(self).manipulate_resource_list(resources) end def after_configuration @ready = true end def is_relative_url?(path) begin uri = URI(path) rescue URI::InvalidURIError # Nothing we can do with it, it's not really a URI return false end !uri.host end def get_image_path(name, path, is_relative, fallback_host) #puts "@@@@@@@ calling get_image_path for name:#{name} path:#{path}, is_relative:#{is_relative}, fallback_host:#{fallback_host}" begin uri = URI(path) rescue URI::InvalidURIError # Nothing we can do with it, it's not really a URI return path end if uri.host path else svg_path = File.join(File.dirname(name),File.basename(name,".*"), path) if is_relative url = app.asset_path(:images, svg_path) images_dir = app.config[:images_dir] || 'images' url = url.sub("/#{images_dir}/",'/') if fallback_host && is_relative_url?(url) File.join(fallback_host, url) else url end else svg_path end end end def generate_media_queries(name, sizes, is_relative, fallback_host) output = [] if sizes.keys.length === 1 return "svg{background-image:url(#{get_image_path(name, sizes[sizes.keys.first], is_relative, fallback_host)});}" end previous_key = nil sizes.keys.sort.each_with_index do |key, i| line = ["@media screen and "] if i == 0 line << "(max-width:#{key}px)" elsif i == (sizes.keys.length - 1) line << "(min-width:#{previous_key+1}px)" else line << "(min-width:#{previous_key+1}px) and (max-width:#{key}px)" end line << "{svg{background-image:url(#{get_image_path(name, sizes[key], is_relative, fallback_host)});}}" output << line.join("") previous_key = key end output.join("") end def get_physical_image_size(name) main_abs_path = File.join(app.source_dir,name) FastImage.size(main_abs_path, :raise_on_failure => true) end def get_image_sizes(name, options) #puts "getting images sizes for #{name}" main_abs_path = File.join(app.source_dir,name) extname = File.extname(name) basename = File.basename(name, ".*") return {} unless File.exist? main_abs_path width, height = ::FastImage.size(main_abs_path, :raise_on_failure => true) sizes = {} Extension.options_hash[:sizes].each_pair do |sname,swidth| next if swidth > width sizes[swidth] = "#{basename}-#{sname}#{extname}" end if options[:include_original] || Extension.options_hash[:include_originals] sizes[width] = "../#{basename}#{extname}" end #puts "-"*30 #puts [sizes, width, height] [sizes, width, height] end def generate_svg(name, is_relative, options) #puts "name for generate_svg = #{name}" #puts "options for generate_svg = #{options}" sizes, width, height = get_image_sizes(name, options) fallback_host = false if is_relative test_path = app.asset_path(:images, "#{name}.svg") if is_relative_url?(test_path) if options.has_key?(:host) fallback_host = options[:host] elsif app.config[:asset_host] fallback_host = app.config[:asset_host] else warn "WARNING: Inline clowncar images require absolute paths. Please set a :host value" end end end media_queries = generate_media_queries(name, sizes, is_relative, fallback_host) xml = SVG_TEMPLATE.dup xml.sub!("::media_queries::", media_queries) xml.sub!("::width::", width.to_s) xml.sub!("::height::", height.to_s) xml end def generate_clowncar(name, options={}) Extension.svg_files_to_generate << [name, options] end helpers do def automatic_clowncar_sizes Extension.options_hash[:sizes] end def automatic_clowncar_escape(str) alpha = "a-zA-Z" alnum = "#{alpha}\\d" unreserved = "\\-_.!~*'()#{alnum}" reserved = ";/?:@&=+$,\\[\\]" unsafe = Regexp.new("[^#{unreserved}#{reserved}]") str.gsub(unsafe) do us = $& tmp = '' us.each_byte do |uc| tmp << sprintf('%%%02X', uc) end tmp end.force_encoding(Encoding::US_ASCII) end def automatic_clowncar_tag(name, options={}) internal = "" if options[:fallback] fallback = File.basename thumbnail_url(name,:small) fallback_path = extensions[:automatic_clowncar].get_image_path(name, fallback, true, false) internal = %{} end width, height = extensions[:automatic_clowncar].get_physical_image_size(name) object_style = "" if options.has_key?(:prevent_upscaling) && options[:prevent_upscaling] #if options.has_key?(:include_original) && options[:include_original] #else # width = extensions[:automatic_clowncar].options.sizes.map{|k,v| v }.sort.last #end object_style = "max-width:#{width}px;" end if options.has_key?(:inline) && (options[:inline] === false) url = asset_path(:images, "#{name}.svg") %Q{#{internal}} else data = extensions[:automatic_clowncar].generate_svg(name, true, options) puts "data ===============" puts data %Q{#{internal}} end end def thumbnail_specs(image, name) sizes = Extension.options_hash[:sizes] ThumbnailGenerator.specs(image, sizes, app.source_dir) end def thumbnail_url(image, name, options = {}) include_images_dir = options.delete :include_images_dir url = thumbnail_specs(image, name)[name][:name] url = File.join(url) if include_images_dir url end end # helpers end # Extension end # AutomaticClowncar end # Middleman