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.
@@ -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
@@ -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
- mobile_padding = table_opt[MOBILE_PADDING] || opt[MOBILE_PADDING]
79
- td.mobile_style[:padding] = px(mobile_padding) unless mobile_padding.blank? || mobile == HIDE
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 => first_frame, BACKGROUND_SIZE => 'cover',
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
 
@@ -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 }
@@ -1,3 +1,3 @@
1
1
  module Inkcite
2
- VERSION = "1.13.0"
2
+ VERSION = "1.14.0"
3
3
  end
@@ -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
- [ '', '-webkit-' ]
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
@@ -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
@@ -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
@@ -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;">&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
- @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.
@@ -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.13.0
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-01-20 00:00:00.000000000 Z
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/element.rb
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