inkcite 1.2.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/inkcite/view.rb CHANGED
@@ -151,7 +151,32 @@ module Inkcite
151
151
  end
152
152
 
153
153
  def footnotes
154
- @footnotes ||= []
154
+
155
+ if @footnotes.nil?
156
+ @footnotes = []
157
+
158
+ # Preload the array of footnotes if they exist
159
+ footnotes_tsv_file = @email.project_file(FOOTNOTES_TSV_FILE)
160
+ if File.exists?(footnotes_tsv_file)
161
+ CSV.foreach(footnotes_tsv_file, { :col_sep => "\t" }) do |fn|
162
+
163
+ id = fn[0]
164
+ next if id.blank?
165
+
166
+ text = fn[2]
167
+ next if text.blank?
168
+
169
+ # Read the symbol and replace it with nil (so that one will be auto-generated)
170
+ symbol = fn[1]
171
+ symbol = nil if symbol.blank?
172
+
173
+ @footnotes << Renderer::Footnote::Instance.new(id, symbol, text, false)
174
+
175
+ end
176
+ end
177
+ end
178
+
179
+ @footnotes
155
180
  end
156
181
 
157
182
  def file_name ext=nil
@@ -190,7 +215,12 @@ module Inkcite
190
215
 
191
216
  # Prepend the image host onto the src if one is specified in the properties.
192
217
  # During local development, images are always expected in an images/ subdirectory.
193
- image_host = development?? "#{Email::IMAGES}/" : self[Email::IMAGE_HOST]
218
+ image_host = if development?
219
+ (@email.optimize_images?? Minifier::IMAGE_CACHE : Email::IMAGES) + '/'
220
+ else
221
+ self[Email::IMAGE_HOST]
222
+ end
223
+
194
224
  src_url << image_host unless image_host.blank?
195
225
 
196
226
  # Add the source of the image.
@@ -344,13 +374,19 @@ module Inkcite
344
374
  html << '</style>'
345
375
  html << '</head>'
346
376
 
347
- # Render the body statement and apply the email's background color to it.
348
- bgcolor = Renderer.hex(self[BACKGROUND])
349
-
350
- # Intentially not setting the link colors because those should be entirely
377
+ # Intentionally not setting the link colors because those should be entirely
351
378
  # controlled by the styles and attributes of the links themselves. By not
352
379
  # setting it, links created sans-helper should be visually distinct.
