inkcite 1.0.0
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.
- checksums.yaml +7 -0
 - data/LICENSE +20 -0
 - data/README.md +110 -0
 - data/Rakefile +8 -0
 - data/assets/facebook-like.css +62 -0
 - data/assets/facebook-like.js +59 -0
 - data/assets/init/config.yml +97 -0
 - data/assets/init/helpers.tsv +31 -0
 - data/assets/init/source.html +60 -0
 - data/assets/init/source.txt +6 -0
 - data/bin/inkcite +6 -0
 - data/inkcite.gemspec +42 -0
 - data/lib/inkcite.rb +32 -0
 - data/lib/inkcite/cli/base.rb +128 -0
 - data/lib/inkcite/cli/build.rb +130 -0
 - data/lib/inkcite/cli/init.rb +58 -0
 - data/lib/inkcite/cli/preview.rb +30 -0
 - data/lib/inkcite/cli/server.rb +123 -0
 - data/lib/inkcite/cli/test.rb +61 -0
 - data/lib/inkcite/email.rb +219 -0
 - data/lib/inkcite/mailer.rb +140 -0
 - data/lib/inkcite/minifier.rb +151 -0
 - data/lib/inkcite/parser.rb +111 -0
 - data/lib/inkcite/renderer.rb +177 -0
 - data/lib/inkcite/renderer/base.rb +186 -0
 - data/lib/inkcite/renderer/button.rb +168 -0
 - data/lib/inkcite/renderer/div.rb +29 -0
 - data/lib/inkcite/renderer/element.rb +82 -0
 - data/lib/inkcite/renderer/footnote.rb +132 -0
 - data/lib/inkcite/renderer/google_analytics.rb +35 -0
 - data/lib/inkcite/renderer/image.rb +95 -0
 - data/lib/inkcite/renderer/image_base.rb +82 -0
 - data/lib/inkcite/renderer/in_browser.rb +38 -0
 - data/lib/inkcite/renderer/like.rb +73 -0
 - data/lib/inkcite/renderer/link.rb +243 -0
 - data/lib/inkcite/renderer/litmus.rb +33 -0
 - data/lib/inkcite/renderer/lorem.rb +39 -0
 - data/lib/inkcite/renderer/mobile_image.rb +67 -0
 - data/lib/inkcite/renderer/mobile_style.rb +40 -0
 - data/lib/inkcite/renderer/mobile_toggle.rb +27 -0
 - data/lib/inkcite/renderer/outlook_background.rb +48 -0
 - data/lib/inkcite/renderer/partial.rb +31 -0
 - data/lib/inkcite/renderer/preheader.rb +22 -0
 - data/lib/inkcite/renderer/property.rb +39 -0
 - data/lib/inkcite/renderer/responsive.rb +334 -0
 - data/lib/inkcite/renderer/span.rb +21 -0
 - data/lib/inkcite/renderer/table.rb +67 -0
 - data/lib/inkcite/renderer/table_base.rb +149 -0
 - data/lib/inkcite/renderer/td.rb +92 -0
 - data/lib/inkcite/uploader.rb +173 -0
 - data/lib/inkcite/util.rb +85 -0
 - data/lib/inkcite/version.rb +3 -0
 - data/lib/inkcite/view.rb +745 -0
 - data/lib/inkcite/view/context.rb +38 -0
 - data/lib/inkcite/view/media_query.rb +60 -0
 - data/lib/inkcite/view/tag_stack.rb +38 -0
 - data/test/email_spec.rb +16 -0
 - data/test/parser_spec.rb +72 -0
 - data/test/project/config.yml +98 -0
 - data/test/project/helpers.tsv +56 -0
 - data/test/project/images/inkcite.jpg +0 -0
 - data/test/project/source.html +58 -0
 - data/test/project/source.txt +6 -0
 - data/test/renderer/button_spec.rb +45 -0
 - data/test/renderer/div_spec.rb +101 -0
 - data/test/renderer/element_spec.rb +31 -0
 - data/test/renderer/footnote_spec.rb +57 -0
 - data/test/renderer/image_spec.rb +82 -0
 - data/test/renderer/link_spec.rb +84 -0
 - data/test/renderer/mobile_image_spec.rb +27 -0
 - data/test/renderer/mobile_style_spec.rb +37 -0
 - data/test/renderer/td_spec.rb +126 -0
 - data/test/renderer_spec.rb +28 -0
 - data/test/view_spec.rb +15 -0
 - metadata +333 -0
 
