inkcite 1.12.0 → 1.12.1

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 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