353
- html << Renderer.render("<body bgcolor=\"#{bgcolor}\" style=\"background-color: #{bgcolor}; width: 100% !important; min-width: 100% !important; margin: 0; padding: 0; -webkit-text-size-adjust: none; -ms-text-size-adjust: none;\">", self)
380
+ html << '<body style="width: 100% !important; min-width: 100% !important; margin: 0 !important; padding: 0; -webkit-text-size-adjust: none; -ms-text-size-adjust: none;'
381
+
382
+ # A pleasing but obvious background exposed in development mode to alert
383
+ # the designer that they have exposed the body background - which means
384
+ # unpredictable results if sent.
385
+ if development?
386
+ html << " background: #ccc url('data:image/png;base64,#{Inkcite.blueprint_image64}');"
387
+ end
388
+
389
+ html << %q(">)
354
390
 
355
391
  html << minified
356
392
 
@@ -479,7 +515,6 @@ module Inkcite
479
515
  private
480
516
 
481
517
  ASSETS = 'assets'
482
- BACKGROUND = :'#background'
483
518
  FILE_SCHEME = 'file'
484
519
  FILE_NAME = :'file-name'
485
520
  HTML_EXTENSION = '.html'
@@ -522,6 +557,9 @@ module Inkcite
522
557
  # Tab-separated file containing links declarations.
523
558
  LINKS_TSV_FILE = 'links.tsv'
524
559
 
560
+ # Tab-separated file containing footnote declarations.
561
+ FOOTNOTES_TSV_FILE = 'footnotes.tsv'
562
+
525
563
  def assert_in_browser msg
526
564
  raise msg if email? && !development?
527
565
  end
@@ -716,6 +754,10 @@ module Inkcite
716
754
  # Ensure that telephone numbers are displayed using the same style as links.
717
755
  reset << "a[href^=tel] { color: #{self[Renderer::Base::LINK_COLOR]}; text-decoration:none;}"
718
756
 
757
+ # Remove extraneous left-margins on Android 4.4
758
+ # https://litmus.com/community/code/4194-why-is-email-not-centered-on-android-4-4#comment-5727
759
+ reset << 'div[style*="margin: 16px 0"] { margin:0 !important; }'
760
+
719
761
  end
720
762
 
721
763
  # Reset the font on every cell to the default family.
data/lib/inkcite.rb CHANGED
@@ -23,7 +23,16 @@ require 'inkcite/renderer'
23
23
  module Inkcite
24
24
 
25
25
  def self.asset_path
26
- File.expand_path('../../..', File.dirname(__FILE__))
26
+ File.join(File.expand_path('../', File.dirname(__FILE__)), 'assets')
27
+ end
28
+
29
+ # Loads (and caches) the base64-encoded PNG data for the subtle background
30
+ # texture that Inkcite installs on the <body> tag in development mode.
31
+ def self.blueprint_image64
32
+ @blueprint64 ||= begin
33
+ blueprint_path = File.join(asset_path, 'blueprint.png')
34
+ Base64.encode64(File.read(blueprint_path)).gsub(/[\r\f\n]/, '')
35
+ end
27
36
  end
28
37
 
29
38
  end
@@ -0,0 +1,97 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'inkcite'
4
+
5
+ describe Inkcite::Renderer::Span do
6
+
7
+ before do
8
+ @view = Inkcite::Email.new('test/project/').view(:development, :email)
9
+ end
10
+
11
+ it 'can have empty parameters' do
12
+ Inkcite::Renderer.render('{span}{/span}', @view).must_equal('<span></span>')
13
+ end
14
+
15
+ it 'can have a custom font color' do
16
+ Inkcite::Renderer.render('{span color=#f90}{/span}', @view).must_equal('<span style="color:#ff9900"></span>')
17
+ end
18
+
19
+ it 'can have a custom font family' do
20
+ Inkcite::Renderer.render('{span font-family="Comic Sans"}{/span}', @view).must_equal('<span style="font-family:Comic Sans"></span>')
21
+ end
22
+
23
+ it 'can have a custom font size' do
24
+ Inkcite::Renderer.render('{span font-size=18}{/span}', @view).must_equal('<span style="font-size:18px"></span>')
25
+ end
26
+
27
+ it 'can have a custom font weight' do
28
+ Inkcite::Renderer.render('{span font-weight=bold}{/span}', @view).must_equal('<span style="font-weight:bold"></span>')
29
+ end
30
+
31
+ it 'can have a custom line height' do
32
+ Inkcite::Renderer.render('{span line-height=15}{/span}', @view).must_equal('<span style="line-height:15px"></span>')
33
+ end
34
+
35
+ it 'can inherit a font from the context' do
36
+ Inkcite::Renderer.render('{span font=large}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
37
+ end
38
+
39
+ it 'can override the font size of an inherited font' do
40
+ Inkcite::Renderer.render('{span font=large font-size=8}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:8px;font-weight:bold;line-height:24px"></span>')
41
+ Inkcite::Renderer.render('{span font=large font-size=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-weight:bold;line-height:24px"></span>')
42
+ end
43
+
44
+ it 'can override the color of an inherited font' do
45
+ Inkcite::Renderer.render('{span font=large color=#00f}{/span}', @view).must_equal('<span style="color:#0000ff;font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
46
+ Inkcite::Renderer.render('{span font=large color=none}{/span}', @view).must_equal('<span style="font-family:serif;font-size:24px;font-weight:bold;line-height:24px"></span>')
47
+ end
48
+
49
+ it 'can override the font weight of an inherited font' do
50
+ Inkcite::Renderer.render('{span font=large font-weight=normal}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:normal;line-height:24px"></span>')
51
+ Inkcite::Renderer.render('{span font=large font-weight=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;line-height:24px"></span>')
52
+ end
53
+
54
+ it 'can override the line height of an inherited font' do
55
+ Inkcite::Renderer.render('{span font=large line-height=12}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:12px"></span>')
56
+ Inkcite::Renderer.render('{span font=large line-height=normal}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold;line-height:normal"></span>')
57
+ Inkcite::Renderer.render('{span font=large line-height=none}{/span}', @view).must_equal('<span style="color:#ff0000;font-family:serif;font-size:24px;font-weight:bold"></span>')
58
+ end
59
+
60
+ it 'can have a text shadow' do
61
+ Inkcite::Renderer.render('{span shadow=#99c}{/span}', @view).must_equal('<span style="text-shadow:0 1px 0 #9999cc"></span>')
62
+ Inkcite::Renderer.render('{span shadow=#9c9 shadow-blur=2}{/span}', @view).must_equal('<span style="text-shadow:0 1px 2px #99cc99"></span>')
63
+ Inkcite::Renderer.render('{span shadow=#c99 shadow-offset=-1}{/span}', @view).must_equal('<span style="text-shadow:0 -1px 0 #cc9999"></span>')
64
+ end
65
+
66
+ it 'can have a background color' do
67
+ Inkcite::Renderer.render('{span bgcolor=#06c}{/span}', @view).must_equal('<span style="background-color:#0066cc"></span>')
68
+ end
69
+
70
+ it 'can be responsive' do
71
+ Inkcite::Renderer.render('{span mobile=hide}{/span}', @view).must_equal('<span class="hide"></span>')
72
+ end
73
+
74
+ it 'can have custom letter spacing' do
75
+ Inkcite::Renderer.render('{span letter-spacing=3}{/span}', @view).must_equal('<span style="letter-spacing:3px"></span>')
76
+ end
77
+
78
+ it 'can have a custom font size on mobile' do
79
+ Inkcite::Renderer.render('{span font-size=15 mobile-font-size=20}{/span}', @view).must_equal('<span class="m1" style="font-size:15px"></span>')
80
+ @view.media_query.find_by_klass('m1').declarations.must_match('font-size:20px !important')
81
+ end
82
+
83
+ it 'can have a custom line height on mobile' do
84
+ Inkcite::Renderer.render('{span line-height=15 mobile-line-height=20}{/span}', @view).must_equal('<span class="m1" style="line-height:15px"></span>')
85
+ @view.media_query.find_by_klass('m1').to_css.must_equal('span[class~="m1"] { line-height:20px !important }')
86
+ end
87
+
88
+ it 'can inherit a custom font size on mobile from the context' do
89
+ Inkcite::Renderer.render('{span font=responsive}{/span}', @view).must_equal('<span class="m1" style="font-size:20px"></span>')
90
+ @view.media_query.find_by_klass('m1').declarations.must_match('font-size:40px')
91
+ end
92
+
93
+ it 'supports padding' do
94
+ Inkcite::Renderer.render('{span padding=15}{/span}', @view).must_equal('<span style="padding:15px"></span>')
95
+ end
96
+
97
+ end
@@ -91,12 +91,12 @@ describe Inkcite::Renderer::Td do
91
91
 
92
92
  it 'can have a custom background color on mobile' do
93
93
  Inkcite::Renderer.render('{td mobile-bgcolor=#f09}', @view).must_equal('<td class="m1">')
94
- @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background-color:#ff0099 }')
94
+ @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:#ff0099 }')
95
95
  end
96
96
 
97
97
  it 'can override background color on mobile' do
98
98
  Inkcite::Renderer.render('{td bgcolor=#f00 mobile-bgcolor=#00f}', @view).must_equal('<td bgcolor=#ff0000 class="m1">')
99
- @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background-color:#0000ff !important }')
99
+ @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:#0000ff !important }')
100
100
  end
101
101
 
102
102
  it 'can have a background image' do
@@ -109,18 +109,18 @@ describe Inkcite::Renderer::Td do
109
109
  end
110
110
 
111
111
  it 'can override background image on mobile' do
112
- Inkcite::Renderer.render('{td background=floor.jpg background-position=bottom mobile-background-image=sky.jpg mobile-background-position=top}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg) bottom no-repeat">')
113
- @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/sky.jpg) top no-repeat !important }')
112
+ Inkcite::Renderer.render('{td background=floor.jpg mobile-background-image=sky.jpg }', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg)">')
113
+ @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/sky.jpg) !important }')
114
114
  end