| 
         @@ -0,0 +1,61 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'litmus'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'inkcite/mailer'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Inkcite
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Cli
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Test
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.invoke email, opt
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    # Push the browser preview up to the server to ensure that the
         
     | 
| 
      
 11 
     | 
    
         
            +
                    # latest images are available.
         
     | 
| 
      
 12 
     | 
    
         
            +
                    email.upload
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    config = email.config[:litmus]
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    # Initialize the Litmus base.
         
     | 
| 
      
 17 
     | 
    
         
            +
                    Litmus::Base.new(config[:subdomain], config[:username], config[:password], true)
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                    # Send each version to Litmus separately
         
     | 
| 
      
 20 
     | 
    
         
            +
                    email.versions.each do |version|
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                      view = email.view(:preview, :email, version)
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                      # This will hold the Litmus Test Version which provides the GUID (e.g. email)
         
     | 
| 
      
 25 
     | 
    
         
            +
                      # to which we will send.
         
     | 
| 
      
 26 
     | 
    
         
            +
                      test_version = nil
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                      # Check to see if this email already has a test ID.
         
     | 
| 
      
 29 
     | 
    
         
            +
                      test_id = view.meta(:litmus_test_id)
         
     | 
| 
      
 30 
     | 
    
         
            +
                      if test_id.blank? || opt[:new]
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                        email_test = Litmus::EmailTest.create
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        # Store the litmus test ID in the email's meta data.
         
     | 
| 
      
 35 
     | 
    
         
            +
                        view.set_meta :litmus_test_id, email_test['id']
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                        # Extract the email address we need to send the test to.
         
     | 
| 
      
 38 
     | 
    
         
            +
                        test_version = email_test["test_set_versions"].first
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                      else
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                        # Create a new version of the test using the same ID as before.
         
     | 
| 
      
 43 
     | 
    
         
            +
                        test_version = Litmus::TestVersion.create(test_id)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      # Extract the email address to send the test to.
         
     | 
| 
      
 48 
     | 
    
         
            +
                      send_to = test_version["url_or_guid"]
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                      puts "Sending '#{view.subject}' to #{send_to} ..."
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                      Inkcite::Mailer.litmus(email, version, send_to)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                    true
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,219 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Inkcite
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Email
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                CACHE_BUST          = :'cache-bust'
         
     | 
| 
      
 5 
     | 
    
         
            +
                IMAGE_HOST          = :'image-host'
         
     | 
| 
      
 6 
     | 
    
         
            +
                IMAGE_PLACEHOLDERS  = :'image-placeholders'
         
     | 
| 
      
 7 
     | 
    
         
            +
                OPTIMIZE_IMAGES     = :'optimize-images'
         
     | 
| 
      
 8 
     | 
    
         
            +
                TRACK_LINKS         = :'track-links'
         
     | 
| 
      
 9 
     | 
    
         
            +
                VIEW_IN_BROWSER_URL = :'view-in-browser-url'
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # Sub-directory where images are located.
         
     | 
| 
      
 12 
     | 
    
         
            +
                IMAGES = 'images'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # Allowed environments.
         
     | 
| 
      
 15 
     | 
    
         
            +
                ENVIRONMENTS = [ :development, :preview, :production ].freeze
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                # The path to the directory from which the email is being generated.
         
     | 
| 
      
 18 
     | 
    
         
            +
                # e.g. /projects/emails/holiday-mailing
         
     | 
