premailer 1.12.0 → 1.29.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 +4 -4
- data/README.md +128 -26
- data/bin/premailer +1 -1
- data/lib/premailer/adapter/nokogiri.rb +64 -51
- data/lib/premailer/adapter/nokogiri_fast.rb +67 -53
- data/lib/premailer/adapter/nokogumbo.rb +58 -46
- data/lib/premailer/adapter/rgb_to_hex.rb +11 -9
- data/lib/premailer/adapter.rb +10 -13
- data/lib/premailer/cached_rule_set.rb +13 -0
- data/lib/premailer/executor.rb +12 -11
- data/lib/premailer/html_to_plain_text.rb +34 -34
- data/lib/premailer/premailer.rb +170 -135
- data/lib/premailer/version.rb +2 -1
- data/lib/premailer.rb +3 -1
- metadata +16 -148
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa4f38aaadca592e45c38537b54ad6e88bd15c0c43c07efed0beb25af8194766
|
|
4
|
+
data.tar.gz: cde54fad4ac9397bd09b88509aa2b5a22daaa1721172a7c98e0e78c94e8707ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc0358601f237a6ec2cf74764dfb28e5d800cf1ca486d88ca44a7e4d3913ade8ff00b76b98017fa6f565f5812bd6f9c3e66be819708a2764a70fca051458f09f
|
|
7
|
+
data.tar.gz: 9ea81979f54bd5fd9d8edb2e390e5900d53b7462064bc1465999dba639218db5a479c74e93e6e179d6d6a51abbf066685a21b033d5003899ab037a021db385f7
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
|
|
|
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
|
|
@@ -68,7 +55,7 @@ Premailer::Adapter.use = :nokogiri_fast
|
|
|
68
55
|
|
|
69
56
|
## Ruby Compatibility
|
|
70
57
|
|
|
71
|
-
|
|
58
|
+
See .github/workflows/actions.yml for which ruby versions are tested. JRuby support is close, contributors are welcome.
|
|
72
59
|
|
|
73
60
|
## Premailer-specific CSS
|
|
74
61
|
|
|
@@ -97,9 +84,125 @@ will result in
|
|
|
97
84
|
<table cellspacing='5' width='500'>
|
|
98
85
|
```
|
|
99
86
|
|
|
87
|
+
## Plain text version
|
|
88
|
+
|
|
89
|
+
Premailer can generate a plain text version of your HTML. Links and images will be inlined.
|
|
90
|
+
|
|
91
|
+
For example
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<a href="https://example.com" >
|
|
95
|
+
<img src="https://github.com/premailer.png" alt="Premailer Logo" />
|
|
96
|
+
</a>
|
|
97
|
+
```
|
|
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:
|
|
120
|
+
```ruby
|
|
121
|
+
Premailer.new(
|
|
122
|
+
html, # html as string
|
|
123
|
+
with_html_string: true,
|
|
124
|
+
drop_unmergeable_css_rules: true
|
|
125
|
+
)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
[available options](https://premailer.github.io/premailer/Premailer.html#initialize-instance_method)
|
|
129
|
+
|
|
130
|
+
## Support for CSS variables
|
|
131
|
+
|
|
132
|
+
The gem does not automatically replace CSS variables with their static values.
|
|
133
|
+
|
|
134
|
+
For example, if a variable is used to set the `font-weight` of an `h1` element, the result will be
|
|
135
|
+
```html
|
|
136
|
+
<h1 style="
|
|
137
|
+
font-size:3rem;
|
|
138
|
+
font-weight:var(--bulma-content-heading-weight);">
|
|
139
|
+
Title</h1>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This causes the `font-weight` value to be the CSS variable call `var(--bulma-content-heading-weight);` instead of its static value.
|
|
143
|
+
|
|
144
|
+
### Replace CSS variable calls with their static values
|
|
145
|
+
|
|
146
|
+
The following section instructs how to replace CSS variables with their static value in the context of a Ruby on Rails application.
|
|
147
|
+
|
|
148
|
+
Install the `postcss-css-variables` plugin for PostCSS to process the CSS variables.
|
|
149
|
+
|
|
150
|
+
```shell
|
|
151
|
+
yarn add postcss postcss-cli postcss-css-variables
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
To configure the plugin, create the file `postcss.config.js` in the root directory with the content:
|
|
155
|
+
|
|
156
|
+
```javascript
|
|
157
|
+
module.exports = {
|
|
158
|
+
plugins: [
|
|
159
|
+
// https://github.com/MadLittleMods/postcss-css-variables to transform the css
|
|
160
|
+
require("postcss-css-variables")({
|
|
161
|
+
preserve: false, // Set to false to replace variables with static values
|
|
162
|
+
}),
|
|
163
|
+
],
|
|
164
|
+
};
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
In the `package.json` file, add the new "build:emails" to the scripts.<br>Replace `./app/assets/stylesheets/emails.css` with your file path:
|
|
168
|
+
```json
|
|
169
|
+
"scripts": {
|
|
170
|
+
"build:emails": "postcss ./app/assets/stylesheets/emails.css -o ./app/assets/builds/emails.css"
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The previous script processes and overwrites the file at `./app/assets/stylesheets/emails.css` with PostCSS using its `postcss-css-variables` plugin, replacing the CSS variables with their static value.
|
|
175
|
+
|
|
176
|
+
If the file to be processed is not `.css`, but `.scss`, it needs to be converted first to `.css`, then have its variables replaced. The script would then be
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
"scripts": {
|
|
180
|
+
"build:emails": "sass ./app/assets/stylesheets/emails.scss:./app/assets/builds/emails.css --no-source-map --load-path=node_modules && postcss ./app/assets/builds/emails.css -o ./app/assets/builds/emails.css"
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Next, to execute the script when running `bin/dev`, add the following line in the file `Procfile.dev`
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
emails_css: yarn build:emails --watch
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
The srcipt can also be executed separately with the command
|
|
191
|
+
|
|
192
|
+
```shell
|
|
193
|
+
yarn build:emails
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Caveat
|
|
197
|
+
|
|
198
|
+
The variables must be declared before use. Otherwise, their values when called will be set to `undefined`.
|
|
199
|
+
|
|
100
200
|
## Contributions
|
|
101
201
|
|
|
102
|
-
Contributions are most welcome.
|
|
202
|
+
Contributions are most welcome.
|
|
203
|
+
Premailer was rotting away in a private SVN repository for too long and could use some TLC.
|
|
204
|
+
Fork and patch to your heart's content.
|
|
205
|
+
Please don't increment the version numbers.
|
|
103
206
|
|
|
104
207
|
A few areas that are particularly in need of love:
|
|
105
208
|
|
|
@@ -116,4 +219,3 @@ and to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the w
|
|
|
116
219
|
The source code can be found on [GitHub](https://github.com/premailer/premailer).
|
|
117
220
|
|
|
118
221
|
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.
|
|
119
|
-
|
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.
|
|
@@ -21,19 +23,19 @@ class Premailer
|
|
|
21
23
|
# Iterate through the rules and merge them into the HTML
|
|
22
24
|
@css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
|
|
23
25
|
# Save un-mergable rules separately
|
|
24
|
-
selector.gsub!(/:link([\s]*)+/i) { |
|
|
26
|
+
selector.gsub!(/:link([\s]*)+/i) { |_m| $1 }
|
|
25
27
|
|
|
26
28
|
# Convert element names to lower case
|
|
27
|
-
selector.gsub!(/([\s]|^)([\w]+)/) { |
|
|
29
|
+
selector.gsub!(/([\s]|^)([\w]+)/) { |_m| $1.to_s + $2.to_s.downcase }
|
|
28
30
|
|
|
29
|
-
if Premailer.
|
|
30
|
-
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
|
31
|
+
if Premailer.media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
|
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 selector
|
|
35
|
+
if Premailer::RE_RESET_SELECTORS.match?(selector) && !!@options[:preserve_reset]
|
|
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))
|
|
38
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration))
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
# Change single ID CSS selectors into xpath so that we can match more
|
|
@@ -41,52 +43,71 @@ class Premailer
|
|
|
41
43
|
selector.gsub!(/\A\#([\w_\-]+)\Z/, '*[@id=\1]')
|
|
42
44
|
|
|
43
45
|
doc.search(selector).each do |el|
|
|
44
|
-
if el.elem?
|
|
46
|
+
if el.elem? && ((el.name != 'head') && (el.parent.name != 'head'))
|
|
45
47
|
# Add a style attribute or append to the existing one
|
|
46
48
|
block = "[SPEC=#{specificity}[#{declaration}]]"
|
|
47
49
|
el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block
|
|
48
50
|
end
|
|
49
51
|
end
|
|
50
52
|
rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError
|
|
51
|
-
|
|
53
|
+
warn "CSS syntax error with selector: #{selector}" if @options[:verbose]
|
|
52
54
|
next
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
# Remove script tags
|
|
58
|
-
if @options[:remove_scripts]
|
|
59
|
-
doc.search("script").remove
|
|
60
|
-
end
|
|
60
|
+
doc.search("script").remove if @options[:remove_scripts]
|
|
61
61
|
|
|
62
62
|
# Read STYLE attributes and perform folding
|
|
63
63
|
doc.search("*[@style]").each do |el|
|
|
64
64
|
style = el.attributes['style'].to_s
|
|
65
65
|
|
|
66
|
-
declarations = []
|
|
67
|
-
|
|
68
|
-
rs
|
|
69
|
-
|
|
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)
|
|
68
|
+
rs.expand_shorthand!
|
|
69
|
+
rs
|
|
70
|
+
rescue ArgumentError => e
|
|
71
|
+
raise e if @options[:rule_set_exceptions]
|
|
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
|
-
if Premailer::RELATED_ATTRIBUTES.
|
|
78
|
-
Premailer::RELATED_ATTRIBUTES[el.name].each do |
|
|
79
|
-
if el[
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
if Premailer::RELATED_ATTRIBUTES.key?(el.name) && @options[:css_to_attributes]
|
|
84
|
+
Premailer::RELATED_ATTRIBUTES[el.name].each do |css_attr, html_attr|
|
|
85
|
+
if el[html_attr].nil? && !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 WIDTH_AND_HIGHT.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
|
-
merged.instance_variable_get(
|
|
85
|
-
|
|
104
|
+
merged.instance_variable_get(:@declarations).tap do |declarations|
|
|
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
|
|
|
@@ -96,9 +117,9 @@ class Premailer
|
|
|
96
117
|
|
|
97
118
|
doc = write_unmergable_css_rules(doc, @unmergable_rules) unless @options[:drop_unmergeable_css_rules]
|
|
98
119
|
|
|
99
|
-
if @options[:remove_classes]
|
|
120
|
+
if @options[:remove_classes] || @options[:remove_comments]
|
|
100
121
|
doc.traverse do |el|
|
|
101
|
-
if el.comment?
|
|
122
|
+
if el.comment? && @options[:remove_comments]
|
|
102
123
|
el.remove
|
|
103
124
|
elsif el.element?
|
|
104
125
|
el.remove_attribute('class') if @options[:remove_classes]
|
|
@@ -110,15 +131,15 @@ class Premailer
|
|
|
110
131
|
# find all anchor's targets and hash them
|
|
111
132
|
targets = []
|
|
112
133
|
doc.search("a[@href^='#']").each do |el|
|
|
113
|
-
target = el.get_attribute('href')[1
|
|
134
|
+
target = el.get_attribute('href')[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
|
|
@@ -132,7 +153,7 @@ class Premailer
|
|
|
132
153
|
end
|
|
133
154
|
|
|
134
155
|
@processed_doc = doc
|
|
135
|
-
if
|
|
156
|
+
if xhtml?
|
|
136
157
|
# we don't want to encode carriage returns
|
|
137
158
|
@processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")
|
|
138
159
|
else
|
|
@@ -154,17 +175,16 @@ class Premailer
|
|
|
154
175
|
style_tag.content = styles
|
|
155
176
|
doc.add_child(style_tag)
|
|
156
177
|
else
|
|
157
|
-
style_tag = doc.create_element "style",
|
|
178
|
+
style_tag = doc.create_element "style", styles.to_s
|
|
158
179
|
head = doc.at_css('head')
|
|
159
|
-
head ||= doc.root.first_element_child.add_previous_sibling(doc.create_element
|
|
160
|
-
head ||= doc.add_child(doc.create_element
|
|
180
|
+
head ||= doc.root.first_element_child.add_previous_sibling(doc.create_element("head")) if doc.root&.first_element_child
|
|
181
|
+
head ||= doc.add_child(doc.create_element("head"))
|
|
161
182
|
head << style_tag
|
|
162
183
|
end
|
|
163
184
|
end
|
|
164
185
|
doc
|
|
165
186
|
end
|
|
166
187
|
|
|
167
|
-
|
|
168
188
|
# Converts the HTML document to a format suitable for plain-text e-mail.
|
|
169
189
|
#
|
|
170
190
|
# If present, uses the <body> element as its base; otherwise uses the whole document.
|
|
@@ -174,17 +194,17 @@ class Premailer
|
|
|
174
194
|
html_src = ''
|
|
175
195
|
begin
|
|
176
196
|
html_src = @doc.at("body").inner_html
|
|
177
|
-
rescue
|
|
197
|
+
rescue StandardError
|
|
178
198
|
end
|
|
179
199
|
|
|
180
|
-
html_src = @doc.to_html unless html_src
|
|
200
|
+
html_src = @doc.to_html unless html_src && !html_src.empty?
|
|
181
201
|
convert_to_text(html_src, @options[:line_length], @html_encoding)
|
|
182
202
|
end
|
|
183
203
|
|
|
184
204
|
# Gets the original HTML as a string.
|
|
185
205
|
# @return [String] HTML.
|
|
186
206
|
def to_s
|
|
187
|
-
if
|
|
207
|
+
if xhtml?
|
|
188
208
|
@doc.to_xhtml(:encoding => nil)
|
|
189
209
|
else
|
|
190
210
|
@doc.to_html(:encoding => nil)
|
|
@@ -198,13 +218,13 @@ class Premailer
|
|
|
198
218
|
thing = nil
|
|
199
219
|
|
|
200
220
|
# TODO: duplicate options
|
|
201
|
-
if @options[:with_html_string]
|
|
221
|
+
if @options[:with_html_string] || @options[:inline] || input.respond_to?(:read)
|
|
202
222
|
thing = input
|
|
203
223
|
elsif @is_local_file
|
|
204
224
|
@base_dir = File.dirname(input)
|
|
205
225
|
thing = File.open(input, 'r')
|
|
206
226
|
else
|
|
207
|
-
thing =
|
|
227
|
+
thing = URI.parse(input).open
|
|
208
228
|
end
|
|
209
229
|
|
|
210
230
|
if thing.respond_to?(:read)
|
|
@@ -215,37 +235,30 @@ class Premailer
|
|
|
215
235
|
doc = nil
|
|
216
236
|
|
|
217
237
|
# Handle HTML entities
|
|
218
|
-
if @options[:replace_html_entities] == true
|
|
238
|
+
if (@options[:replace_html_entities] == true) && thing.is_a?(String)
|
|
239
|
+
thing = +thing
|
|
219
240
|
HTML_ENTITIES.map do |entity, replacement|
|
|
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
|
|
234
|
-
::Nokogiri::HTML(thing, nil, encoding
|
|
248
|
+
::Nokogiri::HTML(thing, nil, encoding, &:recover)
|
|
235
249
|
end
|
|
236
250
|
|
|
237
251
|
# Fix for removing any CDATA tags from both style and script tags inserted per
|
|
238
252
|
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
|
239
253
|
# https://github.com/premailer/premailer/issues/199
|
|
240
|
-
|
|
254
|
+
['style', 'script'].each do |tag|
|
|
241
255
|
doc.search(tag).children.each do |child|
|
|
242
|
-
child.swap(child.text
|
|
256
|
+
child.swap(child.text) if child.cdata?
|
|
243
257
|
end
|
|
244
258
|
end
|
|
245
259
|
|
|
246
260
|
doc
|
|
247
261
|
end
|
|
248
|
-
|
|
249
262
|
end
|
|
250
263
|
end
|
|
251
264
|
end
|