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 +5 -5
- data/README.md +17 -3
- data/lib/premailer/adapter/nokogiri.rb +37 -23
- data/lib/premailer/adapter/nokogiri_fast.rb +36 -20
- data/lib/premailer/adapter/nokogumbo.rb +38 -24
- data/lib/premailer/html_to_plain_text.rb +29 -13
- data/lib/premailer/premailer.rb +17 -17
- data/lib/premailer/version.rb +1 -1
- data/lib/premailer.rb +1 -1
- metadata +11 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 47eb32064c7ed2912f426d2666ceb0aba0bd7a7f9c235bbc20e86bf961374688
|
4
|
+
data.tar.gz: 84dc0c5fa0331965aaa552f542d05bc2165cc140b98b25826fa7eb4e0470167e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3cc088f586ad72db34ed8cf8a44f7b9cdffd4ce1ccbea2009160f228364683f64b7e4891b914c8db4d4c04d3603b792648800912eff6523650c99c5702af17d2
|
7
|
+
data.tar.gz: 023ecd473bd37bf98b7f9e551bda5f7daf494e4f56bad458aa66d01d9e30e7647d514b13c7560aae97f3b06a00c79228a765cc4698a838d1575adc1ef1b673a6
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Premailer README [](https://github.com/premailer/premailer/actions/workflows/actions.yml) [](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
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
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 |
|
79
|
-
if el[
|
80
|
-
|
81
|
-
|
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
|
-
|
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::
|
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::
|
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
|
-
|
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
|
-
|
71
|
-
|
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
|
-
|
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 |
|
81
|
-
if el[
|
82
|
-
|
83
|
-
|
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
|
-
|
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::
|
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::
|
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
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
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 |
|
79
|
-
if el[
|
80
|
-
|
81
|
-
|
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
|
-
|
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::
|
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::
|
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
|
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
|
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
|
35
|
-
|
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
|
38
|
-
|
53
|
+
elsif href.nil? || text.strip.downcase == href.strip.downcase
|
54
|
+
text.strip
|
39
55
|
else
|
40
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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"
|
data/lib/premailer/premailer.rb
CHANGED
@@ -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
|
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.
|
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.
|
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.
|
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
|
-
|
501
|
-
|
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
|
-
|
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 = []
|
data/lib/premailer/version.rb
CHANGED
data/lib/premailer.rb
CHANGED
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.
|
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:
|
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.
|
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.
|
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.
|
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.
|
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:
|
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.
|
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
|
-
|
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.
|