| 
      
 19 
     | 
    
         
            +
                attr_reader :path
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def initialize path
         
     | 
| 
      
 22 
     | 
    
         
            +
                  @path = path
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def config
         
     | 
| 
      
 26 
     | 
    
         
            +
                  Util.read_yml(File.join(path, 'config.yml'), true)
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def formats env
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  f = [ :email, :browser ]
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  # Need to make sure a source.txt exists before we can include
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # it in the list of known formats.
         
     | 
| 
      
 35 
     | 
    
         
            +
                  f << :text if File.exists?(project_file('source.txt'))
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  f
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def image_dir
         
     | 
| 
      
 41 
     | 
    
         
            +
                  File.join(path, IMAGES)
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def image_path file
         
     | 
| 
      
 45 
     | 
    
         
            +
                  File.join(image_dir, file)
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def meta key
         
     | 
| 
      
 49 
     | 
    
         
            +
                  meta_data[key.to_sym]
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def optimize_images
         
     | 
| 
      
 53 
     | 
    
         
            +
                  Minifier.images(self)
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def optimize_images?
         
     | 
| 
      
 57 
     | 
    
         
            +
                  config[Inkcite::Email::OPTIMIZE_IMAGES] == true
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                # Returns the directory that optimized, compressed images
         
     | 
| 
      
 61 
     | 
    
         
            +
                # have been saved to.
         
     | 
| 
      
 62 
     | 
    
         
            +
                def optimized_image_dir
         
     | 
| 
      
 63 
     | 
    
         
            +
                  File.join(path, optimize_images?? Minifier::IMAGE_CACHE : IMAGES)
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                def properties
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  opts = {
         
     | 
| 
      
 69 
     | 
    
         
            +
                    :n => NEW_LINE
         
     | 
| 
      
 70 
     | 
    
         
            +
                  }
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  # Load the project's properties, which may include references to additional
         
     | 
| 
      
 73 
     | 
    
         
            +
                  # properties in other directories.
         
     | 
| 
      
 74 
     | 
    
         
            +
                  read_properties opts, 'helpers.tsv'
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                  # As a convenience pre-populate the month name of the email.
         
     | 
| 
      
 77 
     | 
    
         
            +
                  mm = opts[:mm].to_i
         
     | 
| 
      
 78 
     | 
    
         
            +
                  opts[:month] = Date::MONTHNAMES[mm] if mm > 0
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  opts
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def project_file file
         
     | 
| 
      
 84 
     | 
    
         
            +
                  File.join(path, file)
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def set_meta key, value
         
     | 
| 
      
 88 
     | 
    
         
            +
                  md = meta_data
         
     | 
| 
      
 89 
     | 
    
         
            +
                  md[key.to_sym] = value
         
     | 
| 
      
 90 
     | 
    
         
            +
                  File.open(File.join(path, meta_file_name), 'w+') { |f| f.write(md.to_yaml) }
         
     | 
| 
      
 91 
     | 
    
         
            +
                  value
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                def upload
         
     | 
| 
      
 95 
     | 
    
         
            +
                  require_relative 'uploader'
         
     | 
| 
      
 96 
     | 
    
         
            +
                  Uploader.upload(self)
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def upload!
         
     | 
| 
      
 100 
     | 
    
         
            +
                  require_relative 'uploader'
         
     | 
| 
      
 101 
     | 
    
         
            +
                  Uploader.upload!(self)
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                def versions
         
     | 
| 
      
 105 
     | 
    
         
            +
                  [* self.config[:versions] || :default ].collect(&:to_sym)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                def view environment, format, version=nil
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                  environment = environment.to_sym
         
     | 
| 
      
 111 
     | 
    
         
            +
                  format = format.to_sym
         
     | 
| 
      
 112 
     | 
    
         
            +
                  version = (version || versions.first).to_sym
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                  raise "Unknown environment \"#{environment}\" - must be one of #{ENVIRONMENTS.join(',')}" unless ENVIRONMENTS.include?(environment)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  raise "Unknown format \"#{format}\" - must be one of #{FORMATS.join(',')}" unless FORMATS.include?(format)
         
     | 
