premailer 1.19.0 → 1.30.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
2
  SHA256:
3
- metadata.gz: 6e81cf33d2027d02e79ecb94b7a535a54b45d0f62170195baecc51e150fdc4e8
4
- data.tar.gz: ef4b18d2837a8a80565da581d4ad075115c2457ff2087cff1757c099d4a5724d
3
+ metadata.gz: 9d0463a94da370db30e1e80fa7851c90f9b24743518137a2d04b2c21ed7246cd
4
+ data.tar.gz: 73bf8f6e3f7cade9389ff65a6fef58112e8cf2a756f0271adc8b3278bd998a5b
5
5
  SHA512:
6
- metadata.gz: 15a9659bb308546d7617c8bb09d79cf9d140e0773111309f078958f4b27c7964b331c78787bda3abb221d2b0561de0ba31a2eb007c853bd41d14f8ca1f4791a5
7
- data.tar.gz: dc8ac1a09294cdbfa440445f5a6558d5d33d20ead9aa5026c4c83fec487d46aa1cab339abd8c8a15ac51871615a6e4ddcec505b5ddf7ba0436bfdff3cfbb5491
6
+ metadata.gz: 3d98924c3ff4088566f7952d8b56c2ae8747fc99e6ba3f286dcce5e9dd788b3b5c80d1faf60c1a8ff3bb77dc4be136477b1f862cd00b4f1f55b100c9d6ae1289
7
+ data.tar.gz: aff5c151b261c9f35eb58a9a87407cb5c6ee67804ae2ec9585ad4b59043eb6132e443223ad650565dfda7a9fdfe8c528315b5946c5b2d4ea1281ecb8e65bb373
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
- script is my solution.
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](https://premailer.github.io/premailer/HtmlToPlainText.html) is created (optional)
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', :warn_level => Premailer::Warnings::SAFE)
28
+ premailer = Premailer.new('http://example.com/myfile.html', warn_level: Premailer::Warnings::SAFE)
33
29
 
34
- # Write the plain-text output
35
- # This must come before to_inline_css (https://github.com/premailer/premailer/issues/201)
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.open("output.html", "w") do |fout|
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 due to its EOL, please use `~>1.9.0` version if You still need it..
48
+ (hpricot adapter removed, use `~>1.9.0` version if you need it)
62
49
 
63
- `NokogiriFast` adapter improves the Algorithmic complexity of the running time by 20x with a slight compensation on memory. To switch to any of these adapters, add the following line. For example, if you want to include the `NokogiriFast` adapter,
50
+ Picking an adapter:
64
51
 
65
52
  ```ruby
66
53
  Premailer::Adapter.use = :nokogiri_fast
@@ -97,22 +84,125 @@ will result in
97
84
  <table cellspacing='5' width='500'>
98
85
  ```
99
86
 
100
- ## Configuration options
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.
101
106
 
102
- The behavior of Premailer can be configured by passing options in the initializer.
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
+ ```
103
116
 
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.
117
+ ## Configuration options
105
118
 
119
+ For example:
106
120
  ```ruby
107
- premailer = Premailer.new(html_string, with_html_string: true, drop_unmergeable_css_rules: true)
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
+ }
108
172
  ```
109
173
 
110
- [See here for a full list of the available options](https://premailer.github.io/premailer/Premailer.html#initialize-instance_method).
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.
111
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`.
112
199
 
113
200
  ## Contributions
114
201
 
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.
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.
116
206
 
117
207
  A few areas that are particularly in need of love:
118
208
 
@@ -129,4 +219,3 @@ and to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the w
129
219
  The source code can be found on [GitHub](https://github.com/premailer/premailer).
130
220
 
131
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.
132
-
data/bin/premailer CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # This binary used in rubygems environment only as part of installed gem
4
5
 
5
6
  require 'premailer/executor'
6
-
@@ -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.
@@ -13,42 +15,67 @@ class Premailer
13
15
  doc = @processed_doc
14
16
  @unmergable_rules = CssParser::Parser.new
15
17
 
18
+ # Accumulate matched rule sets per node in memory instead of
19
+ # round-tripping them through style attributes as [SPEC=n[...]]
20
+ # string markers (which costs two libxml2 attribute round-trips per
21
+ # element plus a regex re-scan, and dominates allocation churn).
22
+ rules_by_node = {}
23
+
16
24
  # Give all styles already in style attributes a specificity of 1000
17
25
  # per http://www.w3.org/TR/CSS21/cascade.html#specificity
26
+ # Identical style strings (repeated components) share one rule set so
27
+ # the fold cache below can hit on them.
28
+ rule_set_cache = {}
18
29
  doc.search("*[@style]").each do |el|
19
- el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'
30
+ style = el.attributes['style'].to_s
31
+ rs = rule_set_cache.fetch(style) { rule_set_cache[style] = build_rule_set(style, 1000) }
32
+ rules_by_node[el] = rs ? [rs] : []
20
33
  end
21
34
  # Iterate through the rules and merge them into the HTML
22
35
  @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
23
36
  # Save un-mergable rules separately
24
- selector.gsub!(/:link([\s]*)+/i) { |m| $1 }
37
+ selector.gsub!(/:link([\s]*)+/i) { |_m| $1 }
25
38
 
26
39
  # Convert element names to lower case
27
- selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
40
+ selector.gsub!(/([\s]|^)([\w]+)/) { |_m| $1.to_s + $2.to_s.downcase }
28
41
 
29
- 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]
42
+ if Premailer.media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
43
+ @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration), media_types) unless @options[:preserve_styles]
31
44
  else
