premailer 1.8.6 → 1.8.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/bin/premailer +0 -1
  4. data/lib/premailer/adapter.rb +6 -2
  5. data/lib/premailer/adapter/hpricot.rb +12 -2
  6. data/lib/premailer/adapter/nokogiri.rb +8 -6
  7. data/lib/premailer/adapter/nokogumbo.rb +243 -0
  8. data/lib/premailer/html_to_plain_text.rb +9 -15
  9. data/lib/premailer/premailer.rb +11 -10
  10. data/lib/premailer/version.rb +1 -1
  11. metadata +64 -69
  12. data/.gitignore +0 -12
  13. data/.jrubyrc +0 -1
  14. data/.travis.yml +0 -34
  15. data/.yardopts +0 -9
  16. data/Gemfile +0 -16
  17. data/gemfiles/.ruby187.gemfile +0 -21
  18. data/init.rb +0 -1
  19. data/local-premailer +0 -9
  20. data/premailer.gemspec +0 -30
  21. data/rakefile.rb +0 -62
  22. data/test/files/base.html +0 -140
  23. data/test/files/chars.html +0 -6
  24. data/test/files/contact_bg.png +0 -0
  25. data/test/files/dialect.png +0 -0
  26. data/test/files/dots_end.png +0 -0
  27. data/test/files/dots_h.gif +0 -0
  28. data/test/files/html4.html +0 -12
  29. data/test/files/html_with_uri.html +0 -9
  30. data/test/files/ignore.css +0 -3
  31. data/test/files/ignore.html +0 -15
  32. data/test/files/import.css +0 -13
  33. data/test/files/inc/2009-placeholder.png +0 -0
  34. data/test/files/iso-8859-2.html +0 -1
  35. data/test/files/iso-8859-5.html +0 -8
  36. data/test/files/no_css.html +0 -11
  37. data/test/files/noimport.css +0 -13
  38. data/test/files/styles.css +0 -106
  39. data/test/files/xhtml.html +0 -11
  40. data/test/future_tests.rb +0 -50
  41. data/test/helper.rb +0 -42
  42. data/test/test_adapter.rb +0 -29
  43. data/test/test_html_to_plain_text.rb +0 -184
  44. data/test/test_links.rb +0 -207
  45. data/test/test_misc.rb +0 -373
  46. data/test/test_premailer.rb +0 -347
  47. data/test/test_warnings.rb +0 -95
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7316d27a2fa70efed484e7c64733a351491f1cea
4
- data.tar.gz: 6dd11b25df48ce0e73149ffdc25e6702de94ec21
3
+ metadata.gz: ceccb9e67d075de22a76d50df38d99481a6811bf
4
+ data.tar.gz: 475cc5974d20a0b8b69bef467d10de3edff7d821
5
5
  SHA512:
6
- metadata.gz: 9d5aed590607bd5cdbe7b629424c2b18832e5ee36c5688627216ea660c493edfb92a6f5d2f77e5d78b2b5cb502d2fb7f592adf3e0d624d58ced7b57ac7f6acdb
7
- data.tar.gz: 107fb306346e6e35b146b7e327b0e2cc8cca1ded78f75687ed5281e090f08d50e7a9625335fbaffe066496e0c72f121b3389779751933900b7aec356d8b86791
6
+ metadata.gz: 2591ab12195ae73b9909b945131cdf06382917afde4e4270dc496b937eac87c98cea081c825209a0e4cfd40f8b2202f1953f980d8203cf90afcadec80b139c17
7
+ data.tar.gz: e34eda9604977da572ef0f712c710547e9e7d965370404a2b1a233a2dd04760a7461c70acf4b40008b72824fe2a38f4cb18bd36d49da794bd61bd41a09096721
data/README.md CHANGED
@@ -54,7 +54,7 @@ end
54
54
 
55
55
  ## Ruby Compatibility
56
56
 