| 
      
 116 
     | 
    
         
            +
                  raise "Unknown version: \"#{version}\" - must be one of #{versions.join(',')}" unless versions.include?(version)
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  opt = properties
         
     | 
| 
      
 119 
     | 
    
         
            +
                  opt.merge!(self.config)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                  # Instantiate a new view of this email with the desired view and
         
     | 
| 
      
 122 
     | 
    
         
            +
                  # format.
         
     | 
| 
      
 123 
     | 
    
         
            +
                  View.new(self, environment, format, version, opt)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                # Returns an array of all possible Views (every combination of version
         
     | 
| 
      
 128 
     | 
    
         
            +
                # and format )of this email for the designated environment.
         
     | 
| 
      
 129 
     | 
    
         
            +
                def views environment, &block
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                  vs = []
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  formats(environment).each do |format|
         
     | 
| 
      
 134 
     | 
    
         
            +
                    versions.each do |version|
         
     | 
| 
      
 135 
     | 
    
         
            +
                      ev = view(environment, format, version)
         
     | 
| 
      
 136 
     | 
    
         
            +
                      yield(ev) if block_given?
         
     | 
| 
      
 137 
     | 
    
         
            +
                      vs << ev
         
     | 
| 
      
 138 
     | 
    
         
            +
                    end
         
     | 
| 
      
 139 
     | 
    
         
            +
                  end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                  vs
         
     | 
| 
      
 142 
     | 
    
         
            +
                end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                private
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                # Allowed formats.
         
     | 
| 
      
 147 
     | 
    
         
            +
                FORMATS = [ :browser, :email, :text ].freeze
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                # Name of the property controlling the meta data file name and
         
     | 
| 
      
 150 
     | 
    
         
            +
                # the default file name.
         
     | 
| 
      
 151 
     | 
    
         
            +
                META_FILE_NAME = :'meta-file'
         
     | 
| 
      
 152 
     | 
    
         
            +
                META_FILE      = '.inkcite'
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                COMMENT         = '//'
         
     | 
| 
      
 155 
     | 
    
         
            +
                NEW_LINE        = "\n"
         
     | 
| 
      
 156 
     | 
    
         
            +
                TAB             = "\t"
         
     | 
| 
      
 157 
     | 
    
         
            +
                CARRIAGE_RETURN = "\r"
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                # Used for
         
     | 
| 
      
 160 
     | 
    
         
            +
                MULTILINE_START = "<<-START"
         
     | 
| 
      
 161 
     | 
    
         
            +
                MULTILINE_END = "END->>"
         
     | 
| 
      
 162 
     | 
    
         
            +
                TAB_TO_SPACE = '  '
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                def meta_data
         
     | 
| 
      
 165 
     | 
    
         
            +
                  Util.read_yml(File.join(path, meta_file_name), false)
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def meta_file_name
         
     | 
| 
      
 169 
     | 
    
         
            +
                  config[META_FILE_NAME] || META_FILE
         
     | 
| 
      
 170 
     | 
    
         
            +
                end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
                def read_properties into, file
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
                  fp = File.join(path, file)
         
     | 
| 
      
 175 
     | 
    
         
            +
                  abort("Can't find #{file} in #{path} - are you sure this is an Inkcite project?") unless File.exists?(fp)
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                  # Consolidate line-breaks for simplicity
         
     | 
| 
      
 178 
     | 
    
         
            +
                  raw = File.read(fp)
         
     | 
| 
      
 179 
     | 
    
         
            +
                  raw.gsub!(/[\r\f\n]{1,}/, NEW_LINE)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  # Initial position of the
         
     | 
| 
      
 182 
     | 
    
         
            +
                  multiline_starts_at = 0
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                  # Determine if there are any multiline declarations - those that are wrapped with
         
     | 
