inkcite 1.13.0 → 1.14.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 +4 -4
- data/README.md +1 -1
- data/inkcite.gemspec +1 -0
- data/lib/inkcite.rb +1 -1
- data/lib/inkcite/cli/server.rb +31 -6
- data/lib/inkcite/facade.rb +6 -0
- data/lib/inkcite/{animation.rb → facade/animation.rb} +19 -56
- data/lib/inkcite/{renderer → facade}/element.rb +0 -0
- data/lib/inkcite/facade/keyframe.rb +83 -0
- data/lib/inkcite/{renderer → facade}/style.rb +4 -0
- data/lib/inkcite/minifier.rb +141 -57
- data/lib/inkcite/parser.rb +1 -1
- data/lib/inkcite/renderer.rb +2 -2
- data/lib/inkcite/renderer/background.rb +1 -1
- data/lib/inkcite/renderer/base.rb +1 -1
- data/lib/inkcite/renderer/container_base.rb +3 -4
- data/lib/inkcite/renderer/fireworks.rb +231 -0
- data/lib/inkcite/renderer/in_browser.rb +1 -2
- data/lib/inkcite/renderer/responsive.rb +29 -2
- data/lib/inkcite/renderer/snow.rb +1 -1
- data/lib/inkcite/renderer/special_effect.rb +66 -29
- data/lib/inkcite/renderer/table_base.rb +6 -0
- data/lib/inkcite/renderer/td.rb +7 -2
- data/lib/inkcite/renderer/video_preview.rb +7 -1
- data/lib/inkcite/util.rb +56 -0
- data/lib/inkcite/version.rb +1 -1
- data/lib/inkcite/view.rb +42 -1
- data/test/animation_spec.rb +38 -5
- data/test/renderer/div_spec.rb +30 -0
- data/test/renderer/td_spec.rb +25 -0
- data/test/renderer/video_preview_spec.rb +1 -1
- data/test/util_spec.rb +11 -0
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5968f3e718b373eb3740d89dd019165130776a2
|
4
|
+
data.tar.gz: e961a846c6e2d0664a66a1ae5abc9ed791db34dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 068fe33972b2d0f91a9c8be212ac3781392860d518aaf5277104cb3af1e1491766e2f3a5da47bed778bea41273bbd1fba3335c8f0d026845e16d29da85e06311
|
7
|
+
data.tar.gz: 4f39b31b971373bb1beb2ab1c6689a53bdd73a0e75d3def5bc2eeaae61e83449e7a43581bce9375d2434c2c7e6178a4b912b098459a304080acb11be46664595
|
data/README.md
CHANGED
@@ -112,7 +112,7 @@ developer questions in a timely manner.
|
|
112
112
|
|
113
113
|
## License
|
114
114
|
|
115
|
-
Copyright (c) 2014-
|
115
|
+
Copyright (c) 2014-2017 Jeffrey D. Hoffman. MIT Licensed, see [LICENSE] for
|
116
116
|
details.
|
117
117
|
|
118
118
|
[Middleman]: http://middlemanapp.com
|
data/inkcite.gemspec
CHANGED
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_dependency 'htmlbeautifier'
|
34
34
|
spec.add_dependency 'image_optim'
|
35
35
|
spec.add_dependency 'image_optim_pack'
|
36
|
+
spec.add_dependency 'kraken-io'
|
36
37
|
spec.add_dependency 'listen'
|
37
38
|
spec.add_dependency 'litmus'
|
38
39
|
spec.add_dependency 'mail'
|
data/lib/inkcite.rb
CHANGED
@@ -23,13 +23,13 @@ require 'active_support/core_ext/string/inflections'
|
|
23
23
|
require 'active_support/core_ext/string/starts_ends_with'
|
24
24
|
|
25
25
|
require 'inkcite/version'
|
26
|
+
require 'inkcite/facade'
|
26
27
|
require 'inkcite/email'
|
27
28
|
require 'inkcite/util'
|
28
29
|
require 'inkcite/view'
|
29
30
|
require 'inkcite/minifier'
|
30
31
|
require 'inkcite/parser'
|
31
32
|
require 'inkcite/renderer'
|
32
|
-
require 'inkcite/animation'
|
33
33
|
|
34
34
|
module Inkcite
|
35
35
|
|
data/lib/inkcite/cli/server.rb
CHANGED
@@ -53,7 +53,8 @@ module Inkcite
|
|
53
53
|
# InkciteApp to server the email as the root index page.
|
54
54
|
app = Rack::Builder.new do
|
55
55
|
use Rack::LiveReload
|
56
|
-
use Rack::Static, :urls => %w( /images
|
56
|
+
use Rack::Static, :urls => %w( /images/ ), :root => '.'
|
57
|
+
use OptimizedImage, :email => email, :urls => %w( /images-optim/ ), :root => '.'
|
57
58
|
run InkciteApp.new(email, opts)
|
58
59
|
end
|
59
60
|
|
@@ -88,6 +89,35 @@ module Inkcite
|
|
88
89
|
|
89
90
|
private
|
90
91
|
|
92
|
+
# Extends Rack::Static to provide dynamic image
|
93
|
+
# minification on demand. When an image is requested
|
94
|
+
# from the images-optim directory, compression is
|
95
|
+
# performed on the desired image if necessary and then
|
96
|
+
# the optimized image is returned.
|
97
|
+
class OptimizedImage < Rack::Static
|
98
|
+
|
99
|
+
def initialize app, opts
|
100
|
+
@email = opts[:email]
|
101
|
+
super
|
102
|
+
end
|
103
|
+
|
104
|
+
def call env
|
105
|
+
|
106
|
+
# e.g. images-optim/my-image.jpg
|
107
|
+
path = env['PATH_INFO']
|
108
|
+
|
109
|
+
# Minify the image if the source version in images/ is newer
|
110
|
+
# or if the configuration file controlling optimization has
|
111
|
+
# been updated since the last time the image was requested.
|
112
|
+
Minifier.image(@email, File.basename(path), false) if can_serve(path)
|
113
|
+
|
114
|
+
# Let the super method handle the actual serving of the image.
|
115
|
+
super
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
91
121
|
class InkciteApp
|
92
122
|
|
93
123
|
def initialize email, opts
|
@@ -123,11 +153,6 @@ module Inkcite
|
|
123
153
|
puts ''
|
124
154
|
puts "#{ts} Rendering your email [environment=#{environment}, format=#{format}, version=#{version || 'default'}]"
|
125
155
|
|
126
|
-
# Before the rendering takes place, trigger image optimization of any
|
127
|
-
# new or updated images. The {image} tag takes care of injecting the
|
128
|
-
# right path (optimized or not) depending on which version is needed.
|
129
|
-
@email.optimize_images
|
130
|
-
|
131
156
|
view = @email.view(environment, format, version)
|
132
157
|
|
133
158
|
html = view.render!
|
@@ -1,71 +1,27 @@
|
|
1
1
|
module Inkcite
|
2
2
|
class Animation
|
3
3
|
|
4
|
-
|
4
|
+
# A collection of animations assigned to a single element.
|
5
|
+
class Composite
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
def initialize percent, ctx, styles={}
|
9
|
-
|
10
|
-
# Animation percents are always rounded to the nearest whole number.
|
11
|
-
@percent = percent.round(0)
|
12
|
-
|
13
|
-
# Instantiate a new Style for this percentage.
|
14
|
-
@style = Inkcite::Renderer::Style.new("#{@percent}%", ctx, styles)
|
15
|
-
|
16
|
-
end
|
17
|
-
|
18
|
-
def [] key
|
19
|
-
@style[key]
|
20
|
-
end
|
21
|
-
|
22
|
-
def []= key, val
|
23
|
-
@style[key] = val
|
24
|
-
end
|
25
|
-
|
26
|
-
# For style chaining - e.g. keyframe.add(:key1, 'val').add(:key)
|
27
|
-
def add key, val
|
28
|
-
@style[key] = val
|
29
|
-
self
|
7
|
+
def initialize
|
8
|
+
@animations = []
|
30
9
|
end
|
31
10
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@style[key] ||= ''
|
36
|
-
@style[key] << ' ' unless @style[key].blank?
|
37
|
-
@style[key] << val
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def add_with_prefixes key, val, ctx
|
42
|
-
|
43
|
-
ctx.prefixes.each do |prefix|
|
44
|
-
_key = "#{prefix}#{key}".to_sym
|
45
|
-
self[_key] = val
|
46
|
-
end
|
47
|
-
|
48
|
-
self
|
11
|
+
def << animation
|
12
|
+
@animations << animation
|
49
13
|
end
|
50
14
|
|
51
|
-
def
|
52
|
-
@
|
15
|
+
def to_keyframe_css
|
16
|
+
@animations.collect(&:to_keyframe_css).join("\n")
|
53
17
|
end
|
54
18
|
|
55
|
-
|
56
|
-
|
57
|
-
# Creates a copy of the array of styles with the appropriate
|
58
|
-
# properties (e.g. transform) prefixed.
|
59
|
-
def get_prefixed_styles prefix
|
60
|
-
|
61
|
-
_styles = {}
|
19
|
+
def to_s
|
62
20
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
21
|
+
# Render each of the animations in the collection and join them
|
22
|
+
# in a single, comma-delimited string.
|
23
|
+
@animations.collect(&:to_s).join(', ')
|
67
24
|
|
68
|
-
_styles
|
69
25
|
end
|
70
26
|
|
71
27
|
end
|
@@ -76,7 +32,9 @@ module Inkcite
|
|
76
32
|
# Timing functions
|
77
33
|
LINEAR = 'linear'
|
78
34
|
EASE = 'ease'
|
35
|
+
EASE_IN = 'ease-in'
|
79
36
|
EASE_IN_OUT = 'ease-in-out'
|
37
|
+
EASE_OUT = 'ease-out'
|
80
38
|
|
81
39
|
# Animation name, view context and array of keyframes
|
82
40
|
attr_reader :name, :ctx
|
@@ -106,6 +64,11 @@ module Inkcite
|
|
106
64
|
keyframe
|
107
65
|
end
|
108
66
|
|
67
|
+
# Returns true if this animation is blank - e.g. it has no keyframes.
|
68
|
+
def blank?
|
69
|
+
@keyframes.blank?
|
70
|
+
end
|
71
|
+
|
109
72
|
def to_keyframe_css
|
110
73
|
|
111
74
|
css = ''
|
File without changes
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Inkcite
|
2
|
+
class Animation
|
3
|
+
class Keyframe
|
4
|
+
|
5
|
+
attr_reader :percent, :style
|
6
|
+
|
7
|
+
# Ending percentage the animation stays at this keyframe. For
|
8
|
+
# example, a keyframe that starts at 20% and has a duration
|
9
|
+
# of 19.9% would render as 25%, 39.9% { ... }
|
10
|
+
attr_accessor :duration
|
11
|
+
|
12
|
+
def initialize percent, ctx, styles={}
|
13
|
+
|
14
|
+
# Animation percents are always rounded to the nearest whole number.
|
15
|
+
@percent = percent.round(0)
|
16
|
+
@duration = 0
|
17
|
+
|
18
|
+
# Instantiate a new Style for this percentage.
|
19
|
+
@style = Inkcite::Renderer::Style.new(nil, ctx, styles)
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def [] key
|
24
|
+
@style[key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def []= key, val
|
28
|
+
@style[key] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
# For style chaining - e.g. keyframe.add(:key1, 'val').add(:key)
|
32
|
+
def add key, val
|
33
|
+
@style[key] = val
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Appends a value to an existing key
|
38
|
+
def append key, val
|
39
|
+
|
40
|
+
@style[key] ||= ''
|
41
|
+
@style[key] << ' ' unless @style[key].blank?
|
42
|
+
@style[key] << val
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_with_prefixes key, val, ctx
|
47
|
+
|
48
|
+
ctx.prefixes.each do |prefix|
|
49
|
+
_key = "#{prefix}#{key}".to_sym
|
50
|
+
self[_key] = val
|
51
|
+
end
|
52
|
+
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_css prefix
|
57
|
+
css = "#{@percent}%"
|
58
|
+
css << ", #{@percent + @duration.to_f}%" if @duration > 0
|
59
|
+
css << ' { '
|
60
|
+
css << @style.to_inline_css(prefix)
|
61
|
+
css << ' }'
|
62
|
+
css
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Creates a copy of the array of styles with the appropriate
|
68
|
+
# properties (e.g. transform) prefixed.
|
69
|
+
def get_prefixed_styles prefix
|
70
|
+
|
71
|
+
_styles = {}
|
72
|
+
|
73
|
+
@styles.each_pair do |key, val|
|
74
|
+
key = "#{prefix}#{key}".to_sym if Inkcite::Renderer::Style.needs_prefixing?(key)
|
75
|
+
_styles[key] = val
|
76
|
+
end
|
77
|
+
|
78
|
+
_styles
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/inkcite/minifier.rb
CHANGED
@@ -2,7 +2,7 @@ module Inkcite
|
|
2
2
|
class Minifier
|
3
3
|
|
4
4
|
# Directory of optimized images
|
5
|
-
IMAGE_CACHE =
|
5
|
+
IMAGE_CACHE = 'images-optim'
|
6
6
|
|
7
7
|
# Maximum line length for CSS and HTML - lines exceeding this length cause
|
8
8
|
# problems in certain email clients.
|
@@ -40,14 +40,31 @@ module Inkcite
|
|
40
40
|
# a semicolon or close bracket.
|
41
41
|
if ctx.email? && code.length > MAXIMUM_LINE_LENGTH
|
42
42
|
|
43
|
-
#
|
44
|
-
|
43
|
+
# Last position at which a line break was be inserted at.
|
44
|
+
last_break_at = 0
|
45
45
|
|
46
46
|
# Work through the code injecting line breaks until either no further
|
47
47
|
# breakable characters are found or we've reached the end of the code.
|
48
|
-
while
|
49
|
-
break_at = code.rindex(/[
|
50
|
-
|
48
|
+
while last_break_at < code.length
|
49
|
+
break_at = code.rindex(/[ ,;{}]/, last_break_at + MAXIMUM_LINE_LENGTH)
|
50
|
+
|
51
|
+
# No further characters match (unlikely) or an unbroken string since
|
52
|
+
# the last time a break was injected. Either way, let's get out.
|
53
|
+
break if break_at.nil? || break_at <= last_break_at
|
54
|
+
|
55
|
+
# If we've found a space we can break at, do a direct replacement of the
|
56
|
+
# space with a new line. Otherwise, inject a new line one spot after
|
57
|
+
# the matching character.
|
58
|
+
if code[break_at] == ' '
|
59
|
+
code[break_at] = NEW_LINE
|
60
|
+
|
61
|
+
else
|
62
|
+
break_at += 1
|
63
|
+
code.insert(break_at, NEW_LINE)
|
64
|
+
break_at += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
last_break_at = break_at
|
51
68
|
end
|
52
69
|
|
53
70
|
end
|
@@ -107,75 +124,79 @@ module Inkcite
|
|
107
124
|
|
108
125
|
end
|
109
126
|
|
110
|
-
def self.
|
127
|
+
def self.image email, img_name, force=false
|
111
128
|
|
112
|
-
|
113
|
-
|
129
|
+
# Original, unoptimized source image
|
130
|
+
source_img = File.join(email.image_dir, img_name)
|
114
131
|
|
115
|
-
#
|
116
|
-
|
117
|
-
|
132
|
+
# Cached, optimized path for this image.
|
133
|
+
cache_path = email.project_file(IMAGE_CACHE)
|
134
|
+
cached_img = File.join(cache_path, File.basename(img_name))
|
118
135
|
|
119
|
-
#
|
120
|
-
|
121
|
-
if File.exist?(cache_path)
|
136
|
+
# Full path to the local project's kraken config if it exists
|
137
|
+
kraken_config_path = email.project_file(KRAKEN_CONFIG_YML)
|
122
138
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
139
|
+
# This is the array of config files that will be searched to
|
140
|
+
# determine which algorithm to use to compress the images.
|
141
|
+
config_paths = [
|
142
|
+
kraken_config_path,
|
143
|
+
email.project_file(IMAGE_OPTIM_CONFIG_YML),
|
144
|
+
File.join(Inkcite.asset_path, 'init', IMAGE_OPTIM_CONFIG_YML)
|
145
|
+
]
|
127
146
|
|
128
|
-
|
129
|
-
|
130
|
-
removed_images = removed_images.collect { |img| File.join(cache_path, img) }
|
131
|
-
FileUtils.rm(removed_images)
|
147
|
+
# Grab the first file that exists for this project.
|
148
|
+
config_path = config_paths.detect { |p| File.exist?(p) }
|
132
149
|
|
133
|
-
|
150
|
+
unless force
|
134
151
|
|
135
|
-
|
152
|
+
# Get the last-modified date of the image optimization config
|
153
|
+
# file - if that file is newer than the image, re-optimization
|
154
|
+
# is necessary because the settings have changed.
|
155
|
+
config_last_modified = Util.last_modified(config_path)
|
136
156
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
updated_images = Dir.glob(File.join(images_path, '*.*')).select do |img|
|
141
|
-
cached_img = File.join(cache_path, File.basename(img))
|
157
|
+
# Get the last-modified date of the actual image. If the source
|
158
|
+
# image is newer than the cached version, we'll need to run it
|
159
|
+
# through optimization again, too.
|
142
160
|
cache_last_modified = Util.last_modified(cached_img)
|
143
|
-
|
144
|
-
|
161
|
+
source_last_modified = Util.last_modified(source_img)
|
162
|
+
|
163
|
+
# Nothing to do unless the image in the cache is older than the
|
164
|
+
# source or the config file.
|
165
|
+
return unless config_last_modified > cache_last_modified || source_last_modified > cache_last_modified
|
145
166
|
|
146
|
-
|
147
|
-
return if updated_images.blank?
|
167
|
+
end
|
148
168
|
|
169
|
+
# Make sure the image cache directory exists
|
149
170
|
FileUtils.mkpath(cache_path)
|
150
171
|
|
151
|
-
#
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
172
|
+
# Read the image compression configuration settings
|
173
|
+
config = Util::read_yml(config_path, :fail_if_not_exists => false)
|
174
|
+
|
175
|
+
if config_path == kraken_config_path
|
176
|
+
minify_with_kraken_io email, config, source_img, cached_img
|
177
|
+
|
157
178
|
else
|
158
|
-
{
|
159
|
-
:allow_lossy => true,
|
160
|
-
:gifsicle => { :level => 3 },
|
161
|
-
:jpegoptim => { :max_quality => 50 },
|
162
|
-
:jpegrecompress => { :quality => 1 },
|
163
|
-
:pngout => false,
|
164
|
-
:svgo => false
|
165
|
-
}
|
166
|
-
end
|
167
179
|
|
168
|
-
|
180
|
+
# Default image optimization uses built-in ImageOptim
|
181
|
+
minify_with_image_optim email, config, source_img, cached_img
|
169
182
|
|
170
|
-
# Copy all of the images that need updating into the temporary directory.
|
171
|
-
# Specifically joining the images_path to the image to avoid Email's
|
172
|
-
# image_path which may change it's directory if optimization is enabled.
|
173
|
-
updated_images.each do |img|
|
174
|
-
cached_img = File.join(cache_path, File.basename(img))
|
175
|
-
FileUtils.cp(img, cached_img)
|
176
|
-
image_optim.optimize_image!(cached_img)
|
177
183
|
end
|
178
184
|
|
185
|
+
original_size = File.size(source_img)
|
186
|
+
compressed_size = File.size(cached_img)
|
187
|
+
percent_compressed = ((1.0 - (compressed_size / original_size.to_f)) * 100).round(1)
|
188
|
+
puts "Compressed #{img_name} #{percent_compressed}%"
|
189
|
+
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.images email, force=false
|
193
|
+
|
194
|
+
images_path = email.image_dir
|
195
|
+
|
196
|
+
# Iterate through all of the images in the project and optimize them
|
197
|
+
# if necessary.
|
198
|
+
Dir.glob(File.join(images_path, '*.*')).each { |img| self.image(email, File.basename(img), force) }
|
199
|
+
|
179
200
|
end
|
180
201
|
|
181
202
|
def self.js code, ctx
|
@@ -188,11 +209,74 @@ module Inkcite
|
|
188
209
|
|
189
210
|
private
|
190
211
|
|
212
|
+
def self.minify_with_image_optim email, config, source_img, cached_img
|
213
|
+
|
214
|
+
# Copy the image into the destination directory and then use Image Optim
|
215
|
+
# to optimize it in place.
|
216
|
+
FileUtils.cp(source_img, cached_img)
|
217
|
+
ImageOptim.new(config).optimize_image!(cached_img)
|
218
|
+
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.minify_with_kraken_io email, config, source_img, cached_img
|
222
|
+
|
223
|
+
require 'kraken-io'
|
224
|
+
require 'open-uri'
|
225
|
+
|
226
|
+
# Initialize the Kraken API using the API key and secret defined in the
|
227
|
+
# config.yml file.
|
228
|
+
kraken = Kraken::API.new(
|
229
|
+
:api_key => config[:api_key],
|
230
|
+
:api_secret => config[:api_secret]
|
231
|
+
)
|
232
|
+
|
233
|
+
# As you might expect, Outlook doesn't support webp so it needs to be
|
234
|
+
# disabled by default. Otherwise, Kraken always compresses with webp.
|
235
|
+
kraken_opts = { :webp => false }
|
236
|
+
|
237
|
+
# Get the file format (e.g. gif) of the file being optimized.
|
238
|
+
source_fmt = File.extname(source_img).delete('.')
|
239
|
+
|
240
|
+
# True if the configuration file does not specifically exclude
|
241
|
+
# this format from being processed.
|
242
|
+
compress_this_fmt = config[source_fmt.to_sym] != false
|
243
|
+
|
244
|
+
# Typically, we're going to want lossy compression to minify the file
|
245
|
+
# but if the user has put lossy: false specifically in their config
|
246
|
+
# file, we'll disable that feature in Kraken too. Defaults to true.
|
247
|
+
kraken_opts[:lossy] = compress_this_fmt
|
248
|
+
|
249
|
+
# Send the quality metric to Kraken only if specified. Per their
|
250
|
+
# documentation, Kraken will attempt to guess the best quality to
|
251
|
+
# use but in my experience it errs on the side of higher quality
|
252
|
+
# whereas setting a quality factor around 50 produces a good
|
253
|
+
# balance of image detail and file size.
|
254
|
+
if compress_this_fmt
|
255
|
+
quality = config[:quality].to_i
|
256
|
+
kraken_opts[:quality] = quality if quality > 0 and quality <= 100
|
257
|
+
end
|
258
|
+
|
259
|
+
# Upload the image to Kraken which blocks by default until the image
|
260
|
+
# has been optimized.
|
261
|
+
data = kraken.upload(source_img, kraken_opts)
|
262
|
+
if data.success
|
263
|
+
File.write(cached_img, open(data.kraked_url).read, { :mode => 'wb' })
|
264
|
+
else
|
265
|
+
puts "Failed to optimize #{img_name}: #{data.message}"
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
|
191
270
|
# Name of the Image Optim configuration yml file that can be
|
192
271
|
# put in the project directory to explicitly control the image
|
193
272
|
# optimization process.
|
194
273
|
IMAGE_OPTIM_CONFIG_YML = 'image_optim.yml'
|
195
274
|
|
275
|
+
# Name of the Kraken configuration yml that, when present in
|
276
|
+
# the project directory and populated with an API key and secret
|
277
|
+
# causes Kraken.io paid image optimization service to be used.
|
278
|
+
KRAKEN_CONFIG_YML = 'kraken.yml'
|
279
|
+
|
196
280
|
NEW_LINE = "\n"
|
197
281
|
|
198
282
|
# Used to match inline styles that will be compressed when minifying
|