115
115
 
116
- it 'can disable background image on mobile' do
117
- Inkcite::Renderer.render('{td bgcolor=#f00 background=floor.jpg background-position=bottom mobile-bgcolor=#0f0 mobile-background-image=none}', @view).must_equal('<td bgcolor=#ff0000 class="m1" style="background:#ff0000 url(images/floor.jpg) bottom no-repeat">')
118
- @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background-color:#00ff00 !important;background-image:none !important }')
116
+ it 'can override background position on mobile' do
117
+ Inkcite::Renderer.render('{td background=floor.jpg background-position=bottom mobile-background-position=top}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg) bottom no-repeat">')
118
+ @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/floor.jpg) top no-repeat !important }')
119
119
  end
120
120
 
121
- it 'inherits background position and repeat on mobile' do
122
- Inkcite::Renderer.render('{td background=floor.jpg background-position=bottom mobile-background-image=sky.jpg}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg) bottom no-repeat">')
123
- @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:url(images/sky.jpg) bottom no-repeat !important }')
121
+ it 'can disable background image on mobile' do
122
+ Inkcite::Renderer.render('{td background=floor.jpg mobile-background-image=none}', @view).must_equal('<td class="m1" style="background:url(images/floor.jpg)">')
123
+ @view.media_query.find_by_klass('m1').to_css.must_equal('td[class~="m1"] { background:none !important }')
124
124
  end