| 
      
 185 
     | 
    
         
            +
                  # <<-START and END->> and reduce them to single line declarations.
         
     | 
| 
      
 186 
     | 
    
         
            +
                  while (multiline_starts_at = raw.index(MULTILINE_START, multiline_starts_at))
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
                    break unless (multiline_ends_at = raw.index(MULTILINE_END, multiline_starts_at))
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                    declaration = raw[(multiline_starts_at+MULTILINE_START.length)..multiline_ends_at - 1]
         
     | 
| 
      
 191 
     | 
    
         
            +
                    declaration.strip!
         
     | 
| 
      
 192 
     | 
    
         
            +
                    declaration.gsub!(/\t/, TAB_TO_SPACE)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    declaration.gsub!(/\n/, "\r")
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
                    raw[multiline_starts_at..multiline_ends_at+MULTILINE_END.length - 1] = declaration
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                  end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
                  raw.split(NEW_LINE).each do |line|
         
     | 
| 
      
 200 
     | 
    
         
            +
                    next if line.starts_with?(COMMENT)
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                    line.gsub!(/\r/, NEW_LINE)
         
     | 
| 
      
 203 
     | 
    
         
            +
                    line.strip!
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                    key, open, close = line.split(TAB)
         
     | 
| 
      
 206 
     | 
    
         
            +
                    next if key.blank?
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                    into[key.to_sym] = open.to_s.freeze
         
     | 
| 
      
 209 
     | 
    
         
            +
             
     | 
| 
      
 210 
     | 
    
         
            +
                    # Prepend the key with a "/" and populate the closing tag.
         
     | 
| 
      
 211 
     | 
    
         
            +
                    into["/#{key}".to_sym] = close.to_s.freeze
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
                  end
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                  true
         
     | 
| 
      
 216 
     | 
    
         
            +
                end
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
      
 218 
     | 
    
         
            +
              end
         
     | 
| 
      
 219 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,140 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'mail'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Inkcite
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Mailer
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def self.client email
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  # Determine which preview this is
         
     | 
| 
      
 9 
     | 
    
         
            +
                  count = increment(email, :preview)
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # Get the declared set of recipients.
         
     | 
| 
      
 12 
     | 
    
         
            +
                  recipients = email.config[:recipients]
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  # Get the list of client address(es) - check both client and clients
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # as a convenience.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  to = recipients[:clients] || recipients[:client]
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # If this is the first preview, check to see if there is an
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # additional set of recipients for this initial mailing.
         
     | 
| 
      
 20 
     | 
    
         
            +
                  if count == 1
         
     | 
| 
      
 21 
     | 
    
         
            +
                    also_to = recipients[FIRST_PREVIEW]
         
     | 
| 
      
 22 
     | 
    
         
            +
                    #to = [* to] + [* also_to] unless also_to.blank?
         
     | 
| 
      
 23 
     | 
    
         
            +
                    to = [* also_to] unless also_to.blank?
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  # Always cc internal recipients so everyone stays informed of feedback.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  cc = recipients[:internal]
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  self.send(email, {
         
     | 
| 
      
 30 
     | 
    
         
            +
                      :to => to,
         
     | 
| 
      
 31 
     | 
    
         
            +
                      :cc => cc,
         
     | 
| 
      
 32 
     | 
    
         
            +
                      :bcc => true,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      :tag => "Preview ##{count}"
         
     | 
| 
      
 34 
     | 
    
         
            +
                  })
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def self.developer email
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  count = increment(email, :developer)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  self.send(email, {
         
     | 
| 
      
 43 
     | 
    
         
            +
                      :tag => "Developer Test ##{count}"
         
     | 
| 
      
 44 
     | 
    
         
            +
                  })
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def self.litmus email, version, litmus_email
         
     | 
| 
      
 49 
     | 
    
         
            +
                  self.send_version(email, version, { :to => litmus_email })
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def self.internal email
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  recipients = email.config[:recipients]
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  # Determine which preview this is
         
     | 
