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
@@ -72,9 +72,15 @@ module Inkcite
|
|
72
72
|
width = opt[:width]
|
73
73
|
element[:width] = width unless width.blank?
|
74
74
|
|
75
|
+
mobile_width = opt[MOBILE_WIDTH]
|
76
|
+
element.mobile_style[:width] = px(mobile_width) unless none?(mobile_width)
|
77
|
+
|
75
78
|
height = opt[:height].to_i
|
76
79
|
element[:height] = height if height > 0
|
77
80
|
|
81
|
+
mobile_height = opt[MOBILE_HEIGHT]
|
82
|
+
element.mobile_style[:height] = px(mobile_height) unless none?(mobile_height)
|
83
|
+
|
78
84
|
end
|
79
85
|
|
80
86
|
private
|
data/lib/inkcite/renderer/td.rb
CHANGED
@@ -75,8 +75,10 @@ module Inkcite
|
|
75
75
|
# If the table defines mobile-padding, then apply the correct mobile
|
76
76
|
# style to this td - and its !important if there is padding on
|
77
77
|
# the td already.
|
78
|
-
|
79
|
-
|
78
|
+
unless mobile == HIDE
|
79
|
+
mix_mobile_padding td, table_opt, ctx
|
80
|
+
mix_mobile_padding td, opt, ctx
|
81
|
+
end
|
80
82
|
|
81
83
|
# Need to handle Fluid-Drop HTML injection here before the rest of the
|
82
84
|
# TD is formalized. Fluid-Drop removes the width attribute of the cell
|
@@ -147,6 +149,9 @@ module Inkcite
|
|
147
149
|
|
148
150
|
end
|
149
151
|
|
152
|
+
# Support custom alignment on mobile devices
|
153
|
+
mix_mobile_text_align td, opt, ctx
|
154
|
+
|
150
155
|
rowspan = opt[:rowspan].to_i
|
151
156
|
td[:rowspan] = rowspan if rowspan > 0
|
152
157
|
|
@@ -24,6 +24,11 @@ module Inkcite
|
|
24
24
|
# image name is frozen to ensure it isn't modified later.
|
25
25
|
src = opt[:src].freeze
|
26
26
|
|
27
|
+
# This will hold the names of each of the frames, sans any full
|
28
|
+
# URL qualification (e.g. images/) These are interpolated to
|
29
|
+
# include the index
|
30
|
+
frame_srcs = []
|
31
|
+
|
27
32
|
# This will hold all frame source file names interpolated to include
|
28
33
|
# index (e.g. %1 being replaced with the frame number, if present).
|
29
34
|
frames = []
|
@@ -40,6 +45,7 @@ module Inkcite
|
|
40
45
|
# this loop also verifies that the referenced image exists.
|
41
46
|
frame_count.times do |n|
|
42
47
|
frame_src = src.gsub('%1', "#{n + 1}")
|
48
|
+
frame_srcs << frame_src
|
43
49
|
frames << image_url(frame_src, opt, ctx, false, false)
|
44
50
|
end
|
45
51
|
|
@@ -91,7 +97,7 @@ module Inkcite
|
|
91
97
|
html << Element.new('a', { :id => id, :href => quote(href), :class => hover_klass, :bgcolor => bgcolor, :bggradient => gradient, :block => true }).to_helper
|
92
98
|
|
93
99
|
table = Element.new('table', {
|
94
|
-
:width => '100%', :background =>
|
100
|
+
:width => '100%', :background => frame_srcs[0], BACKGROUND_SIZE => 'cover',
|
95
101
|
Table::TR_TRANSITION => %q("all .5s cubic-bezier(0.075, 0.82, 0.165, 1)")
|
96
102
|
})
|
97
103
|
|
data/lib/inkcite/util.rb
CHANGED
@@ -52,6 +52,62 @@ module Inkcite
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
# Conversion of HSL to RGB color courtesy of
|
56
|
+
# http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
57
|
+
def self.hsl_to_color h, s, l
|
58
|
+
|
59
|
+
# The algorithm expects h, s and l to be values between 0-1.
|
60
|
+
h = h / 360.0
|
61
|
+
s = s / 100.0
|
62
|
+
l = l / 100.0
|
63
|
+
|
64
|
+
# Wrap the color wheel if the hue provided is less than or
|
65
|
+
# greater than 1
|
66
|
+
h += 1.0 while h < 0
|
67
|
+
h -= 1.0 while h > 1
|
68
|
+
|
69
|
+
s = 0.0 if s < 0
|
70
|
+
s = 1.0 if s > 1
|
71
|
+
|
72
|
+
l = 0.0 if l < 0
|
73
|
+
l = 1.0 if l > 1
|
74
|
+
|
75
|
+
r = g = b = 0
|
76
|
+
|
77
|
+
if s == 0
|
78
|
+
r = g = b = l
|
79
|
+
|
80
|
+
else
|
81
|
+
q = l < 0.5 ? l * (1 + s) : l + s - l * s
|
82
|
+
p = 2 * l - q
|
83
|
+
r = hue_to_rgb(p, q, h + 1/3.0)
|
84
|
+
g = hue_to_rgb(p, q, h)
|
85
|
+
b = hue_to_rgb(p, q, h - 1/3.0)
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
r = (r * 255).round(0)
|
90
|
+
g = (g * 255).round(0)
|
91
|
+
b = (b * 255).round(0)
|
92
|
+
|
93
|
+
"##{rgb_to_hex(r)}#{rgb_to_hex(g)}#{rgb_to_hex(b)}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.hue_to_rgb p, q, t
|
97
|
+
t += 1 if t < 0
|
98
|
+
t -= 1 if t > 1
|
99
|
+
return (p + (q - p) * 6.0 * t) if (t < 1.0/6.0)
|
100
|
+
return q if (t < 0.5)
|
101
|
+
return (p + (q - p) * (2/3.0 - t) * 6) if (t < 2/3.0)
|
102
|
+
p
|
103
|
+
end
|
104
|
+
|
105
|
+
# RGB to hex courtesy of
|
106
|
+
# http://blog.lebrijo.com/converting-rgb-colors-to-hexadecimal-with-ruby/
|
107
|
+
def self.rgb_to_hex val
|
108
|
+
val.to_s(16).rjust(2, '0').downcase
|
109
|
+
end
|
110
|
+
|
55
111
|
def self.lighten color, amount=0.6
|
56
112
|
return WHITE if color.nil?
|
57
113
|
rgb = color.gsub('#', '').scan(/../).map { |c| c.hex }
|
data/lib/inkcite/version.rb
CHANGED
data/lib/inkcite/view.rb
CHANGED
@@ -86,6 +86,7 @@ module Inkcite
|
|
86
86
|
|
87
87
|
# Initializing to prevent a ruby verbose warning.
|
88
88
|
@footnotes = nil
|
89
|
+
@substitutions = nil
|
89
90
|
|
90
91
|
end
|
91
92
|
|
@@ -338,7 +339,15 @@ module Inkcite
|
|
338
339
|
# Returns the array of browser prefixes that need to be included in
|
339
340
|
# CSS styles based on which version of the email this is.
|
340
341
|
def prefixes
|
341
|
-
[ ''
|
342
|
+
_prefixes = [ '' ]
|
343
|
+
|
344
|
+
# In development mode, include all prefixes for maximum compatibility.
|
345
|
+
_prefixes += %w(-moz- -ms- -o-) if development?
|
346
|
+
|
347
|
+
# Always include webkit for targeting mobile devices.
|
348
|
+
_prefixes << '-webkit-'
|
349
|
+
|
350
|
+
_prefixes
|
342
351
|
end
|
343
352
|
|
344
353
|
def preview?
|
@@ -376,6 +385,9 @@ module Inkcite
|
|
376
385
|
# <!-- commented out --> to ensure the email is as small as possible.
|
377
386
|
source = Minifier.remove_comments(source, self)
|
378
387
|
|
388
|
+
# Perform global substitution of characters - e.g. ’ into ' if that's
|
389
|
+
substitution_map.each_pair { |orig, replace| source.gsub!(orig, replace) }
|
390
|
+
|
379
391
|
# Protect against unsupported characters
|
380
392
|
source = Renderer.fix_illegal_characters(source, self)
|
381
393
|
|
@@ -509,6 +521,32 @@ module Inkcite
|
|
509
521
|
@subject ||= Renderer.render((self[:subject] || self[:title] || UNTITLED_EMAIL), self)
|
510
522
|
end
|
511
523
|
|
524
|
+
# Returns a map where the strings are an original string
|
525
|
+
# and the value is what should replace it - used for
|
526
|
+
# automating common replacements - e.g. trademark symbols
|
527
|
+
# or apostrophes going in the wrong direction
|
528
|
+
def substitution_map
|
529
|
+
|
530
|
+
if @substitutions.nil?
|
531
|
+
|
532
|
+
# Initialize the substitutions map which will be populated
|
533
|
+
# if the local file exists.
|
534
|
+
@substitutions = {}
|
535
|
+
|
536
|
+
# Preload the array of footnotes if they exist
|
537
|
+
substitutions_tsv_file = @email.project_file(SUBSTITUTIONS_TSV_FILE)
|
538
|
+
if File.exist?(substitutions_tsv_file)
|
539
|
+
CSV.foreach(substitutions_tsv_file, { :col_sep => "\t" }) do |fn|
|
540
|
+
original = fn[0]
|
541
|
+
@substitutions[original] = fn[1].to_s unless original.blank?
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
end
|
546
|
+
|
547
|
+
@substitutions
|
548
|
+
end
|
549
|
+
|
512
550
|
def tag_stack tag
|
513
551
|
@tag_stack ||= Hash.new()
|
514
552
|
@tag_stack[tag] ||= TagStack.new(tag, self)
|
@@ -623,6 +661,9 @@ module Inkcite
|
|
623
661
|
# Tab-separated file containing footnote declarations.
|
624
662
|
FOOTNOTES_TSV_FILE = 'footnotes.tsv'
|
625
663
|
|
664
|
+
# Tab-separated file containing global substitution values.
|
665
|
+
SUBSTITUTIONS_TSV_FILE = 'substitutions.tsv'
|
666
|
+
|
626
667
|
def assert_in_browser msg
|
627
668
|
raise msg if email? && !development?
|
628
669
|
end
|
data/test/animation_spec.rb
CHANGED
@@ -1,17 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
1
|
describe Inkcite::Animation do
|
4
2
|
|
5
3
|
before do
|
6
4
|
@view = Inkcite::Email.new('test/project/').view(:development, :email)
|
5
|
+
@production_view = Inkcite::Email.new('test/project/').view(:production, :email)
|
7
6
|
end
|
8
7
|
|
9
8
|
it 'supports browser prefixing' do
|
10
|
-
Inkcite::Renderer::Style.new(nil, @view, { :animation => 'video-frames 5s ease infinite' }).to_s.must_equal('-webkit-animation:video-frames 5s ease infinite;animation:video-frames 5s ease infinite')
|
9
|
+
Inkcite::Renderer::Style.new(nil, @view, { :animation => 'video-frames 5s ease infinite' }).to_s.must_equal('-moz-animation:video-frames 5s ease infinite;-ms-animation:video-frames 5s ease infinite;-o-animation:video-frames 5s ease infinite;-webkit-animation:video-frames 5s ease infinite;animation:video-frames 5s ease infinite')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'supports limited browser prefixing in production' do
|
13
|
+
Inkcite::Renderer::Style.new(nil, @production_view, { :animation => 'video-frames 5s ease infinite' }).to_s.must_equal('-webkit-animation:video-frames 5s ease infinite;animation:video-frames 5s ease infinite')
|
11
14
|
end
|
12
15
|
|
13
16
|
it 'supports browser prefixing specific items only' do
|
14
|
-
Inkcite::Renderer::Style.new(nil, @view, { :left => '25%', :animation => 'video-frames 5s ease infinite' }).to_s.must_equal('-webkit-animation:video-frames 5s ease infinite;animation:video-frames 5s ease infinite;left:25%')
|
17
|
+
Inkcite::Renderer::Style.new(nil, @view, { :left => '25%', :animation => 'video-frames 5s ease infinite' }).to_s.must_equal('-moz-animation:video-frames 5s ease infinite;-ms-animation:video-frames 5s ease infinite;-o-animation:video-frames 5s ease infinite;-webkit-animation:video-frames 5s ease infinite;animation:video-frames 5s ease infinite;left:25%')
|
15
18
|
end
|
16
19
|
|
17
20
|
it 'can instantiate an animation keyframe' do
|
@@ -35,8 +38,38 @@ describe Inkcite::Animation do
|
|
35
38
|
anim = Inkcite::Animation.new('snowflake7', @view)
|
36
39
|
anim.add_keyframe 5, { :top => '-10px', :left => '22%', :transform => 'rotate(14deg)' }
|
37
40
|
anim.add_keyframe 25, { :top => '100%', :left => '18%' }
|
38
|
-
anim.to_keyframe_css.must_equal(%Q(@keyframes snowflake7 {\n5% { left:22%;top:-10px;transform:rotate(14deg) }\n25% { left:18%;top:100% }\n}\n@-webkit-keyframes snowflake7 {\n5% { -webkit-transform:rotate(14deg);left:22%;top:-10px }\n25% { left:18%;top:100% }\n}\n))
|
41
|
+
anim.to_keyframe_css.must_equal(%Q(@keyframes snowflake7 {\n5% { left:22%;top:-10px;transform:rotate(14deg) }\n25% { left:18%;top:100% }\n}\n@-moz-keyframes snowflake7 {\n5% { -moz-transform:rotate(14deg);left:22%;top:-10px }\n25% { left:18%;top:100% }\n}\n@-ms-keyframes snowflake7 {\n5% { -ms-transform:rotate(14deg);left:22%;top:-10px }\n25% { left:18%;top:100% }\n}\n@-o-keyframes snowflake7 {\n5% { -o-transform:rotate(14deg);left:22%;top:-10px }\n25% { left:18%;top:100% }\n}\n@-webkit-keyframes snowflake7 {\n5% { -webkit-transform:rotate(14deg);left:22%;top:-10px }\n25% { left:18%;top:100% }\n}\n))
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'supports keyframe duration' do
|
45
|
+
keyframe = Inkcite::Animation::Keyframe.new(25, @view)
|
46
|
+
keyframe.duration = 15.9
|
47
|
+
keyframe[:top] = '-15%'
|
48
|
+
keyframe.to_css('').must_equal('25%, 40.9% { top:-15% }')
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'reports when an animation is blank' do
|
52
|
+
anim = Inkcite::Animation.new('snowflake7', @view)
|
53
|
+
anim.blank?.must_equal(true)
|
54
|
+
anim.add_keyframe 25, { :top => '100%', :left => '18%' }
|
55
|
+
anim.blank?.must_equal(false)
|
39
56
|
end
|
40
57
|
|
58
|
+
it 'supports composite animations' do
|
59
|
+
|
60
|
+
comp_anim = Inkcite::Animation::Composite.new()
|
61
|
+
|
62
|
+
explosion = Inkcite::Animation.new('explosion', @view)
|
63
|
+
explosion.duration = 8
|
64
|
+
explosion.timing_function = Inkcite::Animation::EASE_OUT
|
65
|
+
comp_anim << explosion
|
66
|
+
|
67
|
+
gravity = Inkcite::Animation.new('gravity', @view)
|
68
|
+
gravity.iteration_count = 4
|
69
|
+
comp_anim << gravity
|
70
|
+
|
71
|
+
comp_anim.to_s.must_equal(%q(8s ease-out infinite explosion, 1s linear 4 gravity))
|
72
|
+
|
73
|
+
end
|
41
74
|
|
42
75
|
end
|
data/test/renderer/div_spec.rb
CHANGED
@@ -117,4 +117,34 @@ describe Inkcite::Renderer::Div do
|
|
117
117
|
@view.media_query.find_by_klass('m1').declarations.must_match('width:78px !important')
|
118
118
|
end
|
119
119
|
|
120
|
+
it 'supports mobile padding' do
|
121
|
+
Inkcite::Renderer.render('{div mobile-padding="15px 10px"}{/div}', @view).must_equal('<div class="m1"></div>')
|
122
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('padding:15px 10px')
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'supports directional mobile padding' do
|
126
|
+
Inkcite::Renderer.render('{div mobile-padding-top=15}{/div}', @view).must_equal('<div class="m1"></div>')
|
127
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('padding-top:15px')
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'supports directional mobile padding override' do
|
131
|
+
Inkcite::Renderer.render('{div mobile-padding=30px mobile-padding-top=15}{/div}', @view).must_equal('<div class="m1"></div>')
|
132
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('padding:30px;padding-top:15px')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'supports mobile text alignment' do
|
136
|
+
Inkcite::Renderer.render('{div mobile-text-align="center"}{/div}', @view).must_equal('<div class="m1"></div>')
|
137
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('text-align:center')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'supports mobile text alignment override' do
|
141
|
+
Inkcite::Renderer.render('{div text-align="right" mobile-text-align="center"}{/div}', @view).must_equal('<div class="m1" style="text-align:right"></div>')
|
142
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('text-align:center !important')
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'supports mobile display override' do
|
146
|
+
Inkcite::Renderer.render('{div mobile-display="block"}{/div}', @view).must_equal('<div class="m1"></div>')
|
147
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('display:block')
|
148
|
+
end
|
149
|
+
|
120
150
|
end
|
data/test/renderer/td_spec.rb
CHANGED
@@ -17,6 +17,16 @@ describe Inkcite::Renderer::Td do
|
|
17
17
|
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { padding:15px }')
|
18
18
|
end
|
19
19
|
|
20
|
+
it 'supports directional mobile padding' do
|
21
|
+
Inkcite::Renderer.render('{td mobile-padding-top=15}', @view).must_equal('<td class="m1">')
|
22
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { padding-top:15px }')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'supports mobile text alignment' do
|
26
|
+
Inkcite::Renderer.render('{td mobile-text-align="center"}{/td}', @view).must_equal('<td class="m1"></td>')
|
27
|
+
@view.media_query.find_by_klass('m1').declarations.must_match('text-align:center')
|
28
|
+
end
|
29
|
+
|
20
30
|
it 'reuses a style that matches mobile padding' do
|
21
31
|
Inkcite::Renderer.render('{table mobile-padding=15}{td}{table mobile-padding=15}{td}', @view).must_equal('<table border=0 cellpadding=0 cellspacing=0><tr><td class="m1"><table border=0 cellpadding=0 cellspacing=0><tr><td class="m1">')
|
22
32
|
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { padding:15px }')
|
@@ -148,4 +158,19 @@ describe Inkcite::Renderer::Td do
|
|
148
158
|
Inkcite::Renderer.render('{td nowrap}', @view).must_equal('<td nowrap>')
|
149
159
|
end
|
150
160
|
|
161
|
+
it 'supports mobile width override' do
|
162
|
+
Inkcite::Renderer.render('{td width=30 mobile-width=15}', @view).must_equal('<td class="m1" width=30>')
|
163
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { width:15px }')
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'supports mobile height override' do
|
167
|
+
Inkcite::Renderer.render('{td height=30 mobile-height=15}', @view).must_equal('<td class="m1" height=30>')
|
168
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { height:15px }')
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'supports multiple mobile override attributes' do
|
172
|
+
Inkcite::Renderer.render('{td width=15 border-left="1px dotted #cccccc" mobile-display="block" mobile-width="100%" mobile-border-left="none" mobile-border-top="1px dotted #ccc"}', @view).must_equal('<td class="m1" style="border-left:1px dotted #cccccc" width=15>')
|
173
|
+
@view.media_query.find_by_klass('m1').to_css.must_equal('td.m1 { border-left:none !important;border-top:1px dotted #ccc;display:block;width:100% }')
|
174
|
+
end
|
175
|
+
|
151
176
|
end
|
@@ -6,7 +6,7 @@ describe Inkcite::Renderer::VideoPreview do
|
|
6
6
|
|
7
7
|
it 'renders an animated video preview' do
|
8
8
|
Inkcite::Renderer.render('{video-preview id="ripcurl" href="https://www.campaignmonitor.com/customers/ripcurl" src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame%1.jpg" width=600 height=337 frames=3 bgcolor="#5b5f66" gradient="#1d1f21"}', @view).must_equal(%Q(<!--[if !mso]><!-- -->\n<a class="video" href="https://www.campaignmonitor.com/customers/ripcurl" style="background-color:#5b5f66;background-image:radial-gradient(circle at center, #5b5f66, #1d1f21);color:#0099cc;display:block;text-decoration:none" target=_blank>\n<table border=0 cellpadding=0 cellspacing=0 style="-webkit-animation:15s ease infinite video1-frames;animation:15s ease infinite video1-frames;background:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) 0% 0% / cover no-repeat" width=100%><tr style="transition:all .5s cubic-bezier(0.075, 0.82, 0.165, 1)">\n<td width=25%><img alt="" border=0 src=images/vp-150x337.png style="display:block;height:auto;opacity:0;visibility:hidden" width=100%></td>\n<td align=center valign=middle width=50%>\n<div class="play-button" style="background-image: linear-gradient(rgba(0,0,0,0.5), rgba(0,0,0,0.1)); border: 4px solid white; border-radius: 50%; box-shadow: 0 1px 2px rgba(0,0,0,0.3), inset 0 1px 2px rgba(0,0,0,0.3); height: 34px; margin: 0 auto; padding: 18px 16px 18px 24px; transition: transform .5s cubic-bezier(0.075, 0.82, 0.165, 1); width: 34px;">\n<div style="border-color: transparent transparent transparent white; border-style: solid; border-width: 17px 0 17px 30px; display: block; font-size: 0; height: 0; Margin: 0 auto; width: 0;"> </div>\n</div>\n</td>\n<td width=25%> </td>\n</tr></table>\n</a>\n<div style="background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg),url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg),url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg);display:none"></div>\n<!--<![endif]-->\n<!--[if mso]>\n<v:group xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" coordsize="600,337" coordorigin="0,0" href="https://www.campaignmonitor.com/customers/ripcurl" style="width:600px;height:337px;">\n<v:rect fill="t" stroked="f" style="position:absolute;width:600;height:337;"><v:fill src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg" type="frame"/></v:rect>\n<v:oval fill="t" strokecolor="white" strokeweight="4px" style="position:absolute;left:261;top:129;width:78;height:78"><v:fill color="black" opacity="30%"/></v:oval>\n<v:shape coordsize="24,32" path="m,l,32,24,16,xe" fillcolor="white" stroked="f" style="position:absolute;left:285;top:153;width:30;height:30;"/>\n</v:group>\n<![endif]-->))
|
9
|
-
@view.styles.first.must_equal(%Q(.video:hover .play-button {\n transform: scale(1.1);\n}\n.video:hover tr {\n background-color: rgba(255, 255, 255, .2);\n}\n@keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-webkit-keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n))
|
9
|
+
@view.styles.first.must_equal(%Q(.video:hover .play-button {\n transform: scale(1.1);\n}\n.video:hover tr {\n background-color: rgba(255, 255, 255, .2);\n}\n@keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-moz-keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-ms-keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-o-keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-webkit-keyframes video1-frames {\n0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n))
|
10
10
|
|
11
11
|
# Verify that the transparent spacer image was created and then
|
12
12
|
# clean it up.
|
data/test/util_spec.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
describe Inkcite::Util do
|
2
|
+
|
3
|
+
it 'can convert an HSL color to a hex color' do
|
4
|
+
Inkcite::Util.hsl_to_color(78, 78, 78).must_equal('#d8f39b')
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'can convert an HSL color to a hex color' do
|
8
|
+
Inkcite::Util.hsl_to_color(128, 100, 50).must_equal('#00ff22')
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inkcite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeffrey D. Hoffman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-03-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: kraken-io
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: listen
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -361,7 +375,6 @@ files:
|
|
361
375
|
- bin/inkcite
|
362
376
|
- inkcite.gemspec
|
363
377
|
- lib/inkcite.rb
|
364
|
-
- lib/inkcite/animation.rb
|
365
378
|
- lib/inkcite/cli/base.rb
|
366
379
|
- lib/inkcite/cli/build.rb
|
367
380
|
- lib/inkcite/cli/init.rb
|
@@ -371,6 +384,11 @@ files:
|
|
371
384
|
- lib/inkcite/cli/test.rb
|
372
385
|
- lib/inkcite/cli/validate.rb
|
373
386
|
- lib/inkcite/email.rb
|
387
|
+
- lib/inkcite/facade.rb
|
388
|
+
- lib/inkcite/facade/animation.rb
|
389
|
+
- lib/inkcite/facade/element.rb
|
390
|
+
- lib/inkcite/facade/keyframe.rb
|
391
|
+
- lib/inkcite/facade/style.rb
|
374
392
|
- lib/inkcite/mailer.rb
|
375
393
|
- lib/inkcite/minifier.rb
|
376
394
|
- lib/inkcite/parser.rb
|
@@ -380,7 +398,7 @@ files:
|
|
380
398
|
- lib/inkcite/renderer/button.rb
|
381
399
|
- lib/inkcite/renderer/container_base.rb
|
382
400
|
- lib/inkcite/renderer/div.rb
|
383
|
-
- lib/inkcite/renderer/
|
401
|
+
- lib/inkcite/renderer/fireworks.rb
|
384
402
|
- lib/inkcite/renderer/footnote.rb
|
385
403
|
- lib/inkcite/renderer/google_analytics.rb
|
386
404
|
- lib/inkcite/renderer/image.rb
|
@@ -405,7 +423,6 @@ files:
|
|
405
423
|
- lib/inkcite/renderer/span.rb
|
406
424
|
- lib/inkcite/renderer/sparkle.rb
|
407
425
|
- lib/inkcite/renderer/special_effect.rb
|
408
|
-
- lib/inkcite/renderer/style.rb
|
409
426
|
- lib/inkcite/renderer/table.rb
|
410
427
|
- lib/inkcite/renderer/table_base.rb
|
411
428
|
- lib/inkcite/renderer/td.rb
|
@@ -445,6 +462,7 @@ files:
|
|
445
462
|
- test/renderer/video_preview_spec.rb
|
446
463
|
- test/renderer_spec.rb
|
447
464
|
- test/test_helper.rb
|
465
|
+
- test/util_spec.rb
|
448
466
|
- test/view_spec.rb
|
449
467
|
homepage: https://github.com/inkceptional/inkcite
|
450
468
|
licenses:
|
@@ -499,4 +517,5 @@ test_files:
|
|
499
517
|
- test/renderer/video_preview_spec.rb
|
500
518
|
- test/renderer_spec.rb
|
501
519
|
- test/test_helper.rb
|
520
|
+
- test/util_spec.rb
|
502
521
|
- test/view_spec.rb
|