premailer 1.8.6 → 1.8.7

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.
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