| 
      
 57 
     | 
    
         
            +
                  count = increment(email, :internal)
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  self.send(email, {
         
     | 
| 
      
 60 
     | 
    
         
            +
                      :to => recipients[:internal],
         
     | 
| 
      
 61 
     | 
    
         
            +
                      :bcc => true,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      :tag => "Internal Proof ##{count}"
         
     | 
| 
      
 63 
     | 
    
         
            +
                  })
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                private
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                # Name of the distribution list used on the first preview.  For one
         
     | 
| 
      
 70 
     | 
    
         
            +
                # client, they wanted the first preview sent to additional people
         
     | 
| 
      
 71 
     | 
    
         
            +
                # but subsequent previews went to a shorter list.
         
     | 
| 
      
 72 
     | 
    
         
            +
                FIRST_PREVIEW = :'first-preview'
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                def self.increment email, sym
         
     | 
| 
      
 75 
     | 
    
         
            +
                  count = email.meta(sym).to_i + 1
         
     | 
| 
      
 76 
     | 
    
         
            +
                  email.set_meta sym, count
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                # Sends each version of the provided email with the indicated options.
         
     | 
| 
      
 80 
     | 
    
         
            +
                def self.send email, opt
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  email.versions.each do |version|
         
     | 
| 
      
 83 
     | 
    
         
            +
                    self.send_version(email, version, opt)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def self.send_version email, version, opt
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  config = email.config[:smtp]
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  Mail.defaults do
         
     | 
| 
      
 93 
     | 
    
         
            +
                    delivery_method :smtp, {
         
     | 
| 
      
 94 
     | 
    
         
            +
                      :address              => config[:host],
         
     | 
| 
      
 95 
     | 
    
         
            +
                      :port                 => config[:port],
         
     | 
| 
      
 96 
     | 
    
         
            +
                      :user_name            => config[:username],
         
     | 
| 
      
 97 
     | 
    
         
            +
                      :password             => config[:password],
         
     | 
| 
      
 98 
     | 
    
         
            +
                      :authentication       => :plain,
         
     | 
| 
      
 99 
     | 
    
         
            +
                      :enable_starttls_auto => true
         
     | 
| 
      
 100 
     | 
    
         
            +
                    }
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  # The address of the developer
         
     | 
| 
      
 104 
     | 
    
         
            +
                  _from = config[:from]
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  # Subject line tag such as "Preview #3"
         
     | 
| 
      
 107 
     | 
    
         
            +
                  _tag = opt[:tag]
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  # True if the developer should be bcc'd.
         
     | 
| 
      
 110 
     | 
    
         
            +
                  _bcc = opt[:bcc] == true
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                  # The version of the email we will be sending.
         
     | 
| 
      
 113 
     | 
    
         
            +
                  _view = email.view(:preview, :email, version)
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
                  _subject = _view.subject
         
     | 
| 
      
 116 
     | 
    
         
            +
                  _subject = "#{_subject} (#{_tag})" unless _tag.blank?
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                  mail = Mail.new do
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    to      opt[:to] || _from
         
     | 
| 
      
 121 
     | 
    
         
            +
                    cc      opt[:cc]
         
     | 
| 
      
 122 
     | 
    
         
            +
                    from    _from
         
     | 
| 
      
 123 
     | 
    
         
            +
                    subject _subject
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    bcc(_from) if _bcc
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                    html_part do
         
     | 
| 
      
 128 
     | 
    
         
            +
                      content_type 'text/html; charset=UTF-8'
         
     | 
| 
      
 129 
     | 
    
         
            +
                      body _view.render!
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  mail.deliver!
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,151 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yui/compressor'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Inkcite
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Minifier
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                # Directory of optimized images
         
     | 
| 
      
 7 
     | 
    
         
            +
                IMAGE_CACHE = ".images"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def self.css code, ctx
         
     | 
