premailer 1.11.1 → 1.21.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.
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.