125
125
 
126
126
  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.2.0
4
+ version: 1.6.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: 2015-09-15 00:00:00.000000000 Z
11
+ date: 2015-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: image_optim
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: image_optim_pack
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: litmus
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -232,12 +260,16 @@ files:
232
260
  - LICENSE
233
261
  - README.md
234
262
  - Rakefile
263
+ - assets/blueprint.png
264
+ - assets/example/helpers.tsv
265
+ - assets/example/source.html
266
+ - assets/example/source.txt
235
267
  - assets/facebook-like.css
236
268
  - assets/facebook-like.js
237
269
  - assets/init/config.yml
238
270
  - assets/init/helpers.tsv
271
+ - assets/init/image_optim.yml
239
272
  - assets/init/source.html
240
- - assets/init/source.txt
241
273
  - bin/inkcite
242
274
  - bin/release-major
243
275
  - bin/release-minor
@@ -267,7 +299,7 @@ files:
267
299
  - lib/inkcite/renderer/increment.rb
268
300
  - lib/inkcite/renderer/like.rb
269
301
  - lib/inkcite/renderer/link.rb
270
- - lib/inkcite/renderer/litmus.rb
302
+ - lib/inkcite/renderer/litmus_analytics.rb
271
303
  - lib/inkcite/renderer/lorem.rb
272
304
  - lib/inkcite/renderer/mobile_image.rb
273
305
  - lib/inkcite/renderer/mobile_style.rb
@@ -304,6 +336,7 @@ files:
304
336
  - test/renderer/link_spec.rb
305
337
  - test/renderer/mobile_image_spec.rb
306
338
  - test/renderer/mobile_style_spec.rb
339
+ - test/renderer/span_spec.rb
307
340
  - test/renderer/table_spec.rb
308
341
  - test/renderer/td_spec.rb
309
342
  - test/renderer_spec.rb
@@ -349,6 +382,7 @@ test_files:
349
382
  - test/renderer/link_spec.rb
350
383
  - test/renderer/mobile_image_spec.rb
351
384
  - test/renderer/mobile_style_spec.rb
385
+ - test/renderer/span_spec.rb
352
386
  - test/renderer/table_spec.rb
353
387
  - test/renderer/td_spec.rb
354
388
  - test/renderer_spec.rb
@@ -1,33 +0,0 @@
1
- module Inkcite
2
- module Renderer
3
- class Litmus < Base
4
-
5
- def render tag, opt, ctx
6
-
7
- # Litmus tracking is enabled only for production emails.
8
- return nil unless ctx.production? && ctx.email?
9
-
10
- code = opt[:code] || opt[:id]
11
- return nil if code.blank?
12
-
13
- merge_tag = opt[MERGE_TAG] || ctx[MERGE_TAG]
14
-
15
- ctx.styles << "@media print{#_t { background-image: url('https://#{code}.emltrk.com/#{code}?p&d=#{merge_tag}');}}"
16
- ctx.styles << "div.OutlookMessageHeader {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
17
- ctx.styles << "table.moz-email-headers-table {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
18
- ctx.styles << "blockquote #_t {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
19
- ctx.styles << "#MailContainerBody #_t {background-image:url('https://#{code}.emltrk.com/#{code}?f&d=#{merge_tag}')}"
20
-
21
- ctx.footer << '<div id="_t"></div>'
22
- ctx.footer << "<img src=\"https://#{code}.emltrk.com/#{code}?d=#{merge_tag}\" width=1 height=1 border=0 />"
23
-
24
- nil
25
- end
26
-
27
- private
28
-
29
- MERGE_TAG = :'merge-tag'
30
-
31
- end
32
- end
33
- end