| 
      
 10 
     | 
    
         
            +
                  minify?(ctx) ? css_compressor(ctx).compress(code) : code
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.html lines, ctx
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  if minify?(ctx)
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    # Will hold the assembled, minified HTML as it is prepared.
         
     | 
| 
      
 18 
     | 
    
         
            +
                    html = ''
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    # Will hold the line being assembled until it reaches the maximum
         
     | 
| 
      
 21 
     | 
    
         
            +
                    # allowed line length.
         
     | 
| 
      
 22 
     | 
    
         
            +
                    packed_line = ''
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    lines.each do |line|
         
     | 
| 
      
 25 
     | 
    
         
            +
                      next if line.blank?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                      line.strip!
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                      ## Compress all in-line styles.
         
     | 
| 
      
 30 
     | 
    
         
            +
                      #Parser.each line, INLINE_STYLE_REGEX do |style|
         
     | 
| 
      
 31 
     | 
    
         
            +
                      #  style.gsub!(/: +/, ':')
         
     | 
| 
      
 32 
     | 
    
         
            +
                      #  style.gsub!(/; +/, ';')
         
     | 
| 
      
 33 
     | 
    
         
            +
                      #  style.gsub!(/;+/, ';')
         
     | 
| 
      
 34 
     | 
    
         
            +
                      #  style.gsub!(/;+$/, '')
         
     | 
| 
      
 35 
     | 
    
         
            +
                      #  "style=\"#{style}\""
         
     | 
| 
      
 36 
     | 
    
         
            +
                      #end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      # If the length of the packed line with the addition of this line of content would
         
     | 
| 
      
 39 
     | 
    
         
            +
                      # exceed the maximum allowed line length, then push the collected lines onto the
         
     | 
| 
      
 40 
     | 
    
         
            +
                      # html and start a new line.
         
     | 
| 
      
 41 
     | 
    
         
            +
                      if !packed_line.blank? && packed_line.length + line.length > MAXIMUM_LINE_LENGTH
         
     | 
| 
      
 42 
     | 
    
         
            +
                        html << packed_line
         
     | 
| 
      
 43 
     | 
    
         
            +
                        html << NEW_LINE
         
     | 
| 
      
 44 
     | 
    
         
            +
                        packed_line = ''
         
     | 
| 
      
 45 
     | 
    
         
            +
                      end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      packed_line << line
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    # Make sure to get any last lines assembled on the packed line.
         
     | 
| 
      
 52 
     | 
    
         
            +
                    html << packed_line unless packed_line.blank?
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    html
         
     | 
| 
      
 55 
     | 
    
         
            +
                  else
         
     | 
| 
      
 56 
     | 
    
         
            +
                    lines.join(NEW_LINE)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def self.images email
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  image_optim_path = '/Applications/ImageOptim.app/Contents/MacOS/ImageOptim'
         
     | 
| 
      
 65 
     | 
    
         
            +
                  image_optim = File.exists?(image_optim_path)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  abort "Can't find ImageOptim (#{image_optim_path}) - download it from https://imageoptim.com" unless image_optim
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  images_path = email.image_dir
         
     | 
| 
      
 69 
     | 
    
         
            +
                  cache_path = email.project_file(IMAGE_CACHE)
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  # If the image cache exists, we need to check to see if any images have been
         
     | 
| 
      
 72 
     | 
    
         
            +
                  # removed since the last build.
         
     | 
| 
      
 73 
     | 
    
         
            +
                  if File.exists?(cache_path)
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    # Get a list of the files in the cache that do not also exist in the
         
     | 
| 
      
 76 
     | 
    
         
            +
                    # project's images/ directory.
         
     | 
| 
      
 77 
     | 
    
         
            +
                    removed_images = Dir.entries(cache_path) - Dir.entries(images_path)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    unless removed_images.blank?
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                      # Convert the images to fully-qualified paths and then remove
         
     | 
| 
      
 81 
     | 
    
         
            +
                      # those files from the cache
         
     | 