57
- Premailer is tested on Ruby 1.8.7, Ruby 1.9.2, Ruby 1.9.3, and Ruby 2.x.0 . It also works on REE. JRuby support is close; contributors are welcome. Checkout the latest build status on the [Travis CI dashboard](http://travis-ci.org/#!/premailer/premailer).
57
+ Premailer is tested on Ruby 1.8.7, Ruby 1.9.2, Ruby 1.9.3, and Ruby 2.x.0 . It also works on REE. JRuby support is close; contributors are welcome. Checkout the latest build status on the [Travis CI dashboard](https://travis-ci.org/#!/premailer/premailer).
58
58
 
59
59
  ## Premailer-specific CSS
60
60
 
@@ -66,7 +66,7 @@ Premailer looks for a few CSS attributes that make working with tables a bit eas
66
66
  | -premailer-height | Available on `table`, `tr`, `th` and `td` elements |
67
67
  | -premailer-cellpadding | Available on `table` elements |
68
68
  | -premailer-cellspacing | Available on `table` elements |
69
- | data-premailer="ignore" | Available on `style` elements. Premailer will ignore these elements entirely. |
69
+ | data-premailer="ignore" | Available on `link` and `style` elements. Premailer will ignore these elements entirely. |
70
70
 
71
71
  Each of these CSS declarations will be copied to appropriate element's attribute.
72
72
 
@@ -93,10 +93,10 @@ A few areas that are particularly in need of love:
93
93
 
94
94
  ## Credits and code
95
95
 
96
- Thanks to [all the wonderful contributors](https://github.com/alexdunae/premailer/contributors) for their updates.
96
+ Thanks to [all the wonderful contributors](https://github.com/premailer/premailer/contributors) for their updates.
97
97
 
98
98
  Thanks to [Greenhood + Company](http://www.greenhood.com/) for sponsoring some of the 1.5.6 updates,
99
- and to [Campaign Monitor](http://www.campaignmonitor.com) for supporting the web interface.
99
+ and to [Campaign Monitor](https://www.campaignmonitor.com/) for supporting the web interface.
100
100
 
101
101
  The web interface can be found at [premailer.dialect.ca](http://premailer.dialect.ca).
102
102
 
@@ -2,6 +2,5 @@
2
2
 
3
3
  # This binary used in rubygems environment only as part of installed gem
4
4
 
5
- require 'rubygems'
6
5
  require 'premailer/executor'
7
6
 
@@ -4,16 +4,19 @@ class Premailer
4
4
  # Manages the adapter classes. Currently supports:
5
5
  #
6
6
  # * nokogiri
7
+ # * nokogumbo
7
8
  # * hpricot
8
9
  module Adapter
9
10
 
10
11
  autoload :Hpricot, 'premailer/adapter/hpricot'
11
12
  autoload :Nokogiri, 'premailer/adapter/nokogiri'
13
+ autoload :Nokogumbo, 'premailer/adapter/nokogumbo'
12
14
 
13
15
  # adapter to required file mapping.
14
16
  REQUIREMENT_MAP = [
15
- ["hpricot", :hpricot],
16
17
  ["nokogiri", :nokogiri],
18
+ ["nokogumbo", :nokogumbo],
19
+ ["hpricot", :hpricot],
17
20
  ]
18
21
 
19
22
  # Returns the adapter to use.
@@ -28,8 +31,9 @@ class Premailer
28
31
  # then checks to see which are installed if none are loaded.
29
32
  # @raise [RuntimeError] unless suitable adapter found.
30
33
  def self.default
31
- return :hpricot if defined?(::Hpricot)
32
34
  return :nokogiri if defined?(::Nokogiri)
35
+ return :nokogumbo if defined?(::Nokogumbo)
36
+ return :hpricot if defined?(::Hpricot)
33
37
 
34
38
  REQUIREMENT_MAP.each do |(library, adapter)|
35
39
  begin
@@ -4,6 +4,13 @@ class Premailer
4
4
  module Adapter
5
5
  # Hpricot adapter
6
6
  module Hpricot
7
+ def self.included(base)
8
+ warn <<eos
9
+ [DEPRECATED] Premailer's Hpricot adapter will be removed with the next major \
10
+ release. Change your :adapter option to :nokogiri or remove the `hpricot` gem \
11
+ from your application to silence this warning. (Called from #{caller[2]})
12
+ eos
13
+ end
7
14
 
8
15
  # Merge CSS into the HTML document.
9
16
  # @return [String] HTML.
@@ -75,12 +82,15 @@ class Premailer
75
82
  # Perform style folding
76
83
  merged = CssParser.merge(declarations)
77
84
  merged.expand_shorthand!
78
- merged.create_shorthand!
85
+ merged.create_shorthand! if @options[:create_shorthands]
79
86
 
80
87
  # Duplicate CSS attributes as HTML attributes
81
- if Premailer::RELATED_ATTRIBUTES.has_key?(el.name)
88
+ if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
82
89
  Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
83
90
  el[html_att] = merged[css_att].gsub(/url\('(.*)'\)/,'\1').gsub(/;$|\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?
91
+ merged.instance_variable_get("@declarations").tap do |declarations|
92
+ declarations.delete(css_att)
93
+ end
84
94
  end
85
95
  end
86
96
 
@@ -17,7 +17,6 @@ class Premailer
17
17
  doc.search("*[@style]").each do |el|
18
18
  el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'
19
19
  end
20
-
21
20
  # Iterate through the rules and merge them into the HTML
22
21
  @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
23
22
  # Save un-mergable rules separately
@@ -74,19 +73,22 @@ class Premailer
74
73
  merged.expand_shorthand!
75
74
 
76
75
  # Duplicate CSS attributes as HTML attributes
77
- if Premailer::RELATED_ATTRIBUTES.has_key?(el.name)
76
+ if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
78
77
  Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
79
78
  el[html_att] = merged[css_att].gsub(/url\(['|"](.*)['|"]\)/, '\1').gsub(/;$|\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?
79
+ merged.instance_variable_get("@declarations").tap do |declarations|
80
+ declarations.delete(css_att)
81
+ end
80
82
  end
81
83
  end
82
-
83
84
  # Collapse multiple rules into one as much as possible.
84
- merged.create_shorthand!
85
+ merged.create_shorthand! if @options[:create_shorthands]
85
86
 
86
87
  # write the inline STYLE attribute
87
- attributes = Premailer.escape_string(merged.declarations_to_s).split(';').map(&:strip)
88
+ # split by ';' but ignore those in brackets
89
+ attributes = Premailer.escape_string(merged.declarations_to_s).split(/;(?![^(]*\))/).map(&:strip)
88
90
  attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }
89
- el['style'] = attributes.join('; ')
91
+ el['style'] = attributes.join('; ') + ";"
90
92
  end
91
93
 
92
94
  doc = write_unmergable_css_rules(doc, @unmergable_rules)
@@ -0,0 +1,243 @@
1
+ require 'nokogumbo'
2
+
3
+ class Premailer
4
+ module Adapter
5
+ # Nokogiri adapter
6
+ module Nokogumbo
7
+
8
+ # Merge CSS into the HTML document.
9
+ #
10
+ # @return [String] an HTML.
11
+ def to_inline_css
12
+ doc = @processed_doc
13
+ @unmergable_rules = CssParser::Parser.new
14
+
15
+ # Give all styles already in style attributes a specificity of 1000
16
+ # per http://www.w3.org/TR/CSS21/cascade.html#specificity
17
+ doc.search("*[@style]").each do |el|
18
+ el['style'] = '[SPEC=1000[' + el.attributes['style'] + ']]'
19
+ end
20
+ # Iterate through the rules and merge them into the HTML
21
+ @css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
22
+ # Save un-mergable rules separately
23
+ selector.gsub!(/:link([\s]*)+/i) { |m| $1 }
24
+
25
+ # Convert element names to lower case
26
+ selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
27
+
28
+ if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
29
+ @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
30
+ else
31
+ begin
32
+ if selector =~ Premailer::RE_RESET_SELECTORS
33
+ # this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
34
+ # however, this doesn't mean for testing pur
35
+ @unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
36
+ end
37
+
38
+ # Change single ID CSS selectors into xpath so that we can match more
39
+ # than one element. Added to work around dodgy generated code.
40
+ selector.gsub!(/\A\#([\w_\-]+)\Z/, '*[@id=\1]')
41
+
42
+ doc.search(selector).each do |el|
43
+ if el.elem? and (el.name != 'head' and el.parent.name != 'head')
44
+ # Add a style attribute or append to the existing one
45
+ block = "[SPEC=#{specificity}[#{declaration}]]"
46
+ el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block
47
+ end
48
+ end
49
+ rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError
50
+ $stderr.puts "CSS syntax error with selector: #{selector}" if @options[:verbose]
51
+ next
52
+ end
53
+ end
54
+ end
55
+
56
+ # Remove script tags
57
+ if @options[:remove_scripts]
58
+ doc.search("script").remove
59
+ end
60
+
61
+ # Read STYLE attributes and perform folding
62
+ doc.search("*[@style]").each do |el|
63
+ style = el.attributes['style'].to_s
64
+
65
+ declarations = []
66
+ style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
67
+ rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
68
+ declarations << rs
69
+ end
70
+
71
+ # Perform style folding
72
+ merged = CssParser.merge(declarations)
73
+ merged.expand_shorthand!
74
+
75
+ # Duplicate CSS attributes as HTML attributes
76
+ if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
77
+ Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
78
+ el[html_att] = merged[css_att].gsub(/url\(['|"](.*)['|"]\)/, '\1').gsub(/;$|\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?
79
+ merged.instance_variable_get("@declarations").tap do |declarations|
80
+ declarations.delete(css_att)
81
+ end
82
+ end
83
+ end
84
+ # Collapse multiple rules into one as much as possible.
85
+ merged.create_shorthand! if @options[:create_shorthands]
86
+
87
+ # write the inline STYLE attribute
88
+ attributes = Premailer.escape_string(merged.declarations_to_s).split(';').map(&:strip)
89
+ attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }
90
+ el['style'] = attributes.join('; ') + ";"
91
+ end
92
+
93
+ doc = write_unmergable_css_rules(doc, @unmergable_rules)
94
+
95
+ if @options[:remove_classes] or @options[:remove_comments]
96
+ doc.traverse do |el|
97
+ if el.comment? and @options[:remove_comments]
98
+ el.remove
99
+ elsif el.element?
100
+ el.remove_attribute('class') if @options[:remove_classes]
101
+ end
102
+ end
103
+ end
104
+
105
+ if @options[:remove_ids]
106
+ # find all anchor's targets and hash them
107
+ targets = []
108
+ doc.search("a[@href^='#']").each do |el|
109
+ target = el.get_attribute('href')[1..-1]
110
+ targets << target
111
+ el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
112
+ end
113
+ # hash ids that are links target, delete others
114
+ doc.search("*[@id]").each do |el|
115
+ id = el.get_attribute('id')
116
+ if targets.include?(id)
117
+ el.set_attribute('id', Digest::MD5.hexdigest(id))
118
+ else
119
+ el.remove_attribute('id')
120
+ end
121
+ end
122
+ end
123
+
124
+ if @options[:reset_contenteditable]
125
+ doc.search('*[@contenteditable]').each do |el|
126
+ el.remove_attribute('contenteditable')
127
+ end
128
+ end
129
+
130
+ @processed_doc = doc
131
+ if is_xhtml?
132
+ # we don't want to encode carriage returns
133
+ @processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")
134
+ else
135
+ @processed_doc.to_html(:encoding => @options[:output_encoding])
136
+ end
137
+ end
138
+
139
+ # Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)
140
+ # and write it into the <tt>body</tt>.
141
+ #
142
+ # <tt>doc</tt> is an Nokogiri document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.
143
+ #
144
+ # @return [::Nokogiri::XML] a document.
145
+ def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:
146
+ styles = unmergable_rules.to_s
147
+
148
+ unless styles.empty?
149
+ style_tag = "<style type=\"text/css\">\n#{styles}</style>"
150
+ unless (body = doc.search('body')).empty?
151
+ if doc.at_css('body').children && !doc.at_css('body').children.empty?
152
+ doc.at_css('body').children.before(::Nokogiri::XML.fragment(style_tag))
153
+ else
154
+ doc.at_css('body').add_child(::Nokogiri::XML.fragment(style_tag))
155
+ end
156
+ else
157
+ doc.inner_html = style_tag += doc.inner_html
158
+ end
159
+ end
160
+ doc
161
+ end
162
+
163
+
164
+ # Converts the HTML document to a format suitable for plain-text e-mail.
165
+ #
166
+ # If present, uses the <body> element as its base; otherwise uses the whole document.
167
+ #
168
+ # @return [String] a plain text.
169
+ def to_plain_text
170
+ html_src = ''
171
+ begin
172
+ html_src = @doc.at("body").inner_html
173
+ rescue;
174
+ end
175
+
176
+ html_src = @doc.to_html unless html_src and not html_src.empty?
177
+ convert_to_text(html_src, @options[:line_length], @html_encoding)
178
+ end
179
+
180
+ # Gets the original HTML as a string.
181
+ # @return [String] HTML.
182
+ def to_s
183
+ if is_xhtml?
184
+ @doc.to_xhtml(:encoding => nil)
185
+ else
186
+ @doc.to_html(:encoding => nil)
187
+ end
188
+ end
189
+
190
+ # Load the HTML file and convert it into an Nokogiri document.
191
+ #
192
+ # @return [::Nokogiri::XML] a document.
193
+ def load_html(input) # :nodoc:
194
+ thing = nil
195
+
196
+ # TODO: duplicate options
197
+ if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)
198
+ thing = input
199
+ elsif @is_local_file
200
+ @base_dir = File.dirname(input)
201
+ thing = File.open(input, 'r')
202
+ else
203
+ thing = open(input)
204
+ end
205
+
206
+ if thing.respond_to?(:read)
207
+ thing = thing.read
208
+ end
209
+
210
+ return nil unless thing
211
+ doc = nil
212
+
213
+ # Handle HTML entities
214
+ if @options[:replace_html_entities] == true and thing.is_a?(String)
215
+ HTML_ENTITIES.map do |entity, replacement|
216
+ thing.gsub! entity, replacement
217
+ end
218
+ end
219
+ # Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
220
+ # However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
221
+ if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
222
+ thing = thing.force_encoding(@options[:input_encoding]).encode!
223
+ doc = ::Nokogiri::HTML5(thing)
224
+ else
225
+ default_encoding = RUBY_PLATFORM == 'java' ? nil : 'BINARY'
226
+ doc = ::Nokogiri::HTML5(thing)
227
+ end
228
+
229
+ # Fix for removing any CDATA tags from both style and script tags inserted per
230
+ # https://github.com/sparklemotion/nokogiri/issues/311 and
231
+ # https://github.com/premailer/premailer/issues/199
232
+ %w(style script).each do |tag|
233
+ doc.search(tag).children.each do |child|
234
+ child.swap(child.text()) if child.cdata?
235
+ end
236
+ end
237
+
238
+ doc
239
+ end
240
+
241
+ end
242
+ end
243
+ end
@@ -31,17 +31,11 @@ module HtmlToPlainText
31
31
  txt.gsub!(/<img.+?alt=\'([^\']*)\'[^>]*\>/i, '\1')
32
32
 
33
33
  # links
34
- txt.gsub!(/<a\s.*?href=\"(mailto:)?([^\"]*)\"[^>]*>((.|\s)*?)<\/a>/i) do |s|
35
- if $3.empty?
36
- ''
37
- else
38
- $3.strip + ' ( ' + $2.strip + ' )'
39
- end
40
- end
41
-
42
- txt.gsub!(/<a\s.*?href='(mailto:)?([^\']*)\'[^>]*>((.|\s)*?)<\/a>/i) do |s|
34
+ txt.gsub!(/<a\s.*?href=["'](mailto:)?([^"']*)["'][^>]*>((.|\s)*?)<\/a>/i) do |s|
43
35
  if $3.empty?
44
36
  ''
37
+ elsif $3.strip.downcase == $2.strip.downcase
38
+ $3.strip
45
39
  else
46
40
  $3.strip + ' ( ' + $2.strip + ' )'
47
41
  end
@@ -92,25 +86,25 @@ module HtmlToPlainText
92
86
  he = HTMLEntities.new
93
87
  txt = he.decode(txt)
94
88
 
89
+ # no more than two consecutive spaces
90
+ txt.gsub!(/ {2,}/, " ")
91
+
95
92
  txt = word_wrap(txt, line_length)
96
93
 
97
94
  # remove linefeeds (\r\n and \r -> \n)
98
95
  txt.gsub!(/\r\n?/, "\n")
99
96
 
100
97
  # strip extra spaces
101
- txt.gsub!(/\302\240+/, " ") # non-breaking spaces -> spaces
98
+ txt.gsub!(/[ \t]*\302\240+[ \t]*/, " ") # non-breaking spaces -> spaces
102
99
  txt.gsub!(/\n[ \t]+/, "\n") # space at start of lines
103
100
  txt.gsub!(/[ \t]+\n/, "\n") # space at end of lines
104
101
 
105
102
  # no more than two consecutive newlines
106
103
  txt.gsub!(/[\n]{3,}/, "\n\n")
107
104
 
108
- # no more than two consecutive spaces
109
- txt.gsub!(/ {2,}/, " ")
110
-
111
105
  # the word messes up the parens
112
- txt.gsub!(/\([ \n](http[^)]+)[\n ]\)/) do |s|
113
- "( " + $1 + " )"
106
+ txt.gsub!(/\(([ \n])(http[^)]+)([\n ])\)/) do |s|
107
+ ($1 == "\n" ? $1 : '' ) + '( ' + $2 + ' )' + ($3 == "\n" ? $1 : '' )
114
108
  end
115
109
 
116
110
  txt.strip
@@ -166,6 +166,7 @@ class Premailer
166
166
  # @option options [Boolean] :preserve_reset Whether to preserve styles associated with the MailChimp reset code. Default is true.
167
167
  # @option options [Boolean] :with_html_string Whether the html param should be treated as a raw string. Default is false.
168
168
  # @option options [Boolean] :verbose Whether to print errors and warnings to <tt>$stderr</tt>. Default is false.
169
+ # @option options [Boolean] :io_exceptions Throws exceptions on I/O errors.
169
170
  # @option options [Boolean] :include_link_tags Whether to include css from <tt>link rel=stylesheet</tt> tags. Default is true.
170
171
  # @option options [Boolean] :include_style_tags Whether to include css from <tt>style</tt> tags. Default is true.
171
172
  # @option options [String] :input_encoding Manually specify the source documents encoding. This is a good idea. Default is ASCII-8BIT.
@@ -173,6 +174,7 @@ class Premailer
173
174
  # @option options [Boolean] :escape_url_attributes URL Escapes href, src, and background attributes on elements. Default is true.
174
175
  # @option options [Symbol] :adapter Which HTML parser to use, either <tt>:nokogiri</tt> or <tt>:hpricot</tt>. Default is <tt>:hpricot</tt>.
175
176
  # @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.
177
+ # @option options [Boolean] :create_shorthands Combine several properties into a shorthand one, e.g. font: style weight size. Default is true.
176
178
  def initialize(html, options = {})
177
179
  @options = {:warn_level => Warnings::SAFE,
178
180
  :line_length => 65,
@@ -199,6 +201,7 @@ class Premailer
199
201
  :replace_html_entities => false,
200
202
  :escape_url_attributes => true,
201
203
  :unescaped_ampersand => false,
204
+ :create_shorthands => true,
202
205
  :adapter => Adapter.use,
203
206
  }.merge(options)
204
207
 
@@ -260,7 +263,9 @@ protected
260
263
  end
261
264
 
262
265
  load_css_from_string(css_block)
263
- rescue; end
266
+ rescue => e
267
+ raise e if @options[:io_exceptions]
268
+ end
264
269
  end
265
270
 
266
271
  def load_css_from_string(css_string)
@@ -282,11 +287,7 @@ protected
282
287
 
283
288
  # Load CSS included in <tt>style</tt> and <tt>link</tt> tags from an HTML document.
284
289
  def load_css_from_html! # :nodoc:
285
- if (@options[:adapter] == :nokogiri)
286
- tags = @doc.search("link[@rel='stylesheet']:not([@data-premailer='ignore'])", "//style[not(contains(@data-premailer,'ignore'))]")
287
- else
288
- tags = @doc.search("link[@rel='stylesheet']:not([@data-premailer='ignore']), style:not([@data-premailer='ignore'])")
289
- end
290
+ tags = @doc.search("link[@rel='stylesheet']:not([@data-premailer='ignore']), style:not([@data-premailer='ignore'])")
290
291
  if tags
291
292
  tags.each do |tag|
292
293
  if tag.to_s.strip =~ /^\<link/i && tag.attributes['href'] && media_type_ok?(tag.attributes['media']) && @options[:include_link_tags]
@@ -298,10 +299,10 @@ protected
298
299
  link_uri = tag.attributes['href'].to_s.sub(@base_url.to_s, '')
299
300
  else
300
301
  link_uri = File.join(File.dirname(@html_file), tag.attributes['href'].to_s.sub!(@base_url.to_s, ''))
301
- # if the file does not exist locally, try to grab the remote reference
302
- unless File.exists?(link_uri)
303
- link_uri = Premailer.resolve_link(tag.attributes['href'].to_s, @html_file)
304
- end
302
+ end
303
+ # if the file does not exist locally, try to grab the remote reference
304
+ unless File.exists?(link_uri)
305
+ link_uri = Premailer.resolve_link(tag.attributes['href'].to_s, @html_file)
305
306
  end
306
307
  else
307
308
  link_uri = tag.attributes['href'].to_s