32
45
  begin
33
- if selector =~ Premailer::RE_RESET_SELECTORS
46
+ if Premailer::RE_RESET_SELECTORS.match?(selector) && !!@options[:preserve_reset]
34
47
  # this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
35
48
  # however, this doesn't mean for testing pur
36
- @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
49
+ @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selectors: selector, block: declaration))
37
50
  end
38
51
 
39
52
  # Change single ID CSS selectors into xpath so that we can match more
40
53
  # than one element. Added to work around dodgy generated code.
41
54
  selector.gsub!(/\A\#([\w_\-]+)\Z/, '*[@id=\1]')
42
55
 
56
+ rule_set = nil
57
+ rule_set_missing = false
43
58
  doc.search(selector).each do |el|
44
- if el.elem? and (el.name != 'head' and el.parent.name != 'head')
45
- # Add a style attribute or append to the existing one
46
- block = "[SPEC=#{specificity}[#{declaration}]]"
47
- el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block
59
+ if el.elem? && ((el.name != 'head') && (el.parent.name != 'head'))
60
+ # Enroll the element even when the rule below fails to build:
61
+ # the marker implementation wrote the style attribute before
62
+ # parsing, so matched elements end up with style="" when
63
+ # their only rule is invalid and exceptions are disabled.
64
+ rules = (rules_by_node[el] ||= [])
65
+ next if rule_set_missing
66
+
67
+ # One shared rule set per CSS rule; merge never mutates inputs
68
+ # (CachedRuleSet#expand_shorthand! is idempotent by design).
69
+ rule_set ||= build_rule_set(declaration, specificity)
70
+ if rule_set.nil?
71
+ rule_set_missing = true
72
+ next
73
+ end
74
+ rules << rule_set
48
75
  end
49
76
  end
50
77
  rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError
51
- $stderr.puts "CSS syntax error with selector: #{selector}" if @options[:verbose]
78
+ warn "CSS syntax error with selector: #{selector}" if @options[:verbose]
52
79
  next
53
80
  end
54
81
  end
@@ -57,65 +84,35 @@ class Premailer
57
84
  # Remove script tags
58
85
  doc.search("script").remove if @options[:remove_scripts]
59
86
 
60
- # Read STYLE attributes and perform folding
61
- doc.search("*[@style]").each do |el|
62
- style = el.attributes['style'].to_s
63
-
64
- declarations = []
65
- style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
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
72
- end
73
-
74
- # Perform style folding
75
- merged = CssParser.merge(declarations)
76
- merged.expand_shorthand!
77
-
78
- # Duplicate CSS attributes as HTML attributes
79
- if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
80
- Premailer::RELATED_ATTRIBUTES[el.name].each do |css_attr, html_attr|
81
- if el[html_attr].nil? and not merged[css_attr].empty?
82
- new_val = merged[css_attr].dup
83
-
84
- # Remove url() function wrapper
85
- new_val.gsub!(/url\((['"])(.*?)\1\)/, '\2')
86
-
87
- # Remove !important, trailing semi-colon, and leading/trailing whitespace
88
- new_val.gsub!(/;$|\s*!important/, '').strip!
89
-
90
- # For width and height tags, remove px units
91
- new_val.gsub!(/(\d+)px/, '\1') if %w[width height].include?(html_attr)
92
-
93
- # For color-related tags, convert RGB to hex if specified by options
94
- new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
95
-
96
- el[html_attr] = new_val
97
- end
98
-
99
- unless @options[:preserve_style_attribute]
100
- merged.instance_variable_get("@declarations").tap do |declarations|
101
- declarations.delete(css_attr)
102
- end
103
- end
104
- end
87
+ # Perform style folding. Elements sharing the same matched rule sets
88
+ # (ubiquitous in table-based email markup) fold to the same result, so
89
+ # the merge/expand/collapse/serialize pipeline runs once per unique
90
+ # (element name, rule list) pair instead of once per element.
91
+ fold_cache = {}
92
+ rules_by_node.each do |el, declarations|
93
+ related = Premailer::RELATED_ATTRIBUTES.key?(el.name) && @options[:css_to_attributes]
94
+ # Rule sets are interned above, so default identity hashing makes
95
+ # them usable directly as cache key elements.
96
+ cache_key = [related ? el.name : nil, *declarations]
97
+
98
+ folded = fold_cache[cache_key] ||= fold_declarations(declarations, related ? el.name : nil)
99
+ style, attributes = folded
100
+
101
+ # Write the inline STYLE attribute first so the attribute order for
102
+ # elements that had no style attribute matches the marker-based
103
+ # implementation (style attr was created during selector matching).
104
+ el['style'] = style
105
+
106
+ attributes&.each do |html_attr, value|
107
+ el[html_attr] = value if el[html_attr].nil?
105
108
  end
106
-
107
- # Collapse multiple rules into one as much as possible.
108
- merged.create_shorthand! if @options[:create_shorthands]
109
-
110
- # write the inline STYLE attribute
111
- el['style'] = merged.declarations_to_s
112
109
  end
113
110
 
114
111
  doc = write_unmergable_css_rules(doc, @unmergable_rules) unless @options[:drop_unmergeable_css_rules]
115
112
 
116
- if @options[:remove_classes] or @options[:remove_comments]
113
+ if @options[:remove_classes] || @options[:remove_comments]
117
114
  doc.traverse do |el|
118
- if el.comment? and @options[:remove_comments]
115
+ if el.comment? && @options[:remove_comments]
119
116
  el.remove
120
117
  elsif el.element?
121
118
  el.remove_attribute('class') if @options[:remove_classes]
@@ -127,7 +124,7 @@ class Premailer
127
124
  # find all anchor's targets and hash them
128
125
  targets = []
129
126
  doc.search("a[@href^='#']").each do |el|
130
- target = el.get_attribute('href')[1..-1]
127
+ target = el.get_attribute('href')[1..]
131
128
  targets << target
132
129
  el.set_attribute('href', "#" + Digest::SHA256.hexdigest(target))
133
130
  end
@@ -149,7 +146,7 @@ class Premailer
149
146
  end
150
147
 
151
148
  @processed_doc = doc
152
- if is_xhtml?
149
+ if xhtml?
153
150
  # we don't want to encode carriage returns
154
151
  @processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")
155
152
  else
@@ -157,6 +154,72 @@ class Premailer
157
154
  end
158
155
  end
159
156
 
157
+ # Merge a list of rule sets into a final style string plus the HTML
158
+ # attribute duplications (bgcolor/align/...) for RELATED_ATTRIBUTES
159
+ # elements. Element-independent: the per-element "attribute already
160
+ # set" guard stays with the caller.
161
+ def fold_declarations(declarations, related_el_name) # :nodoc:
162
+ merged = CssParser.merge(declarations)
163
+ if merged.equal?(declarations[0])
164
+ # CssParser.merge returns its input untouched when given a single
165
+ # rule set; copy it before the destructive fold pipeline below so
166
+ # rule sets shared across elements are not corrupted.
167
+ merged = CssParser::RuleSet.new(block: merged.declarations_to_s, specificity: merged.specificity)
168
+ end
169
+ begin
170
+ merged.expand_shorthand!
171
+ rescue ArgumentError => e
172
+ raise e if @options[:rule_set_exceptions]
173
+ end
174
+
175
+ attributes = nil
176
+ # Duplicate CSS attributes as HTML attributes
177
+ if related_el_name
178
+ Premailer::RELATED_ATTRIBUTES[related_el_name].each do |css_attr, html_attr|
179
+ unless merged[css_attr].empty?
180
+ new_val = merged[css_attr].dup
181
+
182
+ # Remove url() function wrapper
183
+ new_val.gsub!(/url\((['"])(.*?)\1\)/, '\2')
184
+
185
+ # Remove !important, trailing semi-colon, and leading/trailing whitespace
186
+ new_val.gsub!(/;$|\s*!important/, '').strip!
187
+
188
+ # For width and height tags, remove px units
189
+ new_val.gsub!(/(\d+)px/, '\1') if WIDTH_AND_HIGHT.include?(html_attr)
190
+
191
+ # For color-related tags, convert RGB to hex if specified by options
192
+ new_val = ensure_hex(new_val) if css_attr.end_with?('color') && @options[:rgb_to_hex_attributes]
193
+
194
+ (attributes ||= []) << [html_attr, new_val]
195
+ end
196
+
197
+ unless @options[:preserve_style_attribute]
198
+ merged.instance_variable_get(:@declarations).tap do |declarations|
199
+ declarations.delete(css_attr)
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ # Collapse multiple rules into one as much as possible.
206
+ merged.create_shorthand! if @options[:create_shorthands]
207
+
208
+ # write the inline STYLE attribute
209
+ [merged.declarations_to_s, attributes]
210
+ end
211
+
212
+ # Build an expanded rule set for folding. Returns nil (and optionally
213
+ # swallows the error, mirroring the old fold-time rescue) on bad CSS.
214
+ def build_rule_set(block, specificity) # :nodoc:
215
+ rs = Premailer::CachedRuleSet.new(block: block, specificity: specificity)
216
+ rs.expand_shorthand!
217
+ rs
218
+ rescue ArgumentError => e
219
+ raise e if @options[:rule_set_exceptions]
220
+ nil
221
+ end
222
+
160
223
  # Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)
161
224
  # and write it into the <tt>head</tt>.
162
225
  #
@@ -171,17 +234,16 @@ class Premailer
171
234
  style_tag.content = styles
172
235
  doc.add_child(style_tag)
173
236
  else
174
- style_tag = doc.create_element "style", "#{styles}"
237
+ style_tag = doc.create_element "style", styles.to_s
175
238
  head = doc.at_css('head')
176
- head ||= doc.root.first_element_child.add_previous_sibling(doc.create_element "head") if doc.root && doc.root.first_element_child
177
- head ||= doc.add_child(doc.create_element "head")
239
+ head ||= doc.root.first_element_child.add_previous_sibling(doc.create_element("head")) if doc.root&.first_element_child
240
+ head ||= doc.add_child(doc.create_element("head"))
178
241
  head << style_tag
179
242
  end
180
243
  end
181
244
  doc
182
245
  end
183
246
 
184
-
185
247
  # Converts the HTML document to a format suitable for plain-text e-mail.
186
248
  #
187
249
  # If present, uses the <body> element as its base; otherwise uses the whole document.
@@ -191,17 +253,17 @@ class Premailer
191
253
  html_src = ''
192
254
  begin
193
255
  html_src = @doc.at("body").inner_html
194
- rescue;
256
+ rescue StandardError
195
257
  end
196
258
 
197
- html_src = @doc.to_html unless html_src and not html_src.empty?
259
+ html_src = @doc.to_html unless html_src && !html_src.empty?
198
260
  convert_to_text(html_src, @options[:line_length], @html_encoding)
199
261
  end
200
262
 
201
263
  # Gets the original HTML as a string.
202
264
  # @return [String] HTML.
203
265
  def to_s
204
- if is_xhtml?
266
+ if xhtml?
205
267
  @doc.to_xhtml(:encoding => nil)
206
268
  else
207
269
  @doc.to_html(:encoding => nil)
@@ -215,13 +277,13 @@ class Premailer
215
277
  thing = nil
216
278
 
217
279
  # TODO: duplicate options
218
- if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)
280
+ if @options[:with_html_string] || @options[:inline] || input.respond_to?(:read)
219
281
  thing = input
220
282
  elsif @is_local_file
221
283
  @base_dir = File.dirname(input)
222
284
  thing = File.open(input, 'r')
223
285
  else
224
- thing = URI.open(input)
286
+ thing = URI.parse(input).open
225
287
  end
226
288
 
227
289
  if thing.respond_to?(:read)
@@ -232,7 +294,8 @@ class Premailer
232
294
  doc = nil
233
295
 
234
296
  # Handle HTML entities
235
- if @options[:replace_html_entities] == true and thing.is_a?(String)
297
+ if (@options[:replace_html_entities] == true) && thing.is_a?(String)
298
+ thing = +thing
236
299
  HTML_ENTITIES.map do |entity, replacement|
237
300
  thing.gsub! entity, replacement
238
301
  end
@@ -241,21 +304,20 @@ class Premailer
241
304
  doc = if @options[:html_fragment]
242
305
  ::Nokogiri::HTML.fragment(thing, encoding)
243
306
  else
244
- ::Nokogiri::HTML(thing, nil, encoding) { |c| c.recover }
307
+ ::Nokogiri::HTML(thing, nil, encoding, &:recover)
245
308
  end
246
309
 
247
310
  # Fix for removing any CDATA tags from both style and script tags inserted per
248
311
  # https://github.com/sparklemotion/nokogiri/issues/311 and
249
312
  # https://github.com/premailer/premailer/issues/199
250
- %w(style script).each do |tag|
313
+ ['style', 'script'].each do |tag|
251
314
  doc.search(tag).children.each do |child|
252
- child.swap(child.text()) if child.cdata?
315
+ child.swap(child.text) if child.cdata?
253
316
  end
254
317
  end
255
318
 
256
319
  doc
257
320
  end
258
-
259
321
  end
260
322
  end
261
323
  end