| 
      
 82 
     | 
    
         
            +
                      removed_images = removed_images.collect { |img| File.join(cache_path, img ) }
         
     | 
| 
      
 83 
     | 
    
         
            +
                      FileUtils.rm (removed_images)
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  # Check to see if there are new or updated images that need to be re-optimized.
         
     | 
| 
      
 90 
     | 
    
         
            +
                  updated_images = Dir.entries(images_path).select do |img|
         
     | 
| 
      
 91 
     | 
    
         
            +
                    unless img.start_with?('.')
         
     | 
| 
      
 92 
     | 
    
         
            +
                      cimg = File.join(cache_path, img)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      !File.exists?(cimg) || (File.stat(File.join(images_path, img)).mtime > File.stat(cimg).mtime)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    end
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  return if updated_images.blank?
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  # This is the temporary path into which new or updated images will
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # be copied and then optimized.
         
     | 
| 
      
 101 
     | 
    
         
            +
                  temp_path = email.project_file(IMAGE_TEMP)
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  # Make sure there is no existing temporary directory to interfere
         
     | 
| 
      
 104 
     | 
    
         
            +
                  # with the image processing.
         
     | 
| 
      
 105 
     | 
    
         
            +
                  FileUtils.rm_rf(temp_path)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  FileUtils.mkpath(temp_path)
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                  # Copy all of the images that need updating into the temporary directory.
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # Specifically joining the images_path to the image to avoid Email's
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # image_path which may change it's directory if optimization is enabled.
         
     | 
| 
      
 111 
     | 
    
         
            +
                  updated_images.each { |img| FileUtils.cp(File.join(images_path, img), File.join(temp_path, img)) }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  # Optimize all of the images.
         
     | 
| 
      
 114 
     | 
    
         
            +
                  system("#{image_optim_path} #{temp_path}") if image_optim
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  FileUtils.cp_r(File.join(temp_path, "."), cache_path)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  FileUtils.rm_rf(temp_path)
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                def self.js code, ctx
         
     | 
| 
      
 122 
     | 
    
         
            +
                  minify?(ctx) ? js_compressor(ctx).compress(code) : code
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                private
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                # Temporary directory that new or updated images will be copied into
         
     | 
| 
      
 128 
     | 
    
         
            +
                # to be optimized and then cached in .images
         
     | 
| 
      
 129 
     | 
    
         
            +
                IMAGE_TEMP = ".images-temp"
         
     | 
| 
      
 130 
     | 
    
         
            +
             
     | 
| 
      
 131 
     | 
    
         
            +
                NEW_LINE = "\n"
         
     | 
| 
      
 132 
     | 
    
         
            +
                MAXIMUM_LINE_LENGTH = 800
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                # Used to match inline styles that will be compressed when minifying
         
     | 
| 
      
 135 
     | 
    
         
            +
                # the entire email.
         
     | 
| 
      
 136 
     | 
    
         
            +
                INLINE_STYLE_REGEX = /style=\"([^\"]+)\"/
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                def self.minify? ctx
         
     | 
| 
      
 139 
     | 
    
         
            +
                  ctx.is_enabled?(:minify)
         
     | 
| 
      
 140 
     | 
    
         
            +
                end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                def self.js_compressor ctx
         
     | 
| 
      
 143 
     | 
    
         
            +
                  ctx.js_compressor ||= YUI::JavaScriptCompressor.new(:munge => true)
         
     | 
| 
      
 144 
     | 
    
         
            +
                end
         
     | 
| 
      
 145 
     | 
    
         
            +
             
     | 
| 
      
 146 
     | 
    
         
            +
                def self.css_compressor ctx
         
     | 
| 
      
 147 
     | 
    
         
            +
                  ctx.css_compressor ||= YUI::CssCompressor.new(:line_break => (ctx.email?? MAXIMUM_LINE_LENGTH : nil))
         
     | 
| 
      
 148 
     | 
    
         
            +
                end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
            end
         
     |