inkcite 1.12.0 → 1.12.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c14c10f673644c60a3e92d4c511858d562c16f58
4
- data.tar.gz: 0690d93ebed3be3f26735c9e6ecdd70301936a43
3
+ metadata.gz: 729a1ea12dcb8c0180541d0b4aebb22346985f2f
4
+ data.tar.gz: c10280cade618463c9ecebe81e50b1acca46a49e
5
5
  SHA512:
6
- metadata.gz: 2553f347fb054d2cf5ab68be3696a1223eabda24b7f1196d09871c3068c6cab2ccdda81214b8890720f8bf9463bd560d9093bee9f4db9d9c2e1aeaae1f240f30
7
- data.tar.gz: 62224686033d05c4587995d4c92268287fac082a16013f54d71d9f185d5fb98ca7afbc54d14b9576170c24d4792069cfa0478841448cab82ec29e77296f9bb37
6
+ metadata.gz: 39f5ebde420c845be3b262d7b4266a491bed516f561a5679616bd4d235693adbba3c6990bdc9b51c61facb00dcfd35b418c2c2f7f3602f13f7daeb7de971bdf6
7
+ data.tar.gz: 2f8441f0f65e4faf1323229c30a8fbdd2ea4b31fc8cdc61f4a1520921bc23fb3cb6727e76cd898b104227f047a924d1af7150180dd8e152fa6e36d14c5ae586a
@@ -0,0 +1,9 @@
1
+ // Conditional helpers for inclusion or exclusion.
2
+ if <!--[if $test$]> <![endif]-->
3
+ if-not <!--[if !$test$]><!-- --> <!--<![endif]-->
4
+
5
+ // Predeclaring common inclusion and exclusion targets
6
+ not-outlook {if-not test=mso} {/if-not}
7
+ not-vml {if-not test=vml} {/if-not}
8
+ outlook-only {if test=mso} {/if}
9
+ vml-only {if test=vml} {/if}
@@ -91,7 +91,7 @@ module Inkcite
91
91
  end
92
92
 
93
93
  def if_mso html
