premailer 1.23.0 → 1.27.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +49 -29
- data/bin/premailer +1 -1
- data/lib/premailer/adapter/nokogiri.rb +11 -8
- data/lib/premailer/adapter/nokogiri_fast.rb +12 -10
- data/lib/premailer/adapter/nokogumbo.rb +10 -8
- data/lib/premailer/adapter/rgb_to_hex.rb +1 -0
- data/lib/premailer/adapter.rb +4 -3
- data/lib/premailer/cached_rule_set.rb +1 -0
- data/lib/premailer/executor.rb +5 -4
- data/lib/premailer/html_to_plain_text.rb +4 -3
- data/lib/premailer/premailer.rb +22 -20
- data/lib/premailer/version.rb +2 -1
- data/lib/premailer.rb +1 -0
- metadata +21 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31108244f6afc74508eef1337a9b99adc7f2643b5412e2aacf808ecb8c520101
|
4
|
+
data.tar.gz: b2f10188d4b7143050eb86e04776c38fc220ebb834da7871e838fe47db5fe9c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4cd3655f0aa411da6d874dd50e2e1ab1c6f1cae4f9882cfa84a6ec2022174f5d9670d439527bb27618774f7f731f7ac24b35420c58926db159b4f81e85cb660d
|
7
|
+
data.tar.gz: aa583335ee3a7b0de99320aff3c162d233825c99fbfe287ae13f12790c4b54728b7b0460861f928efe9ddae07416aafb229058f698267214e8b740ff94ea20a8
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
For the best HTML e-mail delivery results, CSS should be inline. This is a
|
6
6
|
huge pain and a simple newsletter becomes un-managable very quickly. This
|
7
|
-
|
7
|
+
gem is a solution.
|
8
8
|
|
9
9
|
* CSS styles are converted to inline style attributes
|
10
10
|
- Checks `style` and `link[rel=stylesheet]` tags and preserves existing inline attributes
|
@@ -12,35 +12,26 @@ 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](
|
15
|
+
* A [plain text version](#plain-text-version) is created (optional)
|
16
16
|
|
17
17
|
## Installation
|
18
18
|
|
19
|
-
Install the Premailer gem from RubyGems.
|
20
|
-
|
21
19
|
```bash
|
22
20
|
gem install premailer
|
23
21
|
```
|
24
22
|
|
25
|
-
or add it to your `Gemfile` and run `bundle`.
|
26
|
-
|
27
23
|
## Example
|
28
24
|
|
29
25
|
```ruby
|
30
26
|
require 'premailer'
|
31
27
|
|
32
|
-
premailer = Premailer.new('http://example.com/myfile.html', :
|
28
|
+
premailer = Premailer.new('http://example.com/myfile.html', warn_level: Premailer::Warnings::SAFE)
|
33
29
|
|
34
|
-
# Write the plain-text output
|
35
|
-
|
36
|
-
File.open("output.txt", "w") do |fout|
|
37
|
-
fout.puts premailer.to_plain_text
|
38
|
-
end
|
30
|
+
# Write the plain-text output (must come before to_inline_css)
|
31
|
+
File.write "output.txt", premailer.to_plain_text
|
39
32
|
|
40
33
|
# Write the HTML output
|
41
|
-
File.
|
42
|
-
fout.puts premailer.to_inline_css
|
43
|
-
end
|
34
|
+
File.write "output.html", premailer.to_inline_css
|
44
35
|
|
45
36
|
# Output any CSS warnings
|
46
37
|
premailer.warnings.each do |w|
|
@@ -50,17 +41,13 @@ end
|
|
50
41
|
|
51
42
|
## Adapters
|
52
43
|
|
53
|
-
Premailer's default adapter is nokogiri if both nokogiri and nokogumbo are included in the Gemfile list. However, if you want to use a different adapter, you can choose to.
|
54
|
-
|
55
|
-
There are three adapters in total (as of premailer 1.10.0)
|
56
|
-
|
57
44
|
1. nokogiri (default)
|
58
|
-
2. nokogiri_fast
|
45
|
+
2. nokogiri_fast (20x speed, more memory)
|
59
46
|
3. nokogumbo
|
60
47
|
|
61
|
-
hpricot adapter removed
|
48
|
+
(hpricot adapter removed, use `~>1.9.0` version if you need it)
|
62
49
|
|
63
|
-
|
50
|
+
Picking an adapter:
|
64
51
|
|
65
52
|
```ruby
|
66
53
|
Premailer::Adapter.use = :nokogiri_fast
|
@@ -97,22 +84,56 @@ will result in
|
|
97
84
|
<table cellspacing='5' width='500'>
|
98
85
|
```
|
99
86
|
|
100
|
-
##
|
87
|
+
## Plain text version
|
88
|
+
|
89
|
+
Premailer can generate a plain text version of your HTML. Links and images will be inlined.
|
101
90
|
|
102
|
-
|
91
|
+
For example
|
103
92
|
|
104
|
-
|
93
|
+
```html
|
94
|
+
<a href="https://example.com" >
|
95
|
+
<img src="https://github.com/premailer.png" alt="Premailer Logo" />
|
96
|
+
</a>
|
97
|
+
```
|
105
98
|
|
99
|
+
will become
|
100
|
+
|
101
|
+
```text
|
102
|
+
Premailer Logo ( https://example.com )
|
103
|
+
```
|
104
|
+
|
105
|
+
To ignore/omit a section of HTML content from the plain text version, wrap it with the following comments.
|
106
|
+
|
107
|
+
```html
|
108
|
+
<!-- start text/html -->
|
109
|
+
<p>This will be omitted from the plain text version.</p>
|
110
|
+
<p>
|
111
|
+
This is extremely helpful for <strong>removing email headers and footers</strong>
|
112
|
+
that aren't needed in the text version.
|
113
|
+
</p>
|
114
|
+
<!-- end text/html -->
|
115
|
+
```
|
116
|
+
|
117
|
+
## Configuration options
|
118
|
+
|
119
|
+
For example:
|
106
120
|
```ruby
|
107
|
-
|
121
|
+
Premailer.new(
|
122
|
+
html, # html as string
|
123
|
+
with_html_string: true,
|
124
|
+
drop_unmergeable_css_rules: true
|
125
|
+
)
|
108
126
|
```
|
109
127
|
|
110
|
-
[
|
128
|
+
[available options](https://premailer.github.io/premailer/Premailer.html#initialize-instance_method)
|
111
129
|
|
112
130
|
|
113
131
|
## Contributions
|
114
132
|
|
115
|
-
Contributions are most welcome.
|
133
|
+
Contributions are most welcome.
|
134
|
+
Premailer was rotting away in a private SVN repository for too long and could use some TLC.
|
135
|
+
Fork and patch to your heart's content.
|
136
|
+
Please don't increment the version numbers.
|
116
137
|
|
117
138
|
A few areas that are particularly in need of love:
|
118
139
|
|
@@ -129,4 +150,3 @@ and to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the w
|
|
129
150
|
The source code can be found on [GitHub](https://github.com/premailer/premailer).
|
130
151
|
|
131
152
|
Copyright by Alex Dunae (dunae.ca, e-mail 'code' at the same domain), 2007-2017. See [LICENSE.md](https://github.com/premailer/premailer/blob/master/LICENSE.md) for license details.
|
132
|
-
|
data/bin/premailer
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'nokogiri'
|
2
3
|
|
3
4
|
class Premailer
|
4
5
|
module Adapter
|
5
6
|
# Nokogiri adapter
|
6
7
|
module Nokogiri
|
8
|
+
WIDTH_AND_HIGHT = ['width', 'height'].freeze
|
7
9
|
|
8
10
|
include AdapterHelper::RgbToHex
|
9
11
|
# Merge CSS into the HTML document.
|
@@ -27,13 +29,13 @@ class Premailer
|
|
27
29
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
28
30
|
|
29
31
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
30
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
32
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
31
33
|
else
|
32
34
|
begin
|
33
|
-
if
|
35
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
34
36
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
35
37
|
# however, this doesn't mean for testing pur
|
36
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
38
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
37
39
|
end
|
38
40
|
|
39
41
|
# Change single ID CSS selectors into xpath so that we can match more
|
@@ -61,8 +63,8 @@ class Premailer
|
|
61
63
|
doc.search("*[@style]").each do |el|
|
62
64
|
style = el.attributes['style'].to_s
|
63
65
|
|
64
|
-
declarations = style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).filter_map do |declaration|
|
65
|
-
rs = Premailer::CachedRuleSet.new(
|
66
|
+
declarations = style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).filter_map do |declaration|
|
67
|
+
rs = Premailer::CachedRuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
66
68
|
rs.expand_shorthand!
|
67
69
|
rs
|
68
70
|
rescue ArgumentError => e
|
@@ -90,7 +92,7 @@ class Premailer
|
|
90
92
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
91
93
|
|
92
94
|
# For width and height tags, remove px units
|
93
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
95
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HIGHT.include?(html_attr)
|
94
96
|
|
95
97
|
# For color-related tags, convert RGB to hex if specified by options
|
96
98
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
@@ -99,7 +101,7 @@ class Premailer
|
|
99
101
|
end
|
100
102
|
|
101
103
|
unless @options[:preserve_style_attribute]
|
102
|
-
merged.instance_variable_get(
|
104
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
103
105
|
declarations.delete(css_attr)
|
104
106
|
end
|
105
107
|
end
|
@@ -235,6 +237,7 @@ class Premailer
|
|
235
237
|
|
236
238
|
# Handle HTML entities
|
237
239
|
if @options[:replace_html_entities] == true and thing.is_a?(String)
|
240
|
+
thing = +thing
|
238
241
|
HTML_ENTITIES.map do |entity, replacement|
|
239
242
|
thing.gsub! entity, replacement
|
240
243
|
end
|
@@ -249,7 +252,7 @@ class Premailer
|
|
249
252
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
250
253
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
251
254
|
# https://github.com/premailer/premailer/issues/199
|
252
|
-
|
255
|
+
['style', 'script'].each do |tag|
|
253
256
|
doc.search(tag).children.each do |child|
|
254
257
|
child.swap(child.text()) if child.cdata?
|
255
258
|
end
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'nokogiri'
|
2
3
|
|
3
4
|
class Premailer
|
4
5
|
module Adapter
|
5
6
|
# NokogiriFast adapter
|
6
7
|
module NokogiriFast
|
8
|
+
WIDTH_AND_HEIGHT = ['width', 'height'].freeze
|
7
9
|
|
8
10
|
include AdapterHelper::RgbToHex
|
9
11
|
# Merge CSS into the HTML document.
|
@@ -33,13 +35,13 @@ class Premailer
|
|
33
35
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
34
36
|
|
35
37
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
36
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
38
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
37
39
|
else
|
38
40
|
begin
|
39
|
-
if
|
41
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
40
42
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
41
43
|
# however, this doesn't mean for testing pur
|
42
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
44
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
43
45
|
end
|
44
46
|
|
45
47
|
# Try the new index based technique. If not supported, fall back to the old brute force one.
|
@@ -66,9 +68,9 @@ class Premailer
|
|
66
68
|
style = el.attributes['style'].to_s
|
67
69
|
|
68
70
|
declarations = []
|
69
|
-
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
71
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).each do |declaration|
|
70
72
|
begin
|
71
|
-
rs = CssParser::RuleSet.new(
|
73
|
+
rs = CssParser::RuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
72
74
|
declarations << rs
|
73
75
|
rescue ArgumentError => e
|
74
76
|
raise e if @options[:rule_set_exceptions]
|
@@ -96,7 +98,7 @@ class Premailer
|
|
96
98
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
97
99
|
|
98
100
|
# For width and height tags, remove px units
|
99
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
101
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HEIGHT.include?(html_attr)
|
100
102
|
|
101
103
|
# For color-related tags, convert RGB to hex if specified by options
|
102
104
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
@@ -105,7 +107,7 @@ class Premailer
|
|
105
107
|
end
|
106
108
|
|
107
109
|
unless @options[:preserve_style_attribute]
|
108
|
-
merged.instance_variable_get(
|
110
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
109
111
|
declarations.delete(css_attr)
|
110
112
|
end
|
111
113
|
end
|
@@ -255,7 +257,7 @@ class Premailer
|
|
255
257
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
256
258
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
257
259
|
# https://github.com/premailer/premailer/issues/199
|
258
|
-
|
260
|
+
['style', 'script'].each do |tag|
|
259
261
|
doc.search(tag).children.each do |child|
|
260
262
|
child.swap(child.text()) if child.cdata?
|
261
263
|
end
|
@@ -314,7 +316,7 @@ class Premailer
|
|
314
316
|
index.default = Set.new
|
315
317
|
descendants.default = Set.new
|
316
318
|
|
317
|
-
|
319
|
+
[index, Set.new(all_nodes), descendants]
|
318
320
|
end
|
319
321
|
|
320
322
|
# @param doc The top level document
|
@@ -354,7 +356,7 @@ class Premailer
|
|
354
356
|
# It will return nil when such a selector is passed, so you can take
|
355
357
|
# action on the falsity of the return value.
|
356
358
|
def match_selector(index, all_nodes, descendants, selector)
|
357
|
-
if /[^-a-zA-Z0-9_\s.#]/.match(selector) then
|
359
|
+
if /[^-a-zA-Z0-9_\s.#]/.match?(selector) then
|
358
360
|
return nil
|
359
361
|
end
|
360
362
|
|
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Premailer
|
2
3
|
module Adapter
|
3
4
|
# Nokogiri adapter
|
4
5
|
module Nokogumbo
|
6
|
+
WIDTH_AND_HEIGHT = ['width', 'height'].freeze
|
5
7
|
|
6
8
|
include AdapterHelper::RgbToHex
|
7
9
|
# Merge CSS into the HTML document.
|
@@ -25,13 +27,13 @@ class Premailer
|
|
25
27
|
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
26
28
|
|
27
29
|
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
28
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
30
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
|
29
31
|
else
|
30
32
|
begin
|
31
|
-
if
|
33
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector)
|
32
34
|
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
33
35
|
# however, this doesn't mean for testing pur
|
34
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
36
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration)) unless !@options[:preserve_reset]
|
35
37
|
end
|
36
38
|
|
37
39
|
# Change single ID CSS selectors into xpath so that we can match more
|
@@ -60,9 +62,9 @@ class Premailer
|
|
60
62
|
style = el.attributes['style'].to_s
|
61
63
|
|
62
64
|
declarations = []
|
63
|
-
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
65
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/m).each do |declaration|
|
64
66
|
begin
|
65
|
-
rs = CssParser::RuleSet.new(
|
67
|
+
rs = CssParser::RuleSet.new(block: declaration[1].to_s, specificity: declaration[0].to_i)
|
66
68
|
declarations << rs
|
67
69
|
rescue ArgumentError => e
|
68
70
|
raise e if @options[:rule_set_exceptions]
|
@@ -90,7 +92,7 @@ class Premailer
|
|
90
92
|
new_val.gsub!(/;$|\s*!important/, '').strip!
|
91
93
|
|
92
94
|
# For width and height tags, remove px units
|
93
|
-
new_val.gsub!(/(\d+)px/, '\1') if
|
95
|
+
new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HEIGHT.include?(html_attr)
|
94
96
|
|
95
97
|
# For color-related tags, convert RGB to hex if specified by options
|
96
98
|
new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
|
@@ -99,7 +101,7 @@ class Premailer
|
|
99
101
|
end
|
100
102
|
|
101
103
|
unless @options[:preserve_style_attribute]
|
102
|
-
merged.instance_variable_get(
|
104
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
103
105
|
declarations.delete(css_attr)
|
104
106
|
end
|
105
107
|
end
|
@@ -248,7 +250,7 @@ class Premailer
|
|
248
250
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
249
251
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
250
252
|
# https://github.com/premailer/premailer/issues/199
|
251
|
-
|
253
|
+
['style', 'script'].each do |tag|
|
252
254
|
doc.search(tag).children.each do |child|
|
253
255
|
child.swap(child.text()) if child.cdata?
|
254
256
|
end
|
data/lib/premailer/adapter.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
class Premailer
|
2
3
|
# Manages the adapter classes. Currently supports:
|
3
4
|
#
|
@@ -14,7 +15,7 @@ class Premailer
|
|
14
15
|
REQUIREMENT_MAP = [
|
15
16
|
["nokogiri", :nokogiri],
|
16
17
|
["nokogiri", :nokogiri_fast],
|
17
|
-
["nokogumbo", :nokogumbo]
|
18
|
+
["nokogumbo", :nokogumbo]
|
18
19
|
]
|
19
20
|
|
20
21
|
# Returns the adapter to use.
|
@@ -42,7 +43,7 @@ class Premailer
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
raise
|
46
|
+
raise "No suitable adapter for Premailer was found, please install nokogiri or nokogumbo"
|
46
47
|
end
|
47
48
|
|
48
49
|
# Sets the adapter to use.
|
@@ -56,7 +57,7 @@ class Premailer
|
|
56
57
|
def self.find(adapter)
|
57
58
|
return adapter if adapter.is_a?(Module)
|
58
59
|
|
59
|
-
Premailer::Adapter.const_get("#{adapter.to_s.split('_').map{|s| s.capitalize}.join
|
60
|
+
Premailer::Adapter.const_get("#{adapter.to_s.split('_').map{|s| s.capitalize}.join}")
|
60
61
|
rescue NameError
|
61
62
|
raise ArgumentError, "Invalid adapter: #{adapter}"
|
62
63
|
end
|
data/lib/premailer/executor.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'optparse'
|
2
3
|
require 'premailer'
|
3
4
|
|
@@ -8,12 +9,12 @@ options = {
|
|
8
9
|
:remove_classes => false,
|
9
10
|
:verbose => false,
|
10
11
|
:line_length => 65,
|
11
|
-
:adapter => :nokogiri
|
12
|
+
:adapter => :nokogiri
|
12
13
|
}
|
13
14
|
|
14
15
|
mode = :html
|
15
16
|
|
16
|
-
|
17
|
+
parser = OptionParser.new do |opts|
|
17
18
|
opts.banner = "Improve the rendering of HTML emails by making CSS inline among other things. Takes a path to a local file, a URL or a pipe as input.\n\n"
|
18
19
|
opts.define_head "Usage: premailer <optional uri|optional path> [options]"
|
19
20
|
opts.separator ""
|
@@ -79,7 +80,7 @@ opts = OptionParser.new do |opts|
|
|
79
80
|
exit
|
80
81
|
end
|
81
82
|
end
|
82
|
-
|
83
|
+
parser.parse!
|
83
84
|
|
84
85
|
$stderr.puts "Processing in #{mode} mode with options #{options.inspect}" if options[:verbose]
|
85
86
|
|
@@ -98,7 +99,7 @@ end
|
|
98
99
|
if input
|
99
100
|
premailer = Premailer.new(input, options)
|
100
101
|
else
|
101
|
-
puts
|
102
|
+
puts parser
|
102
103
|
exit 1
|
103
104
|
end
|
104
105
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'htmlentities'
|
3
4
|
|
4
5
|
# Support functions for Premailer
|
@@ -13,8 +14,8 @@ module HtmlToPlainText
|
|
13
14
|
#
|
14
15
|
# TODO: add support for DL, OL
|
15
16
|
# TODO: this is not safe and needs a real html parser to work
|
16
|
-
def convert_to_text(html, line_length = 65,
|
17
|
-
txt = html
|
17
|
+
def convert_to_text(html, line_length = 65, _from_charset = 'UTF-8')
|
18
|
+
txt = +html
|
18
19
|
|
19
20
|
# strip text ignored html. Useful for removing
|
20
21
|
# headers and footers that aren't needed in the
|
@@ -50,7 +51,7 @@ module HtmlToPlainText
|
|
50
51
|
|
51
52
|
if text.empty?
|
52
53
|
''
|
53
|
-
elsif href.nil? || text.strip.
|
54
|
+
elsif href.nil? || text.strip.casecmp(href.strip) == 0
|
54
55
|
text.strip
|
55
56
|
else
|
56
57
|
text.strip + ' ( ' + href.strip + ' )'
|
data/lib/premailer/premailer.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# Premailer processes HTML and CSS to improve e-mail deliverability.
|
2
3
|
#
|
3
4
|
# Premailer's main function is to render all CSS as inline <tt>style</tt>
|
@@ -15,7 +16,7 @@
|
|
15
16
|
# fout.close
|
16
17
|
#
|
17
18
|
# # Write the plain-text output
|
18
|
-
# fout = File.open("
|
19
|
+
# fout = File.open("output.txt", "w")
|
19
20
|
# fout.puts premailer.to_plain_text
|
20
21
|
# fout.close
|
21
22
|
#
|
@@ -141,7 +142,7 @@ class Premailer
|
|
141
142
|
include Warnings
|
142
143
|
|
143
144
|
# Waning level names
|
144
|
-
WARN_LABEL =
|
145
|
+
WARN_LABEL = ['NONE', 'SAFE', 'POOR', 'RISKY']
|
145
146
|
|
146
147
|
# Create a new Premailer object.
|
147
148
|
#
|
@@ -212,8 +213,7 @@ class Premailer
|
|
212
213
|
:create_shorthands => true,
|
213
214
|
:html_fragment => false,
|
214
215
|
:adapter => Adapter.use,
|
215
|
-
:drop_unmergeable_css_rules => false
|
216
|
-
}.merge(options)
|
216
|
+
:drop_unmergeable_css_rules => false}.merge(options)
|
217
217
|
|
218
218
|
@html_file = html
|
219
219
|
@is_local_file = @options[:with_html_string] || Premailer.local_data?(html)
|
@@ -264,11 +264,11 @@ class Premailer
|
|
264
264
|
|
265
265
|
protected
|
266
266
|
def load_css_from_local_file!(path)
|
267
|
-
css_block = ''
|
268
|
-
path.
|
267
|
+
css_block = +''
|
268
|
+
path.delete_prefix!('file:')
|
269
269
|
begin
|
270
270
|
File.open(path, "r") do |file|
|
271
|
-
while line = file.gets
|
271
|
+
while (line = file.gets)
|
272
272
|
css_block << line
|
273
273
|
end
|
274
274
|
end
|
@@ -355,11 +355,12 @@ public
|
|
355
355
|
media_types.split(/[\s]+|,/).any? { |media_type| media_type.strip =~ /screen|handheld|all/i }
|
356
356
|
end
|
357
357
|
|
358
|
-
def append_query_string(doc,
|
359
|
-
return doc if
|
358
|
+
def append_query_string(doc, queries)
|
359
|
+
return doc if queries.nil?
|
360
360
|
|
361
|
-
|
362
|
-
|
361
|
+
queries = +queries
|
362
|
+
queries.to_s.gsub!(/^[\?]*/, '').strip!
|
363
|
+
return doc if queries.empty?
|
363
364
|
|
364
365
|
begin
|
365
366
|
current_host = @base_url.host
|
@@ -367,18 +368,18 @@ public
|
|
367
368
|
current_host = nil
|
368
369
|
end
|
369
370
|
|
370
|
-
$stderr.puts "Attempting to append_query_string: #{
|
371
|
+
$stderr.puts "Attempting to append_query_string: #{queries}" if @options[:verbose]
|
371
372
|
|
372
|
-
doc.search('a').each do|el|
|
373
|
+
doc.search('a').each do |el|
|
373
374
|
href = el.attributes['href'].to_s.strip
|
374
375
|
next if href.nil? or href.empty?
|
375
376
|
|
376
|
-
next if
|
377
|
+
next if /[\#\{\[\<\%]/.match?(href[0,1]) # don't bother with anchors or special-looking links
|
377
378
|
|
378
379
|
begin
|
379
380
|
href = Addressable::URI.parse(href)
|
380
381
|
|
381
|
-
if current_host and href.host
|
382
|
+
if current_host and !href.host.nil? and href.host != current_host
|
382
383
|
$stderr.puts "Skipping append_query_string for: #{href.to_s} because host is no good" if @options[:verbose]
|
383
384
|
next
|
384
385
|
end
|
@@ -390,9 +391,9 @@ public
|
|
390
391
|
|
391
392
|
if href.query and not href.query.empty?
|
392
393
|
amp = @options[:unescaped_ampersand] ? '&' : '&'
|
393
|
-
href.query = href.query + amp +
|
394
|
+
href.query = href.query + amp + queries
|
394
395
|
else
|
395
|
-
href.query =
|
396
|
+
href.query = queries
|
396
397
|
end
|
397
398
|
|
398
399
|
el['href'] = href.to_s
|
@@ -435,11 +436,11 @@ public
|
|
435
436
|
tags.each do |tag|
|
436
437
|
# skip links that look like they have merge tags
|
437
438
|
# and mailto, ftp, etc...
|
438
|
-
if
|
439
|
+
if /^([\%\<\{\#\[]|data:|tel:|file:|sms:|callto:|facetime:|mailto:|ftp:|gopher:|cid:)/i.match?(tag.attributes[attribute].to_s)
|
439
440
|
next
|
440
441
|
end
|
441
442
|
|
442
|
-
if tag.attributes[attribute].to_s
|
443
|
+
if /^http/i.match?(tag.attributes[attribute].to_s)
|
443
444
|
begin
|
444
445
|
merged = Addressable::URI.parse(tag.attributes[attribute])
|
445
446
|
rescue; next; end
|
@@ -473,9 +474,10 @@ public
|
|
473
474
|
|
474
475
|
# @private
|
475
476
|
def self.resolve_link(path, base_path) # :nodoc:
|
477
|
+
path = +path
|
476
478
|
path.strip!
|
477
479
|
resolved = nil
|
478
|
-
if
|
480
|
+
if /\A(?:(https?|ftp|file):)\/\//i.match?(path)
|
479
481
|
resolved = path
|
480
482
|
Premailer.canonicalize(resolved)
|
481
483
|
elsif base_path.kind_of?(Addressable::URI)
|
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.27.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: 2024-
|
11
|
+
date: 2024-08-26 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.19.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.19.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: htmlentities
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,7 +129,7 @@ dependencies:
|
|
129
129
|
- !ruby/object:Gem::Version
|
130
130
|
version: '0'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
132
|
+
name: webmock
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - ">="
|
@@ -143,7 +143,7 @@ dependencies:
|
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0'
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
146
|
+
name: bump
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - ">="
|
@@ -157,7 +157,21 @@ dependencies:
|
|
157
157
|
- !ruby/object:Gem::Version
|
158
158
|
version: '0'
|
159
159
|
- !ruby/object:Gem::Dependency
|
160
|
-
name:
|
160
|
+
name: rubocop
|
161
|
+
requirement: !ruby/object:Gem::Requirement
|
162
|
+
requirements:
|
163
|
+
- - "~>"
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 1.62.1
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - "~>"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: 1.62.1
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: rubocop-performance
|
161
175
|
requirement: !ruby/object:Gem::Requirement
|
162
176
|
requirements:
|
163
177
|
- - ">="
|