premailer 1.11.1 → 1.21.0

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
- SHA1:
3
- metadata.gz: 2e98876ae79a7e20d9a4ce73747f40099d4f19a1
4
- data.tar.gz: f9f95d0fbc0b3304db6cd772069729a5801c388d
2
+ SHA256:
3
+ metadata.gz: 47eb32064c7ed2912f426d2666ceb0aba0bd7a7f9c235bbc20e86bf961374688
4
+ data.tar.gz: 84dc0c5fa0331965aaa552f542d05bc2165cc140b98b25826fa7eb4e0470167e
5
5
  SHA512:
6
- metadata.gz: 88f3f5d7ba2f1e4ca8e0afdb33b2514352b80914ed98629bab52d9b92450dfa0b133994d08f0f3f3b42f33d10f0e90253432592ebcb0a931bbb7b1f54465dcfe
7
- data.tar.gz: 3d61952d50bfc6ea280fa9f36da8da0a8e7df1af5967c9e742334a26bc916bbe0db463efb69e70f62b77e6770b18cc52b821b2d36d0f9d37c56bd36b803af037
6
+ metadata.gz: 3cc088f586ad72db34ed8cf8a44f7b9cdffd4ce1ccbea2009160f228364683f64b7e4891b914c8db4d4c04d3603b792648800912eff6523650c99c5702af17d2
7
+ data.tar.gz: 023ecd473bd37bf98b7f9e551bda5f7daf494e4f56bad458aa66d01d9e30e7647d514b13c7560aae97f3b06a00c79228a765cc4698a838d1575adc1ef1b673a6
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Premailer README [![Build Status](https://travis-ci.org/premailer/premailer.png?branch=master)](https://travis-ci.org/premailer/premailer) [![Gem Version](https://badge.fury.io/rb/premailer.svg)](https://badge.fury.io/rb/premailer)
1
+ # Premailer README [![CI](https://github.com/premailer/premailer/actions/workflows/actions.yml/badge.svg)](https://github.com/premailer/premailer/actions/workflows/actions.yml) [![Gem Version](https://badge.fury.io/rb/premailer.svg)](https://badge.fury.io/rb/premailer)
2
2
 
3
3
  ## What is this?
4
4
 
@@ -12,7 +12,7 @@ script is my solution.
12
12
  - Checks links in `href`, `src` and CSS `url('')`
13
13
  * CSS properties are checked against e-mail client capabilities
14
14
  - Based on the Email Standards Project's guides
15
- * A plain text version is created (optional)
15
+ * A [plain text version](https://premailer.github.io/premailer/HtmlToPlainText.html) is created (optional)
16
16
 
17
17
  ## Installation
18
18
 
@@ -68,7 +68,7 @@ Premailer::Adapter.use = :nokogiri_fast
68
68
 
69
69
  ## Ruby Compatibility
70
70
 
71
- Premailer is tested on Ruby 2.1 and above. JRuby support is close; contributors are welcome. Checkout the latest build status on the [Travis CI dashboard](https://travis-ci.org/#!/premailer/premailer).
71
+ See .github/workflows/actions.yml for which ruby versions are tested. JRuby support is close, contributors are welcome.
72
72
 
73
73
  ## Premailer-specific CSS
74
74
 
@@ -80,6 +80,7 @@ Premailer looks for a few CSS attributes that make working with tables a bit eas
80
80
  | -premailer-height | Available on `table`, `tr`, `th` and `td` elements |
81
81
  | -premailer-cellpadding | Available on `table` elements |
82
82
  | -premailer-cellspacing | Available on `table` elements |
83
+ | -premailer-align | Available on `table` elements |
83
84
  | data-premailer="ignore" | Available on `link` and `style` elements. Premailer will ignore these elements entirely. |
84
85
 
85
86
  Each of these CSS declarations will be copied to appropriate element's attribute.
@@ -96,6 +97,19 @@ will result in
96
97
  <table cellspacing='5' width='500'>
97
98
  ```
98
99
 
100
+ ## Configuration options
101
+
102
+ The behavior of Premailer can be configured by passing options in the initializer.
103
+
104
+ For example, the following will accept HTML from a string and will exclude unmergeable css from being added to the `<head>` of the output document.
105
+
106
+ ```ruby
107
+ premailer = Premailer.new(html_string, with_html_string: true, drop_unmergeable_css_rules: true)
108
+ ```
109
+
110
+ [See here for a full list of the available options](https://premailer.github.io/premailer/Premailer.html#initialize-instance_method).
111
+
112
+
99
113
  ## Contributions
100
114
 
101
115
  Contributions are most welcome. Premailer was rotting away in a private SVN repository for too long and could use some TLC. Fork and patch to your heart's content. Please don't increment the version numbers, though.
@@ -55,9 +55,7 @@ class Premailer
55
55
  end
56
56
 
57
57
  # Remove script tags
58
- if @options[:remove_scripts]
59
- doc.search("script").remove
60
- end
58
+ doc.search("script").remove if @options[:remove_scripts]
61
59
 
62
60
  # Read STYLE attributes and perform folding
63
61
  doc.search("*[@style]").each do |el|
@@ -65,28 +63,51 @@ class Premailer
65
63
 
66
64
  declarations = []
67
65
  style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
68
- rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
69
- declarations << rs
66
+ begin
67
+ rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
68
+ declarations << rs
69
+ rescue ArgumentError => e
70
+ raise e if @options[:rule_set_exceptions]
71
+ end
70
72
  end
71
73
 
72
74
  # Perform style folding
73
75
  merged = CssParser.merge(declarations)
74
- merged.expand_shorthand!
76
+ begin
77
+ merged.expand_shorthand!
78
+ rescue ArgumentError => e
79
+ raise e if @options[:rule_set_exceptions]
80
+ end
75
81
 
76
82
  # Duplicate CSS attributes as HTML attributes
77
83
  if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
78
- Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
79
- if el[html_att].nil? and not merged[css_att].empty?
80
- new_html_att = merged[css_att].gsub(/url\(['"](.*)['"]\)/, '\1').gsub(/;$|\s*!important/, '').strip
81
- el[html_att] = css_att.end_with?('color') && @options[:rgb_to_hex_attributes] ? ensure_hex(new_html_att) : new_html_att
84
+ Premailer::RELATED_ATTRIBUTES[el.name].each do |css_attr, html_attr|
85
+ if el[html_attr].nil? and not merged[css_attr].empty?
86
+ new_val = merged[css_attr].dup
87
+
88
+ # Remove url() function wrapper
89
+ new_val.gsub!(/url\((['"])(.*?)\1\)/, '\2')
90
+
91
+ # Remove !important, trailing semi-colon, and leading/trailing whitespace
92
+ new_val.gsub!(/;$|\s*!important/, '').strip!
93
+
94
+ # For width and height tags, remove px units
95
+ new_val.gsub!(/(\d+)px/, '\1') if %w[width height].include?(html_attr)
96
+
97
+ # For color-related tags, convert RGB to hex if specified by options
98
+ new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
99
+
100
+ el[html_attr] = new_val
82
101
  end
102
+
83
103
  unless @options[:preserve_style_attribute]
84
104
  merged.instance_variable_get("@declarations").tap do |declarations|
85
- declarations.delete(css_att)
105
+ declarations.delete(css_attr)
86
106
  end
87
107
  end
88
108
  end
89
109
  end
110
+
90
111
  # Collapse multiple rules into one as much as possible.
91
112
  merged.create_shorthand! if @options[:create_shorthands]
92
113
 
@@ -94,7 +115,7 @@ class Premailer
94
115
  el['style'] = merged.declarations_to_s
95
116
  end
96
117
 
97
- doc = write_unmergable_css_rules(doc, @unmergable_rules)
118
+ doc = write_unmergable_css_rules(doc, @unmergable_rules) unless @options[:drop_unmergeable_css_rules]
98
119
 
99
120
  if @options[:remove_classes] or @options[:remove_comments]
100
121
  doc.traverse do |el|
@@ -112,13 +133,13 @@ class Premailer
112
133
  doc.search("a[@href^='#']").each do |el|
113
134
  target = el.get_attribute('href')[1..-1]
114
135
  targets << target
115
- el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
136
+ el.set_attribute('href', "#" + Digest::SHA256.hexdigest(target))
116
137
  end
117
138
  # hash ids that are links target, delete others
118
139
  doc.search("*[@id]").each do |el|
119
140
  id = el.get_attribute('id')
120
141
  if targets.include?(id)
121
- el.set_attribute('id', Digest::MD5.hexdigest(id))
142
+ el.set_attribute('id', Digest::SHA256.hexdigest(id))
122
143
  else
123
144
  el.remove_attribute('id')
124
145
  end
@@ -204,7 +225,7 @@ class Premailer
204
225
  @base_dir = File.dirname(input)
205
226
  thing = File.open(input, 'r')
206
227
  else
207
- thing = open(input)
228
+ thing = URI.open(input)
208
229
  end
209
230
 
210
231
  if thing.respond_to?(:read)
@@ -220,14 +241,7 @@ class Premailer
220
241
  thing.gsub! entity, replacement
221
242
  end
222
243
  end
223
- # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
224
- # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
225
- encoding = if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
226
- thing = thing.force_encoding(@options[:input_encoding]).encode!
227
- @options[:input_encoding]
228
- else
229
- @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
230
- end
244
+ encoding = @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
231
245
  doc = if @options[:html_fragment]
232
246
  ::Nokogiri::HTML.fragment(thing, encoding)
233
247
  else
@@ -67,28 +67,51 @@ class Premailer
67
67
 
68
68
  declarations = []
69
69
  style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
70
- rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
71
- declarations << rs
70
+ begin
71
+ rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
72
+ declarations << rs
73
+ rescue ArgumentError => e
74
+ raise e if @options[:rule_set_exceptions]
75
+ end
72
76
  end
73
77
 
74
78
  # Perform style folding
75
79
  merged = CssParser.merge(declarations)
76
- merged.expand_shorthand!
80
+ begin
81
+ merged.expand_shorthand!
82
+ rescue ArgumentError => e
83
+ raise e if @options[:rule_set_exceptions]
84
+ end
77
85
 
78
86
  # Duplicate CSS attributes as HTML attributes
79
87
  if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
80
- Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
81
- if el[html_att].nil? and not merged[css_att].empty?
82
- new_html_att = merged[css_att].gsub(/url\(['"](.*)['"]\)/, '\1').gsub(/;$|\s*!important/, '').strip
83
- el[html_att] = css_att.end_with?('color') && @options[:rgb_to_hex_attributes] ? ensure_hex(new_html_att) : new_html_att
88
+ Premailer::RELATED_ATTRIBUTES[el.name].each do |css_attr, html_attr|
89
+ if el[html_attr].nil? and not merged[css_attr].empty?
90
+ new_val = merged[css_attr].dup
91
+
92
+ # Remove url() function wrapper
93
+ new_val.gsub!(/url\((['"])(.*?)\1\)/, '\2')
94
+
95
+ # Remove !important, trailing semi-colon, and leading/trailing whitespace
96
+ new_val.gsub!(/;$|\s*!important/, '').strip!
97
+
98
+ # For width and height tags, remove px units
99
+ new_val.gsub!(/(\d+)px/, '\1') if %w[width height].include?(html_attr)
100
+
101
+ # For color-related tags, convert RGB to hex if specified by options
102
+ new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
103
+
104
+ el[html_attr] = new_val
84
105
  end
106
+
85
107
  unless @options[:preserve_style_attribute]
86
108
  merged.instance_variable_get("@declarations").tap do |declarations|
87
- declarations.delete(css_att)
109
+ declarations.delete(css_attr)
88
110
  end
89
111
  end
90
112
  end
91
113
  end
114
+
92
115
  # Collapse multiple rules into one as much as possible.
93
116
  merged.create_shorthand! if @options[:create_shorthands]
94
117
 
@@ -96,7 +119,7 @@ class Premailer
96
119
  el['style'] = merged.declarations_to_s
97
120
  end
98
121
 
99
- doc = write_unmergable_css_rules(doc, @unmergable_rules)
122
+ doc = write_unmergable_css_rules(doc, @unmergable_rules) unless @options[:drop_unmergeable_css_rules]
100
123
 
101
124
  if @options[:remove_classes] or @options[:remove_comments]
102
125
  doc.traverse do |el|
@@ -114,13 +137,13 @@ class Premailer
114
137
  doc.search("a[@href^='#']").each do |el|
115
138
  target = el.get_attribute('href')[1..-1]
116
139
  targets << target
117
- el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
140
+ el.set_attribute('href', "#" + Digest::SHA256.hexdigest(target))
118
141
  end
119
142
  # hash ids that are links target, delete others
120
143
  doc.search("*[@id]").each do |el|
121
144
  id = el.get_attribute('id')
122
145
  if targets.include?(id)
123
- el.set_attribute('id', Digest::MD5.hexdigest(id))
146
+ el.set_attribute('id', Digest::SHA256.hexdigest(id))
124
147
  else
125
148
  el.remove_attribute('id')
126
149
  end
@@ -206,7 +229,7 @@ class Premailer
206
229
  @base_dir = File.dirname(input)
207
230
  thing = File.open(input, 'r')
208
231
  else
209
- thing = open(input)
232
+ thing = URI.open(input)
210
233
  end
211
234
 
212
235
  if thing.respond_to?(:read)
@@ -222,14 +245,7 @@ class Premailer
222
245
  thing.gsub! entity, replacement
223
246
  end
224
247
  end
225
- # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
226
- # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
227
- encoding = if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
228
- thing = thing.force_encoding(@options[:input_encoding]).encode!
229
- @options[:input_encoding]
230
- else
231
- @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
232
- end
248
+ encoding = @options[:input_encoding] || (RUBY_PLATFORM == 'java' ? nil : 'BINARY')
233
249
  doc = if @options[:html_fragment]
234
250
  ::Nokogiri::HTML.fragment(thing, encoding)
235
251
  else
@@ -1,5 +1,3 @@
1
- require 'nokogumbo'
2
-
3
1
  class Premailer
4
2
  module Adapter
5
3
  # Nokogiri adapter
@@ -55,9 +53,7 @@ class Premailer
55
53
  end
56
54
 
57
55
  # Remove script tags
58
- if @options[:remove_scripts]
59
- doc.search("script").remove
60
- end
56
+ doc.search("script").remove if @options[:remove_scripts]
61
57
 
62
58
  # Read STYLE attributes and perform folding
63
59
  doc.search("*[@style]").each do |el|
@@ -65,28 +61,51 @@ class Premailer
65
61
 
66
62
  declarations = []
67
63
  style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
68
- rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
69
- declarations << rs
64
+ begin
65
+ rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
66
+ declarations << rs
67
+ rescue ArgumentError => e
68
+ raise e if @options[:rule_set_exceptions]
69
+ end
70
70
  end
71
71
 
72
72
  # Perform style folding
73
73
  merged = CssParser.merge(declarations)
74
- merged.expand_shorthand!
74
+ begin
75
+ merged.expand_shorthand!
76
+ rescue ArgumentError => e
77
+ raise e if @options[:rule_set_exceptions]
78
+ end
75
79
 
76
80
  # Duplicate CSS attributes as HTML attributes
77
81
  if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
78
- Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
79
- if el[html_att].nil? and not merged[css_att].empty?
80
- new_html_att = merged[css_att].gsub(/url\(['"](.*)['"]\)/, '\1').gsub(/;$|\s*!important/, '').strip
81
- el[html_att] = css_att.end_with?('color') && @options[:rgb_to_hex_attributes] ? ensure_hex(new_html_att) : new_html_att
82
+ Premailer::RELATED_ATTRIBUTES[el.name].each do |css_attr, html_attr|
83
+ if el[html_attr].nil? and not merged[css_attr].empty?
84
+ new_val = merged[css_attr].dup
85
+
86
+ # Remove url() function wrapper
87
+ new_val.gsub!(/url\((['"])(.*?)\1\)/, '\2')
88
+
89
+ # Remove !important, trailing semi-colon, and leading/trailing whitespace
90
+ new_val.gsub!(/;$|\s*!important/, '').strip!
91
+
92
+ # For width and height tags, remove px units
93
+ new_val.gsub!(/(\d+)px/, '\1') if %w[width height].include?(html_attr)
94
+
95
+ # For color-related tags, convert RGB to hex if specified by options
96
+ new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
97
+
98
+ el[html_attr] = new_val
82
99
  end
100
+
83
101
  unless @options[:preserve_style_attribute]
84
102
  merged.instance_variable_get("@declarations").tap do |declarations|
85
- declarations.delete(css_att)
103
+ declarations.delete(css_attr)
86
104
  end
87
105
  end
88
106
  end
89
107
  end
108
+
90
109
  # Collapse multiple rules into one as much as possible.
91
110
  merged.create_shorthand! if @options[:create_shorthands]
92
111
 
@@ -94,7 +113,7 @@ class Premailer
94
113
  el['style'] = merged.declarations_to_s
95
114
  end
96
115
 
97
- doc = write_unmergable_css_rules(doc, @unmergable_rules)
116
+ doc = write_unmergable_css_rules(doc, @unmergable_rules) unless @options[:drop_unmergeable_css_rules]
98
117
 
99
118
  if @options[:remove_classes] or @options[:remove_comments]
100
119
  doc.traverse do |el|
@@ -112,13 +131,13 @@ class Premailer
112
131
  doc.search("a[@href^='#']").each do |el|
113
132
  target = el.get_attribute('href')[1..-1]
114
133
  targets << target
115
- el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
134
+ el.set_attribute('href', "#" + Digest::SHA256.hexdigest(target))
116
135
  end
117
136
  # hash ids that are links target, delete others
118
137
  doc.search("*[@id]").each do |el|
119
138
  id = el.get_attribute('id')
120
139
  if targets.include?(id)
121
- el.set_attribute('id', Digest::MD5.hexdigest(id))
140
+ el.set_attribute('id', Digest::SHA256.hexdigest(id))
122
141
  else
123
142
  el.remove_attribute('id')
124
143
  end
@@ -204,7 +223,7 @@ class Premailer
204
223
  @base_dir = File.dirname(input)
205
224
  thing = File.open(input, 'r')
206
225
  else
207
- thing = open(input)
226
+ thing = URI.open(input)
208
227
  end
209
228
 
210
229
  if thing.respond_to?(:read)
@@ -220,15 +239,10 @@ class Premailer
220
239
  thing.gsub! entity, replacement
221
240
  end
222
241
  end
223
- # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
224
- # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
225
- if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
226
- thing = thing.force_encoding(@options[:input_encoding]).encode!
227
- end
228
242
  doc = if @options[:html_fragment]
229
- ::Nokogiri::HTML5(thing)
230
- else
231
243
  ::Nokogiri::HTML5.fragment(thing)
244
+ else
245
+ ::Nokogiri::HTML5(thing)
232
246
  end
233
247
 
234
248
  # Fix for removing any CDATA tags from both style and script tags inserted per
@@ -6,7 +6,13 @@ module HtmlToPlainText
6
6
 
7
7
  # Returns the text in UTF-8 format with all HTML tags removed
8
8
  #
9
+ # HTML content can be omitted from the output by surrounding it in the following comments:
10
+ #
11
+ # <!-- start text/html -->
12
+ # <!-- end text/html -->
13
+ #
9
14
  # TODO: add support for DL, OL
15
+ # TODO: this is not safe and needs a real html parser to work
10
16
  def convert_to_text(html, line_length = 65, from_charset = 'UTF-8')
11
17
  txt = html
12
18
 
@@ -21,23 +27,33 @@ module HtmlToPlainText
21
27
  # eg. the following formats:
22
28
  # <img alt="" />
23
29
  # <img alt="">
24
- txt.gsub!(/<img.+?alt=\"([^\"]*)\"[^>]*\>/i, '\1')
30
+ txt.gsub!(/<img[^>]+?alt="([^"]*)"[^>]*>/i, '\1')
25
31
 
26
32
  # for img tags with '' for attribute quotes
27
33
  # with or without closing tag
28
34
  # eg. the following formats:
29
35
  # <img alt='' />
30
36
  # <img alt=''>
31
- txt.gsub!(/<img.+?alt=\'([^\']*)\'[^>]*\>/i, '\1')
37
+ txt.gsub!(/<img[^>]+?alt='([^']*)'[^>]*>/i, '\1')
38
+
39
+ # remove script tags and content
40
+ txt.gsub!(/<script.*?\/script>/m, '')
32
41
 
33
42
  # links
34
- txt.gsub!(/<a\s[^\n]*?href=["'](mailto:)?([^"']*)["'][^>]*>(.*?)<\/a>/im) do |s|
35
- if $3.empty?
43
+ txt.gsub!(/<a\s+([^>]+)>(.*?)<\/a>/im) do |s|
44
+ text = $2.strip
45
+
46
+ match = /href=(['"])(?:mailto:)?(.+?)\1/.match(s)
47
+ if match
48
+ href = match[2]
49
+ end
50
+
51
+ if text.empty?
36
52
  ''
37
- elsif $3.strip.downcase == $2.strip.downcase
38
- $3.strip
53
+ elsif href.nil? || text.strip.downcase == href.strip.downcase
54
+ text.strip
39
55
  else
40
- $3.strip + ' ( ' + $2.strip + ' )'
56
+ text.strip + ' ( ' + href.strip + ' )'
41
57
  end
42
58
  end
43
59
 
@@ -56,12 +72,12 @@ module HtmlToPlainText
56
72
  hlength = line_length if hlength > line_length
57
73
 
58
74
  case hlevel
59
- when 1 # H1, asterisks above and below
60
- htext = ('*' * hlength) + "\n" + htext + "\n" + ('*' * hlength)
61
- when 2 # H1, dashes above and below
62
- htext = ('-' * hlength) + "\n" + htext + "\n" + ('-' * hlength)
63
- else # H3-H6, dashes below
64
- htext = htext + "\n" + ('-' * hlength)
75
+ when 1 # H1, asterisks above and below
76
+ htext = ('*' * hlength) + "\n" + htext + "\n" + ('*' * hlength)
77
+ when 2 # H1, dashes above and below
78
+ htext = ('-' * hlength) + "\n" + htext + "\n" + ('-' * hlength)
79
+ else # H3-H6, dashes below
80
+ htext = htext + "\n" + ('-' * hlength)
65
81
  end
66
82
 
67
83
  "\n\n" + htext + "\n\n"
@@ -151,14 +151,14 @@ class Premailer
151
151
  #
152
152
  # @param [Hash] options the options to handle html with.
153
153
  # @option options [Fixnum] :line_length Line length used by to_plain_text. Default is 65.
154
- # @option options [Fixnum] :warn_level What level of CSS compatibility warnings to show (see {Premailer::Warnings}).
154
+ # @option options [Fixnum] :warn_level What level of CSS compatibility warnings to show (see {Premailer::Warnings}, default is Warnings::SAFE).
155
155
  # @option options [String] :link_query_string A string to append to every <tt>a href=""</tt> link. Do not include the initial <tt>?</tt>.
156
156
  # @option options [String] :base_url Used to calculate absolute URLs for local files.
157
157
  # @option options [Array(String)] :css Manually specify CSS stylesheets.
158
- # @option options [Boolean] :css_to_attributes Copy related CSS attributes into HTML attributes (e.g. background-color to bgcolor)
158
+ # @option options [Boolean] :css_to_attributes Copy related CSS attributes into HTML attributes (e.g. background-color to bgcolor). Default is true.
159
159
  # @option options [Boolean] :preserve_style_attribute Preserve original style attribute
160
160
  # @option options [String] :css_string Pass CSS as a string
161
- # @option options [Boolean] :rgb_to_hex_attributes Convert RBG to Hex colors, default false
161
+ # @option options [Boolean] :rgb_to_hex_attributes Convert RBG to Hex colors. Default is false.
162
162
  # @option options [Boolean] :remove_ids Remove ID attributes whenever possible and convert IDs used as anchors to hashed to avoid collisions in webmail programs. Default is false.
163
163
  # @option options [Boolean] :remove_classes Remove class attributes. Default is false.
164
164
  # @option options [Boolean] :remove_comments Remove html comments. Default is false.
@@ -168,7 +168,8 @@ class Premailer
168
168
  # @option options [Boolean] :preserve_reset Whether to preserve styles associated with the MailChimp reset code. Default is true.
169
169
  # @option options [Boolean] :with_html_string Whether the html param should be treated as a raw string. Default is false.
170
170
  # @option options [Boolean] :verbose Whether to print errors and warnings to <tt>$stderr</tt>. Default is false.
171
- # @option options [Boolean] :io_exceptions Throws exceptions on I/O errors.
171
+ # @option options [Boolean] :io_exceptions Throws exceptions on I/O errors. Default is false.
172
+ # @option options [Boolean] :rule_set_exceptions Throws exceptions on invalid values in CSS Parser rule sets. Default is true.
172
173
  # @option options [Boolean] :include_link_tags Whether to include css from <tt>link rel=stylesheet</tt> tags. Default is true.
173
174
  # @option options [Boolean] :include_style_tags Whether to include css from <tt>style</tt> tags. Default is true.
174
175
  # @option options [String] :input_encoding Manually specify the source documents encoding. This is a good idea. Default is ASCII-8BIT.
@@ -178,6 +179,7 @@ class Premailer
178
179
  # @option options [String] :output_encoding Output encoding option for Nokogiri adapter. Should be set to "US-ASCII" to output HTML entities instead of Unicode characters.
179
180
  # @option options [Boolean] :create_shorthands Combine several properties into a shorthand one, e.g. font: style weight size. Default is true.
180
181
  # @option options [Boolean] :html_fragment Handle HTML fragment without any HTML content wrappers. Default is false.
182
+ # @option options [Boolean] :drop_unmergeable_css_rules Do not include unmergeable css rules in a <tt><style><tt> tag. Default is false.
181
183
  def initialize(html, options = {})
182
184
  @options = {:warn_level => Warnings::SAFE,
183
185
  :line_length => 65,
@@ -199,6 +201,7 @@ class Premailer
199
201
  :verbose => false,
200
202
  :debug => false,
201
203
  :io_exceptions => false,
204
+ :rule_set_exceptions => true,
202
205
  :include_link_tags => true,
203
206
  :include_style_tags => true,
204
207
  :input_encoding => 'ASCII-8BIT',
@@ -209,6 +212,7 @@ class Premailer
209
212
  :create_shorthands => true,
210
213
  :html_fragment => false,
211
214
  :adapter => Adapter.use,
215
+ :drop_unmergeable_css_rules => false
212
216
  }.merge(options)
213
217
 
214
218
  @html_file = html
@@ -231,12 +235,13 @@ class Premailer
231
235
  @css_parser = CssParser::Parser.new({
232
236
  :absolute_paths => true,
233
237
  :import => true,
234
- :io_exceptions => @options[:io_exceptions]
238
+ :io_exceptions => @options[:io_exceptions],
239
+ :rule_set_exceptions => @options[:rule_set_exceptions]
235
240
  })
236
241
 
237
242
  @adapter_class = Adapter.find @options[:adapter]
238
243
 
239
- self.class.send(:include, @adapter_class)
244
+ self.extend(@adapter_class)
240
245
 
241
246
  @doc = load_html(@html_file)
242
247
 
@@ -307,7 +312,7 @@ protected
307
312
  link_uri = File.join(File.dirname(@html_file), tag.attributes['href'].to_s.sub!(@base_url.to_s, ''))
308
313
  end
309
314
  # if the file does not exist locally, try to grab the remote reference
310
- unless File.exists?(link_uri)
315
+ unless File.exist?(link_uri)
311
316
  link_uri = Premailer.resolve_link(tag.attributes['href'].to_s, @html_file)
312
317
  end
313
318
  else
@@ -402,7 +407,7 @@ public
402
407
 
403
408
  # Check for an XHTML doctype
404
409
  def is_xhtml?
405
- intro = @doc.to_html.strip.split("\n")[0..2].join(' ')
410
+ intro = @doc.to_xhtml.strip.split("\n")[0..2].join(' ')
406
411
  is_xhtml = !!(intro =~ /w3c\/\/[\s]*dtd[\s]+xhtml/i)
407
412
  $stderr.puts "Is XHTML? #{is_xhtml.inspect}\nChecked:\n#{intro}" if @options[:debug]
408
413
  is_xhtml
@@ -497,18 +502,13 @@ public
497
502
  def self.canonicalize(uri) # :nodoc:
498
503
  u = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI.parse(uri.to_s)
499
504
  u.normalize!
500
- newpath = u.path
501
- while newpath.gsub!(%r{([^/]+)/\.\./?}) { |match|
502
- $1 == '..' ? match : ''
503
- } do end
504
- newpath = newpath.gsub(%r{/\./}, '/').sub(%r{/\.\z}, '/')
505
- u.path = newpath
506
- u.to_s
507
- end
505
+ u.to_s
506
+ end
508
507
 
509
508
  # Check <tt>CLIENT_SUPPORT_FILE</tt> for any CSS warnings
510
509
  def check_client_support # :nodoc:
511
- @client_support ||= YAML::load(File.open(CLIENT_SUPPORT_FILE))
510
+ kwargs = Psych::VERSION >= '4' ? { aliases: true } : {}
511
+ @client_support ||= Psych.load(File.open(CLIENT_SUPPORT_FILE), **kwargs)
512
512
 
513
513
  warnings = []
514
514
  properties = []
@@ -1,4 +1,4 @@
1
1
  class Premailer
2
2
  # Premailer version.
3
- VERSION = '1.11.1'.freeze
3
+ VERSION = '1.21.0'.freeze
4
4
  end
data/lib/premailer.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'yaml'
2
2
  require 'open-uri'
3
- require 'digest/md5'
3
+ require 'digest/sha2'
4
4
  require 'cgi'
5
5
  require 'addressable/uri'
6
6
  require 'css_parser'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: premailer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.1
4
+ version: 1.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dunae
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-14 00:00:00.000000000 Z
11
+ date: 2023-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: css_parser
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.6.0
19
+ version: 1.12.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.6.0
26
+ version: 1.12.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: htmlentities
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -92,28 +92,14 @@ dependencies:
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '1.7'
95
+ version: '1.13'
96
96
  type: :development
97
97
  prerelease: false
98
98
  version_requirements: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '1.7'
103
- - !ruby/object:Gem::Dependency
104
- name: yard
105
- requirement: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - ">="
108
- - !ruby/object:Gem::Version
109
- version: '0'
110
- type: :development
111
- prerelease: false
112
- version_requirements: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- version: '0'
102
+ version: '1.13'
117
103
  - !ruby/object:Gem::Dependency
118
104
  name: redcarpet
119
105
  requirement: !ruby/object:Gem::Requirement
@@ -171,7 +157,7 @@ dependencies:
171
157
  - !ruby/object:Gem::Version
172
158
  version: '0'
173
159
  - !ruby/object:Gem::Dependency
174
- name: nokogumbo
160
+ name: bump
175
161
  requirement: !ruby/object:Gem::Requirement
176
162
  requirements:
177
163
  - - ">="
@@ -207,7 +193,8 @@ files:
207
193
  - lib/premailer/version.rb
208
194
  - misc/client_support.yaml
209
195
  homepage: https://github.com/premailer/premailer
210
- licenses: []
196
+ licenses:
197
+ - BSD-3-Clause
211
198
  metadata:
212
199
  yard.run: yri
213
200
  post_install_message:
@@ -218,15 +205,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
218
205
  requirements:
219
206
  - - ">="
220
207
  - !ruby/object:Gem::Version
221
- version: 2.1.0
208
+ version: 2.7.0
222
209
  required_rubygems_version: !ruby/object:Gem::Requirement
223
210
  requirements:
224
211
  - - ">="
225
212
  - !ruby/object:Gem::Version
226
213
  version: '0'
227
214
  requirements: []
228
- rubyforge_project:
229
- rubygems_version: 2.6.12
215
+ rubygems_version: 3.1.6
230
216
  signing_key:
231
217
  specification_version: 4
232
218
  summary: Preflight for HTML e-mail.