94
- %Q(<!--[if mso]>#{html.to_s}<![endif]-->)
94
+ %Q({outlook-only}#{html.to_s}{/outlook-only})
95
95
  end
96
96
 
97
97
  def none? val
@@ -84,10 +84,8 @@ module Inkcite
84
84
  img[:src] = image_url(outlook_src, opt, ctx)
85
85
 
86
86
  # Wrap the image in the outlook-specific conditionals.
87
- html << '<!--[if mso]>'
88
- html << img.to_s
89
- html << '<![endif]-->'
90
- html << '<!--[if !mso]><!-- -->'
87
+ html << if_mso(img)
88
+ html << '{not-outlook}'
91
89
 
92
90
  end
93
91
 
@@ -137,7 +135,7 @@ module Inkcite
137
135
  html << img.to_s
138
136
 
139
137
  # Conclude the outlook-specific conditional if opened.
140
- html << '<!--<![endif]-->' unless outlook_src.blank?
138
+ html << '{/not-outlook}' unless outlook_src.blank?
141
139
 
142
140
  html
143
141
  end
@@ -148,10 +146,6 @@ module Inkcite
148
146
  # be copied to the title field.
149
147
  COPY_ALT_TO_TITLE = :'copy-alt-to-title'
150
148
 
151
- # Name of the property that allows an outlook-specific src to be specified
152
- # for an image.
153
- OUTLOOK_SRC = :'outlook-src'
154
-
155
149
  end
156
150
  end
157
151
  end
@@ -4,6 +4,10 @@ module Inkcite
4
4
 
5
5
  protected
6
6
 
7
+ # Name of the property that allows an outlook-specific src to be specified
8
+ # for an image.
9
+ OUTLOOK_SRC = :'outlook-src'
10
+
7
11
  # Display mode constants
8
12
  BLOCK = 'block'
9
13
  DEFAULT = 'default'
@@ -12,7 +16,7 @@ module Inkcite
12
16
  # For the given image source URL provided, returns either the fully-qualfied
13
17
  # path to the image (via View's image_url method) or returns a placeholder
14
18
  # if the image is missing.
15
- def image_url _src, opt, ctx, assert_dimensions=true
19
+ def image_url _src, opt, ctx, assert_dimensions=true, add_quotes=true
16
20
 
17
21
  src = _src
18
22
 
@@ -81,7 +85,10 @@ module Inkcite
81
85
  # src so that we don't display the verbose qualified URL to the developer.
82
86
  ctx.error('Missing image dimensions', { :src => _src }) if missing_dimensions && assert_dimensions
83
87
 
84
- quote(src)
88
+ # If desired (and usually it is) wrap the src in quotes.
89
+ src = quote(src) if add_quotes
90
+
91
+ src
85
92
  end
86
93
 
87
94
  def klass_name src, ctx
@@ -115,7 +115,7 @@ module Inkcite
115
115
 
116
116
  # Always duplicate the string provided just to make sure we're not
117
117
  # modifying a frozen link or adding tagging to a previously tagged HREF.
118
- href = href.dup
118
+ href = href.dup if href
119
119
 
120
120
  # True if the href is missing. If so, we may try to look it up by it's ID
121
121
  # or we'll insert a default TBD link.
@@ -16,7 +16,7 @@ module Inkcite
16
16
  # Intentionally NOT using 'mso-hide: all' version as it requires all
17
17
  # nested tables to have that attribute applied. Why have all that extra
18
18
  # markup - just use this simple conditional instead.
19
- html << '<!--[if !mso 9]><!-->'
19
+ html << '{if-not test="mso 9"}'
20
20
 
21
21
  # These elements style the div such that it is invisible in all
22
22
  # other major email clients.
@@ -36,7 +36,7 @@ module Inkcite
36
36
  html << '</div>'
37
37
 
38
38
  # Close the outlook conditional for the close tag.
39
- html << '<!--<![endif]-->'
39
+ html << '{/if-not}'
40
40
 
41
41
  end
42
42
 
@@ -16,7 +16,7 @@ module Inkcite
16
16
  # 'vml: false' in config.yml
17
17
  return nil unless ctx.vml_enabled?
18
18
 
19
- html = '<!--[if gte mso 9]>'
19
+ html = '{if test="gte mso 9"}'
20
20
 
21
21
  if tag == '/outlook-bg'
22
22
  html << '</div>'
@@ -86,7 +86,7 @@ module Inkcite
86
86
 
87
87
  end
88
88
 
89
- html << '<![endif]-->'
89
+ html << '{/if}'
90
90
 
91
91
  html
92
92
  end
@@ -3,7 +3,7 @@ module Inkcite
3
3
 
4
4
  # Better video preview courtesy of @stigm
5
5
  # https://medium.com/cm-engineering/better-video-previews-for-email-12432ce71846#.2o9qgc7hd
6
- class VideoPreview < Base
6
+ class VideoPreview < ImageBase
7
7
 
8
8
  def render tag, opt, ctx
9
9
 
@@ -40,15 +40,7 @@ module Inkcite
40
40
  # this loop also verifies that the referenced image exists.
41
41
  frame_count.times do |n|
42
42
  frame_src = src.gsub('%1', "#{n + 1}")
43
-
44
- # Check that non-fully-qualified images exist in the project's
45
- # images/ directory.
46
- unless Util::is_fully_qualified?(frame_src)
47
- ctx.assert_image_exists(frame_src)
48
- frame_src = ctx.image_url(frame_src)
49
- end
50
-
51
- frames << frame_src
43
+ frames << image_url(frame_src, opt, ctx, false, false)
52
44
  end
53
45
 
54
46
  # Grab the first fully-qualified frame
@@ -81,12 +73,22 @@ module Inkcite
81
73
  # assigned to the table and defined in the CSS.
82
74
  animation_name = "#{hover_klass}#{uid}-frames"
83
75
 
76
+ # Size calculations based on the specified arrow size or
77
+ # the defaulted 30px arrow. The border_* variables control
78
+ # the circular border around the play arrow.
79
+ play_arrow_size = (opt[PLAY_ARROW_SIZE] || 30).to_i
80
+ play_arrow_height = (play_arrow_size * 0.5666).round
81
+ play_border_radius = (play_arrow_size * 1.1333).round
82
+ play_border_top_bottom = (play_arrow_size * 0.6).round
83
+ play_border_right = (play_arrow_size * 0.5333).round
84
+ play_border_left = (play_arrow_size * 0.8).round
85
+
84
86
  html = []
85
- html << '<!--[if !vml]-->'
87
+ html << '{not-outlook}'
86
88
 
87
89
  # Using an Element to produce the appropriate anchor helper with
88
90
  # the desired
89
- html << Element.new('a', { :id => id, :href => href, :class => hover_klass, :bgcolor => bgcolor, :bggradient => gradient, :block => true }).to_helper
91
+ html << Element.new('a', { :id => id, :href => quote(href), :class => hover_klass, :bgcolor => bgcolor, :bggradient => gradient, :block => true }).to_helper
90
92
 
91
93
  table = Element.new('table', {
92
94
  :width => '100%', :background => first_frame, BACKGROUND_SIZE => 'cover',
@@ -95,8 +97,6 @@ module Inkcite
95
97
  table[:animation] = %Q("#{animation_name} #{duration}s ease infinite") if has_animation
96
98
  html << table.to_helper
97
99
 
98
- html << Element.new('td', :width => '25%').to_helper
99
-
100
100
  # Transparent spacer for preserving aspect ratio.
101
101
  spacer_image_name = "vp-#{scaled_width}x#{height}.png"
102
102
  spacer_image = File.join(ctx.email.image_dir, spacer_image_name)
@@ -114,26 +114,18 @@ module Inkcite
114
114
 
115
115
  end
116
116
 
117
- # 12/21/16: Using placehold.it is sub-optimal. It would be better
118
- # to generate a transparent gif using something like Rmagick. I've
119
- # seen some instances where placeholdit can crash outlook but I guess
120
- # this part is hidden from outlook so it isn't so bad.
121
- html << Element.new('img', { :src => ctx.image_url(spacer_image_name), :alt => '', :width => '100%', :border => 0,
122
- :style => { :height => :auto, :opacity => 0, :visibility => :hidden } }).to_s
123
-
124
- html << '{/td}'
117
+ # Assembling the first <td> which manages the aspect ratio of the
118
+ # video as a separate string to avoid unnecessary line breaks in
119
+ # the resulting HTML.
120
+ aspect_ratio_td = Element.new('td', :width => '25%').to_helper
121
+ aspect_ratio_td << Element.new('img', { :src => ctx.image_url(spacer_image_name), :alt => quote(''), :width => '100%', :border => 0,
122
+ :style => { :height => :auto, :display => :block, :opacity => 0, :visibility => :hidden } }).to_s
123
+ aspect_ratio_td << '{/td}'
124
+ html << aspect_ratio_td
125
125
 
126
126
  # Center column holds the CSS-based arrow
127
127
  html << Element.new('td', :width => '50%', :align => :center, :valign => :middle).to_helper
128
128
 
129
- play_arrow_size = (opt[PLAY_ARROW_SIZE] || 30).to_i
130
-
131
- play_border_radius = (play_arrow_size * 1.1333).round
132
- play_border_top_bottom = (play_arrow_size * 0.6).round
133
- play_border_right = (play_arrow_size * 0.5333).round
134
- play_border_left = (play_arrow_size * 0.8).round
135
- play_arrow_height = (play_arrow_size * 0.5666).round
136
-
137
129
  # These are the arrow and circle border, respectively. Not currently
138
130
  # configurable in terms of size or color.
139
131
  html << %Q(<div class="#{play_button_klass}" 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: #{px(play_border_radius)}; margin: 0 auto; padding: #{px(play_border_top_bottom)} #{px(play_border_right)} #{px(play_border_top_bottom)} #{px(play_border_left)}; transition: transform .5s cubic-bezier(0.075, 0.82, 0.165, 1); width: #{px(play_border_radius)};">)
@@ -149,28 +141,53 @@ module Inkcite
149
141
  # browser only loads the frames once the animation demands them.
150
142
  if has_animation && !opt[NO_PRELOAD]
151
143
  all_frames = frames.collect { |f| %Q(url(#{f})) }.join(',')
152
- html << Element.new('div', :style => { BACKGROUND_IMAGE => %Q("#{all_frames}"), :display => 'none' }).to_s + '</div>'
144
+ html << Element.new('div', :style => { BACKGROUND_IMAGE => %Q(#{all_frames}), :display => 'none' }).to_s + '</div>'
153
145
  end
154
146
 
155
147
  # Concludes the if [if !vml] section targeting non-outlook.
156
- html << '<![endif]-->'
157
-
158
- # Outlook arrow size also not configurable at this time.
159
- outlook_arrow_size = 78
160
- outlook_left = width / 2 - outlook_arrow_size / 2
161
- outlook_top = height / 2 - outlook_arrow_size / 2
162
-
163
- # Use the link central processing routine to ensure a viable link has
164
- # been provided and tag/track it from Outlook.
165
- outlook_href = Link.process(id, href, false, ctx)
166
-
167
- html << '<!--[if vml]>'
168
- html << %Q(<v:group xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" coordsize="#{width},#{height}" coordorigin="0,0" href="#{outlook_href}" style="width:#{width}px;height:#{height}px;">)
169
- html << %Q(<v:rect fill="t" stroked="f" style="position:absolute;width:#{width};height:#{height};"><v:fill src="#{first_frame}" type="frame"/></v:rect>)
170
- html << %Q(<v:oval fill="t" strokecolor="white" strokeweight="4px" style="position:absolute;left:#{outlook_left};top:"#{outlook_top};width:#{outlook_arrow_size};height:#{outlook_arrow_size}"><v:fill color="black" opacity="30%" /></v:oval>)
171
- html << %q(<v:shape coordsize="24,32" path="m,l,32,24,16,xe" fillcolor="white" stroked="f" style="position:absolute;left:289;top:151;width:30;height:34;" />)
172
- html << '</v:group>'
173
- html << '<![endif]-->'
148
+ html << '{/not-outlook}'
149
+
150
+ # Check for the outlook-src attribute which will be used in place of
151
+ # the first frame if it is specified.
152
+ outlook_src = opt[OUTLOOK_SRC]
153
+ outlook_src = outlook_src.blank? ? first_frame : image_url(outlook_src, opt, ctx, false, false)
154
+
155
+ html << '{outlook-only}'
156
+ if ctx.vml_enabled?
157
+
158
+ # Calculations necessary to render the play arrow in VML.
159
+ outlook_arrow_size = (play_arrow_size * 2.6).round
160
+ outlook_arrow_width = (play_arrow_size * 1.0666).round
161
+ outlook_arrow_height = (play_arrow_size * 0.5333).round
162
+ outlook_arrow_left = width / 2 - play_arrow_size / 2
163
+ outlook_arrow_top = height / 2 - play_arrow_size / 2
164
+ outlook_border_left = width / 2 - outlook_arrow_size / 2
165
+ outlook_border_top = height / 2 - outlook_arrow_size / 2
166
+
167
+ # Use the link central processing routine to ensure a viable link has
168
+ # been provided and tag/track it from Outlook.
169
+ outlook_id, outlook_href, target_blank = Link.process(id, href, false, ctx)
170
+
171
+ html << %Q(<v:group xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" coordsize="#{width},#{height}" coordorigin="0,0" href="#{outlook_href}" style="width:#{width}px;height:#{height}px;">)
172
+ html << %Q(<v:rect fill="t" stroked="f" style="position:absolute;width:#{width};height:#{height};"><v:fill src="#{outlook_src}" type="frame"/></v:rect>)
173
+ html << %Q(<v:oval fill="t" strokecolor="white" strokeweight="4px" style="position:absolute;left:#{outlook_border_left};top:#{outlook_border_top};width:#{outlook_arrow_size};height:#{outlook_arrow_size}"><v:fill color="black" opacity="30%"/></v:oval>)
174
+ html << %Q(<v:shape coordsize="#{play_border_left},#{outlook_arrow_width}" path="m,l,#{outlook_arrow_width},#{play_border_left},#{outlook_arrow_height},xe" fillcolor="white" stroked="f" style="position:absolute;left:#{outlook_arrow_left};top:#{outlook_arrow_top};width:#{play_arrow_size};height:#{play_arrow_size};"/>)
175
+ html << '</v:group>'
176
+
177
+ # Notify the context that VML was used in this version.
178
+ ctx.vml_used!
179
+
180
+ else
181
+
182
+ # This is the only version that can support alt text.
183
+ alt = opt[:alt] || 'Click to play'
184
+
185
+ html << Element.new('a', { :id => id, :href => quote(href) }).to_helper +
186
+ Element.new('img', { :src => quote(outlook_src), :height => height, :width => width, :alt => quote(alt) }).to_helper +
187
+ '{/a}'
188
+
189
+ end
190
+ html << '{/outlook-only}'
174
191
 
175
192
  # Will hold any CSS styles, if there are some necessary
176
193
  # to inject into the email.
@@ -1,3 +1,3 @@
1
1
  module Inkcite
2
- VERSION = "1.12.0"
2
+ VERSION = "1.12.1"
3
3
  end
@@ -420,9 +420,7 @@ module Inkcite
420
420
 
421
421
  # Enable responsive media queries on Windows phones courtesy of @jamesmacwhite
422
422
  # https://blog.jmwhite.co.uk/2014/03/01/windows-phone-does-support-css3-media-queries-in-html-email/
423
- html << '<!--[if !mso]><!-- -->'
424
- html << '<meta http-equiv="X-UA-Compatible" content="IE=edge" />'
425
- html << '<!--<![endif]-->'
423
+ html << Renderer.render('{not-outlook}<meta http-equiv="X-UA-Compatible" content="IE=edge" />{/not-outlook}', self)
426
424
 
427
425
  # Some native Android clients display the title before the preheader so
428
426
  # don't include it in non-development or email rendering per @moonstrips
@@ -847,10 +845,8 @@ module Inkcite
847
845
  html.join(NEW_LINE)
848
846
  end
849
847
 
850
- def load_helper_file filename, into, abort_on_fail=true
848
+ def load_helper_file file, into, abort_on_fail=true
851
849
 
852
- path = @email.path
853
- file = File.join(path, filename)
854
850
  unless File.exist?(file)
855
851
  error("Unable to load helper file", :file => file) if abort_on_fail
856
852
  return
@@ -904,18 +900,24 @@ module Inkcite
904
900
  :n => NEW_LINE
905
901
  }
906
902
 
903
+ # Get the project path from which most helpers will be loaded.
904
+ path = @email.path
905
+
906
+ # First load the built-in helpers.
907
+ load_helper_file(File.join(Inkcite.asset_path, 'builtin-helpers.tsv'), _helpers, false)
908
+
907
909
  # Check to see if the config.yml includes a "helpers:" array which allows
908
910
  # additional out-of-project, shared helper files to be loaded.
909
911
  included_helpers = [*@email.config[:helpers]]
910
- included_helpers.each { |file| load_helper_file(file, _helpers) }
912
+ included_helpers.each { |file| load_helper_file(File.join(path, file), _helpers) }
911
913
 
912
914
  # Load the project's properties, which may include references to additional
913
915
  # properties in other directories.
914
- load_helper_file 'helpers.tsv', _helpers
916
+ load_helper_file File.join(path, 'helpers.tsv'), _helpers
915
917
 
916
918
  # Look for a version-specific override allowing restyling of an email based
917
919
  # on its version - e.g. difference colors in the "no orders for 30 days" email.
918
- load_helper_file "helpers-#{@version}.tsv", _helpers, false
920
+ load_helper_file File.join(path, "helpers-#{@version}.tsv"), _helpers, false
919
921
 
920
922
  # As a convenience pre-populate the month name of the email.
921
923
  mm = _helpers[:mm].to_i
@@ -5,17 +5,17 @@ describe Inkcite::Renderer::MobileOnly do
5
5
  end
6
6
 
7
7
  it 'styles a div to show content only on mobile' do
8
- Inkcite::Renderer.render('{mobile-only}I will only appear on mobile{/mobile-only}', @view).must_equal('<!--[if !mso 9]><!--><div class="show" style="display:none;max-height:0;overflow:hidden">I will only appear on mobile</div><!--<![endif]-->')
8
+ Inkcite::Renderer.render('{mobile-only}I will only appear on mobile{/mobile-only}', @view).must_equal('<!--[if !mso 9]><!-- --><div class="show" style="display:none;max-height:0;overflow:hidden">I will only appear on mobile</div><!--<![endif]-->')
9
9
  @view.media_query.find_by_klass('show').declarations.must_match('display: block !important; max-height: none !important;')
10
10
  end
11
11
 
12
12
  it 'supports inline as a boolean attribute' do
13
- Inkcite::Renderer.render('800-555-1212{mobile-only inline}&nbsp;&raquo;{/mobile-only}', @view).must_equal('800-555-1212<!--[if !mso 9]><!--><div class="show-inline" style="display:none;max-height:0;overflow:hidden">&nbsp;&raquo;</div><!--<![endif]-->')
13
+ Inkcite::Renderer.render('800-555-1212{mobile-only inline}&nbsp;&raquo;{/mobile-only}', @view).must_equal('800-555-1212<!--[if !mso 9]><!-- --><div class="show-inline" style="display:none;max-height:0;overflow:hidden">&nbsp;&raquo;</div><!--<![endif]-->')
14
14
  @view.media_query.find_by_klass('show-inline').declarations.must_match('display: inline !important; max-height: none !important;')
15
15
  end
16
16
 
17
17
  it 'does not support fonts or other container attributes' do
18
- Inkcite::Renderer.render('{mobile-only font=large}Fonts not supported{/mobile-only}', @view).must_equal('<!--[if !mso 9]><!--><div class="show" style="display:none;max-height:0;overflow:hidden">Fonts not supported</div><!--<![endif]-->')
18
+ Inkcite::Renderer.render('{mobile-only font=large}Fonts not supported{/mobile-only}', @view).must_equal('<!--[if !mso 9]><!-- --><div class="show" style="display:none;max-height:0;overflow:hidden">Fonts not supported</div><!--<![endif]-->')
19
19
  end
20
20
 
21
21
  end
@@ -5,7 +5,7 @@ describe Inkcite::Renderer::VideoPreview do
5
5
  end
6
6
 
7
7
  it 'renders an animated video preview' do
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 !vml]-->\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:video1-frames 15s ease infinite;animation:video1-frames 15s ease infinite;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%>\n<img border=0 src=images/vp-150x337.png style="height:auto;opacity:0;visibility:hidden" width=100%>\n</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;">&nbsp;</div>\n</div>\n</td>\n<td width=25%>&nbsp;</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 vml]>\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="["ripcurl", "https://www.campaignmonitor.com/customers/ripcurl", true]" 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:289;top:151;width:30;height:34;" />\n</v:group>\n<![endif]-->))
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:video1-frames 15s ease infinite;animation:video1-frames 15s ease infinite;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;">&nbsp;</div>\n</div>\n</td>\n<td width=25%>&nbsp;</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
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 {\n 0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n 22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n 33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n 55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n 66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n 88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n 100% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n}\n@-webkit-keyframes video1-frames {\n 0% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n 22% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame1.jpg) }\n 33% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n 55% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame2.jpg) }\n 66% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n 88% { background-image:url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/103009/frame3.jpg) }\n 100% { 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
@@ -16,4 +16,15 @@ describe Inkcite::Renderer::VideoPreview do
16
16
 
17
17
  end
18
18
 
19
+ it 'supports the outlook-src attribute' do
20
+ 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" outlook-src="outlook-anim.gif" 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:video1-frames 15s ease infinite;animation:video1-frames 15s ease infinite;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;">&nbsp;</div>\n</div>\n</td>\n<td width=25%>&nbsp;</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="images/outlook-anim.gif" 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]-->))
21
+
22
+ # Verify that the transparent spacer image was created and then
23
+ # clean it up.
24
+ spacer_image = @view.email.image_path('vp-150x337.png')
25
+ File.exist?(spacer_image).must_equal(true)
26
+ File.delete(spacer_image)
27
+
28
+ end
29
+
19
30
  end
@@ -25,4 +25,16 @@ describe Inkcite::View do
25
25
  Inkcite::Renderer.render('{project}', @view).must_equal('project')
26
26
  end
27
27
 
28
+ it 'can produce Outlook-only code' do
29
+ Inkcite::Renderer.render('{outlook-only}MSO 07-13{/outlook-only}', @view).must_equal('<!--[if mso]>MSO 07-13<![endif]-->')
30
+ end
31
+
32
+ it 'can produce code only visible in VML-aware clients' do
33
+ Inkcite::Renderer.render('{vml-only}VML-aware clients only{/vml-only}', @view).must_equal('<!--[if vml]>VML-aware clients only<![endif]-->')
34
+ end
35
+
36
+ it 'can exclude Outlook' do
37
+ Inkcite::Renderer.render('{not-vml}All except VML-aware clients{/not-vml}', @view).must_equal('<!--[if !vml]><!-- -->All except VML-aware clients<!--<![endif]-->')
38
+ end
39
+
28
40
  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.12.0
4
+ version: 1.12.1
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: 2016-12-30 00:00:00.000000000 Z
11
+ date: 2017-01-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -345,6 +345,7 @@ files:
345
345
  - README.md
346
346
  - Rakefile
347
347
  - assets/blueprint.png
348
+ - assets/builtin-helpers.tsv
348
349
  - assets/example/helpers.tsv
349
350
  - assets/example/source.html
350
351
  - assets/example/source.txt