premailer 1.8.7 → 1.9.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 +6 -5
- data/lib/premailer/adapter.rb +4 -0
- data/lib/premailer/adapter/nokogiri_fast.rb +354 -0
- data/lib/premailer/executor.rb +1 -1
- data/lib/premailer/version.rb +1 -1
- metadata +17 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e8e7d38035296170890e416aa7b33bb1aef052b
|
4
|
+
data.tar.gz: 4ee232c94b2585448385d2e8e8b8884380ada987
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6a5daedd7752a35cf677cbf62d58697809cc496f44372247f7a080395a13756e9a9a1e633c44ba44faeff55f83c118454cb146c7860108cd5d792dda0d82ab2
|
7
|
+
data.tar.gz: d4d53382db2412c1c8e4e022adc114828fd46e79c8fd6f8903089c34a2ffed0b8799252792f0fdf1eca3e0d7844bd6bffa8feccc8bf5b6e6294aec9b9ccda0c1
|
data/README.md
CHANGED
@@ -36,16 +36,17 @@ require 'premailer'
|
|
36
36
|
|
37
37
|
premailer = Premailer.new('http://example.com/myfile.html', :warn_level => Premailer::Warnings::SAFE)
|
38
38
|
|
39
|
-
# Write the HTML output
|
40
|
-
File.open("output.html", "w") do |fout|
|
41
|
-
fout.puts premailer.to_inline_css
|
42
|
-
end
|
43
|
-
|
44
39
|
# Write the plain-text output
|
40
|
+
# This must come before to_inline_css (https://github.com/premailer/premailer/issues/201)
|
45
41
|
File.open("output.txt", "w") do |fout|
|
46
42
|
fout.puts premailer.to_plain_text
|
47
43
|
end
|
48
44
|
|
45
|
+
# Write the HTML output
|
46
|
+
File.open("output.html", "w") do |fout|
|
47
|
+
fout.puts premailer.to_inline_css
|
48
|
+
end
|
49
|
+
|
49
50
|
# Output any CSS warnings
|
50
51
|
premailer.warnings.each do |w|
|
51
52
|
puts "#{w[:message]} (#{w[:level]}) may not render properly in #{w[:clients]}"
|
data/lib/premailer/adapter.rb
CHANGED
@@ -4,17 +4,20 @@ class Premailer
|
|
4
4
|
# Manages the adapter classes. Currently supports:
|
5
5
|
#
|
6
6
|
# * nokogiri
|
7
|
+
# * nokogiri_fast
|
7
8
|
# * nokogumbo
|
8
9
|
# * hpricot
|
9
10
|
module Adapter
|
10
11
|
|
11
12
|
autoload :Hpricot, 'premailer/adapter/hpricot'
|
12
13
|
autoload :Nokogiri, 'premailer/adapter/nokogiri'
|
14
|
+
autoload :NokogiriFast, 'premailer/adapter/nokogiri_fast'
|
13
15
|
autoload :Nokogumbo, 'premailer/adapter/nokogumbo'
|
14
16
|
|
15
17
|
# adapter to required file mapping.
|
16
18
|
REQUIREMENT_MAP = [
|
17
19
|
["nokogiri", :nokogiri],
|
20
|
+
["nokogiri", :nokogiri_fast],
|
18
21
|
["nokogumbo", :nokogumbo],
|
19
22
|
["hpricot", :hpricot],
|
20
23
|
]
|
@@ -32,6 +35,7 @@ class Premailer
|
|
32
35
|
# @raise [RuntimeError] unless suitable adapter found.
|
33
36
|
def self.default
|
34
37
|
return :nokogiri if defined?(::Nokogiri)
|
38
|
+
return :nokogiri_fast if defined?(::NokogiriFast)
|
35
39
|
return :nokogumbo if defined?(::Nokogumbo)
|
36
40
|
return :hpricot if defined?(::Hpricot)
|
37
41
|
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
class Premailer
|
4
|
+
module Adapter
|
5
|
+
# NokogiriFast adapter
|
6
|
+
module NokogiriFast
|
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
|
+
|
21
|
+
# Create an index for nodes by tag name/id/class
|
22
|
+
# Also precompute the map of nodes to descendants
|
23
|
+
index, all_nodes, descendants = make_index(doc)
|
24
|
+
|
25
|
+
# Iterate through the rules and merge them into the HTML
|
26
|
+
@css_parser.each_selector(:all) do |selector, declaration, specificity, media_types|
|
27
|
+
|
28
|
+
# Save un-mergable rules separately
|
29
|
+
selector.gsub!(/:link([\s]*)+/i) { |m| $1 }
|
30
|
+
|
31
|
+
# Convert element names to lower case
|
32
|
+
selector.gsub!(/([\s]|^)([\w]+)/) { |m| $1.to_s + $2.to_s.downcase }
|
33
|
+
|
34
|
+
if Premailer.is_media_query?(media_types) || selector =~ Premailer::RE_UNMERGABLE_SELECTORS
|
35
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration), media_types) unless @options[:preserve_styles]
|
36
|
+
else
|
37
|
+
begin
|
38
|
+
if selector =~ Premailer::RE_RESET_SELECTORS
|
39
|
+
# this is in place to preserve the MailChimp CSS reset: http://github.com/mailchimp/Email-Blueprints/
|
40
|
+
# however, this doesn't mean for testing pur
|
41
|
+
@unmergable_rules.add_rule_set!(CssParser::RuleSet.new(selector, declaration)) unless !@options[:preserve_reset]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Try the new index based technique. If not supported, fall back to the old brute force one.
|
45
|
+
nodes = match_selector(index, all_nodes, descendants, selector) || doc.search(selector)
|
46
|
+
nodes.each do |el|
|
47
|
+
if el.elem? and (el.name != 'head' and el.parent.name != 'head')
|
48
|
+
# Add a style attribute or append to the existing one
|
49
|
+
block = "[SPEC=#{specificity}[#{declaration}]]"
|
50
|
+
el['style'] = (el.attributes['style'].to_s ||= '') + ' ' + block
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue ::Nokogiri::SyntaxError, RuntimeError, ArgumentError
|
54
|
+
$stderr.puts "CSS syntax error with selector: #{selector}" if @options[:verbose]
|
55
|
+
next
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Remove script tags
|
61
|
+
doc.search("script").remove if @options[:remove_scripts]
|
62
|
+
|
63
|
+
# Read STYLE attributes and perform folding
|
64
|
+
doc.search("*[@style]").each do |el|
|
65
|
+
style = el.attributes['style'].to_s
|
66
|
+
|
67
|
+
declarations = []
|
68
|
+
style.scan(/\[SPEC\=([\d]+)\[(.[^\]\]]*)\]\]/).each do |declaration|
|
69
|
+
rs = CssParser::RuleSet.new(nil, declaration[1].to_s, declaration[0].to_i)
|
70
|
+
declarations << rs
|
71
|
+
end
|
72
|
+
|
73
|
+
# Perform style folding
|
74
|
+
merged = CssParser.merge(declarations)
|
75
|
+
merged.expand_shorthand!
|
76
|
+
|
77
|
+
# Duplicate CSS attributes as HTML attributes
|
78
|
+
if Premailer::RELATED_ATTRIBUTES.has_key?(el.name) && @options[:css_to_attributes]
|
79
|
+
Premailer::RELATED_ATTRIBUTES[el.name].each do |css_att, html_att|
|
80
|
+
el[html_att] = merged[css_att].gsub(/url\(['|"](.*)['|"]\)/, '\1').gsub(/;$|\s*!important/, '').strip if el[html_att].nil? and not merged[css_att].empty?
|
81
|
+
merged.instance_variable_get("@declarations").tap do |declarations|
|
82
|
+
declarations.delete(css_att)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
# Collapse multiple rules into one as much as possible.
|
87
|
+
merged.create_shorthand! if @options[:create_shorthands]
|
88
|
+
|
89
|
+
# write the inline STYLE attribute
|
90
|
+
# split by ';' but ignore those in brackets
|
91
|
+
attributes = Premailer.escape_string(merged.declarations_to_s).split(/;(?![^(]*\))/).map(&:strip)
|
92
|
+
attributes = attributes.map { |attr| [attr.split(':').first, attr] }.sort_by { |pair| pair.first }.map { |pair| pair[1] }
|
93
|
+
el['style'] = attributes.join('; ') + ";"
|
94
|
+
end
|
95
|
+
|
96
|
+
doc = write_unmergable_css_rules(doc, @unmergable_rules)
|
97
|
+
|
98
|
+
if @options[:remove_classes] or @options[:remove_comments]
|
99
|
+
doc.traverse do |el|
|
100
|
+
if el.comment? and @options[:remove_comments]
|
101
|
+
el.remove
|
102
|
+
elsif el.element?
|
103
|
+
el.remove_attribute('class') if @options[:remove_classes]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
if @options[:remove_ids]
|
109
|
+
# find all anchor's targets and hash them
|
110
|
+
targets = []
|
111
|
+
doc.search("a[@href^='#']").each do |el|
|
112
|
+
target = el.get_attribute('href')[1..-1]
|
113
|
+
targets << target
|
114
|
+
el.set_attribute('href', "#" + Digest::MD5.hexdigest(target))
|
115
|
+
end
|
116
|
+
# hash ids that are links target, delete others
|
117
|
+
doc.search("*[@id]").each do |el|
|
118
|
+
id = el.get_attribute('id')
|
119
|
+
if targets.include?(id)
|
120
|
+
el.set_attribute('id', Digest::MD5.hexdigest(id))
|
121
|
+
else
|
122
|
+
el.remove_attribute('id')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
if @options[:reset_contenteditable]
|
128
|
+
doc.search('*[@contenteditable]').each do |el|
|
129
|
+
el.remove_attribute('contenteditable')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@processed_doc = doc
|
134
|
+
if is_xhtml?
|
135
|
+
# we don't want to encode carriage returns
|
136
|
+
@processed_doc.to_xhtml(:encoding => @options[:output_encoding]).gsub(/&\#(xD|13);/i, "\r")
|
137
|
+
else
|
138
|
+
@processed_doc.to_html(:encoding => @options[:output_encoding])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Create a <tt>style</tt> element with un-mergable rules (e.g. <tt>:hover</tt>)
|
143
|
+
# and write it into the <tt>body</tt>.
|
144
|
+
#
|
145
|
+
# <tt>doc</tt> is an Nokogiri document and <tt>unmergable_css_rules</tt> is a Css::RuleSet.
|
146
|
+
#
|
147
|
+
# @return [::Nokogiri::XML] a document.
|
148
|
+
def write_unmergable_css_rules(doc, unmergable_rules) # :nodoc:
|
149
|
+
styles = unmergable_rules.to_s
|
150
|
+
|
151
|
+
unless styles.empty?
|
152
|
+
style_tag = "<style type=\"text/css\">\n#{styles}</style>"
|
153
|
+
unless (body = doc.search('body')).empty?
|
154
|
+
if doc.at_css('body').children && !doc.at_css('body').children.empty?
|
155
|
+
doc.at_css('body').children.before(::Nokogiri::XML.fragment(style_tag))
|
156
|
+
else
|
157
|
+
doc.at_css('body').add_child(::Nokogiri::XML.fragment(style_tag))
|
158
|
+
end
|
159
|
+
else
|
160
|
+
doc.inner_html = style_tag += doc.inner_html
|
161
|
+
end
|
162
|
+
end
|
163
|
+
doc
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
# Converts the HTML document to a format suitable for plain-text e-mail.
|
168
|
+
#
|
169
|
+
# If present, uses the <body> element as its base; otherwise uses the whole document.
|
170
|
+
#
|
171
|
+
# @return [String] a plain text.
|
172
|
+
def to_plain_text
|
173
|
+
html_src = ''
|
174
|
+
begin
|
175
|
+
html_src = @doc.at("body").inner_html
|
176
|
+
rescue;
|
177
|
+
end
|
178
|
+
|
179
|
+
html_src = @doc.to_html unless html_src and not html_src.empty?
|
180
|
+
convert_to_text(html_src, @options[:line_length], @html_encoding)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Gets the original HTML as a string.
|
184
|
+
# @return [String] HTML.
|
185
|
+
def to_s
|
186
|
+
if is_xhtml?
|
187
|
+
@doc.to_xhtml(:encoding => nil)
|
188
|
+
else
|
189
|
+
@doc.to_html(:encoding => nil)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Load the HTML file and convert it into an Nokogiri document.
|
194
|
+
#
|
195
|
+
# @return [::Nokogiri::XML] a document.
|
196
|
+
def load_html(input) # :nodoc:
|
197
|
+
thing = nil
|
198
|
+
|
199
|
+
# TODO: duplicate options
|
200
|
+
if @options[:with_html_string] or @options[:inline] or input.respond_to?(:read)
|
201
|
+
thing = input
|
202
|
+
elsif @is_local_file
|
203
|
+
@base_dir = File.dirname(input)
|
204
|
+
thing = File.open(input, 'r')
|
205
|
+
else
|
206
|
+
thing = open(input)
|
207
|
+
end
|
208
|
+
|
209
|
+
if thing.respond_to?(:read)
|
210
|
+
thing = thing.read
|
211
|
+
end
|
212
|
+
|
213
|
+
return nil unless thing
|
214
|
+
doc = nil
|
215
|
+
|
216
|
+
# Handle HTML entities
|
217
|
+
if @options[:replace_html_entities] == true and thing.is_a?(String)
|
218
|
+
HTML_ENTITIES.map do |entity, replacement|
|
219
|
+
thing.gsub! entity, replacement
|
220
|
+
end
|
221
|
+
end
|
222
|
+
# Default encoding is ASCII-8BIT (binary) per http://groups.google.com/group/nokogiri-talk/msg/0b81ef0dc180dc74
|
223
|
+
# However, we really don't want to hardcode this. ASCII-8BIT should be the default, but not the only option.
|
224
|
+
if thing.is_a?(String) and RUBY_VERSION =~ /1.9/
|
225
|
+
thing = thing.force_encoding(@options[:input_encoding]).encode!
|
226
|
+
doc = ::Nokogiri::HTML5(thing)
|
227
|
+
else
|
228
|
+
default_encoding = RUBY_PLATFORM == 'java' ? nil : 'BINARY'
|
229
|
+
doc = ::Nokogiri::HTML5(thing)
|
230
|
+
end
|
231
|
+
|
232
|
+
# Fix for removing any CDATA tags from both style and script tags inserted per
|
233
|
+
# https://github.com/sparklemotion/nokogiri/issues/311 and
|
234
|
+
# https://github.com/premailer/premailer/issues/199
|
235
|
+
%w(style script).each do |tag|
|
236
|
+
doc.search(tag).children.each do |child|
|
237
|
+
child.swap(child.text()) if child.cdata?
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
doc
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# For very large documents, it is useful to trade off some memory for performance.
|
247
|
+
# We can build an index of the nodes so we can quickly select by id/class/tagname
|
248
|
+
# instead of search the tree again and again.
|
249
|
+
#
|
250
|
+
# @param page The Nokogiri HTML document to index.
|
251
|
+
# @return [index, set_of_all_nodes, descendants] The index is a hash from key to set of nodes.
|
252
|
+
# The "descendants" is a hash mapping a node to the set of its descendant nodes.
|
253
|
+
def make_index(page)
|
254
|
+
index = {} # Contains a map of tag/class/id names to set of nodes.
|
255
|
+
all_nodes = [] # A plain array of all nodes in the doc. The superset.
|
256
|
+
descendants = {} # Maps node -> set of descendants
|
257
|
+
|
258
|
+
page.traverse do |node|
|
259
|
+
all_nodes.push(node)
|
260
|
+
|
261
|
+
if node != page then
|
262
|
+
index_ancestry(page, node, node.parent, descendants)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Index the node by tag name. This is the least selective
|
266
|
+
# of the three index types empirically.
|
267
|
+
index[node.name] = (index[node.name] || Set.new).add(node)
|
268
|
+
|
269
|
+
# Index the node by all class attributes it possesses.
|
270
|
+
# Classes are modestly selective. Usually more than tag names
|
271
|
+
# but less selective than ids.
|
272
|
+
if node.has_attribute?("class") then
|
273
|
+
node.get_attribute("class").split(/\s+/).each do |c|
|
274
|
+
c = '.' + c
|
275
|
+
index[c] = (index[c] || Set.new).add(node)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
# Index the node by its "id" attribute if it has one.
|
280
|
+
# This is usually the most selective of the three.
|
281
|
+
if node.has_attribute?("id") then
|
282
|
+
id = '#' + node.get_attribute("id")
|
283
|
+
index[id] = (index[id] || Set.new).add(node)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# If an index key isn't there, then we should treat it as an empty set.
|
288
|
+
# This makes the index total and we don't need to special case presence.
|
289
|
+
# Note that the default value will never be modified. So we don't need
|
290
|
+
# default_proc.
|
291
|
+
index.default = Set.new
|
292
|
+
descendants.default = Set.new
|
293
|
+
|
294
|
+
return index, Set.new(all_nodes), descendants
|
295
|
+
end
|
296
|
+
|
297
|
+
# @param doc The top level document
|
298
|
+
# @param elem The element whose ancestry is to be captured
|
299
|
+
# @param parent the current parent in the process of capturing. Should be set to elem.parent for starters.
|
300
|
+
# @param descendants The running hash map of node -> set of nodes that maps descendants of a node.
|
301
|
+
# @return The descendants argument after updating it.
|
302
|
+
def index_ancestry(doc, elem, parent, descendants)
|
303
|
+
if parent then
|
304
|
+
descendants[parent] = (descendants[parent] || Set.new).add(elem)
|
305
|
+
if doc != parent then
|
306
|
+
index_ancestry(doc, elem, parent.parent, descendants)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
descendants
|
310
|
+
end
|
311
|
+
|
312
|
+
# @param index An index hash returned by make_index
|
313
|
+
# @param base The base set of nodes within which the given spec is to be matched.
|
314
|
+
# @param intersection_selector A CSS intersection selector string of the form
|
315
|
+
# "hello.world" or "#blue.diamond". This should not contain spaces.
|
316
|
+
# @return Set of nodes matching the given spec that are present in the base set.
|
317
|
+
def narrow_down_nodes(index, base, intersection_selector)
|
318
|
+
intersection_selector.split(/(?=[.#])/).reduce(base) do |acc, sel|
|
319
|
+
acc = index[sel].intersection(acc)
|
320
|
+
acc
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @param index An index returned by make_index
|
325
|
+
# @param allNodes The set of all nodes in the DOM to search
|
326
|
+
# @param selector A simple CSS tree matching selector of the form "div.container p.item span"
|
327
|
+
# @return Set of matching nodes
|
328
|
+
#
|
329
|
+
# Note that fancy CSS selector syntax is not supported. Anything
|
330
|
+
# not matching the regex /^[-a-zA-Z0-9\s_.#]*$/ should not be passed.
|
331
|
+
# It will return nil when such a selector is passed, so you can take
|
332
|
+
# action on the falsity of the return value.
|
333
|
+
def match_selector(index, all_nodes, descendants, selector)
|
334
|
+
if /[^-a-zA-Z0-9_\s.#]/.match(selector) then
|
335
|
+
return nil
|
336
|
+
end
|
337
|
+
|
338
|
+
take_children = false
|
339
|
+
selector.split(/\s+/).reduce(all_nodes) do |base, spec|
|
340
|
+
desc = base
|
341
|
+
if take_children then
|
342
|
+
desc = Set.new
|
343
|
+
base.each do |n|
|
344
|
+
desc.merge(descendants[n])
|
345
|
+
end
|
346
|
+
else
|
347
|
+
take_children = true
|
348
|
+
end
|
349
|
+
narrow_down_nodes(index, desc, spec)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
data/lib/premailer/executor.rb
CHANGED
@@ -45,7 +45,7 @@ opts = OptionParser.new do |opts|
|
|
45
45
|
end
|
46
46
|
|
47
47
|
opts.on("-j", "--remove-scripts", "Remove <script> elements") do |v|
|
48
|
-
options[:
|
48
|
+
options[:remove_scripts] = v
|
49
49
|
end
|
50
50
|
|
51
51
|
opts.on("-l", "--line-length N", Integer, "Line length for plaintext (default: #{options[:line_length].to_s})") do |v|
|
data/lib/premailer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: premailer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Dunae
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: css_parser
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">"
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0.8'
|
62
62
|
- - "!="
|
@@ -66,7 +66,7 @@ dependencies:
|
|
66
66
|
prerelease: false
|
67
67
|
version_requirements: !ruby/object:Gem::Requirement
|
68
68
|
requirements:
|
69
|
-
- - "
|
69
|
+
- - ">"
|
70
70
|
- !ruby/object:Gem::Version
|
71
71
|
version: '0.8'
|
72
72
|
- - "!="
|
@@ -93,6 +93,9 @@ dependencies:
|
|
93
93
|
- - ">="
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: 1.4.4
|
96
|
+
- - "<="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 1.6.8
|
96
99
|
type: :development
|
97
100
|
prerelease: false
|
98
101
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -100,20 +103,23 @@ dependencies:
|
|
100
103
|
- - ">="
|
101
104
|
- !ruby/object:Gem::Version
|
102
105
|
version: 1.4.4
|
106
|
+
- - "<="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 1.6.8
|
103
109
|
- !ruby/object:Gem::Dependency
|
104
110
|
name: yard
|
105
111
|
requirement: !ruby/object:Gem::Requirement
|
106
112
|
requirements:
|
107
|
-
- - "
|
113
|
+
- - ">="
|
108
114
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0
|
115
|
+
version: '0'
|
110
116
|
type: :development
|
111
117
|
prerelease: false
|
112
118
|
version_requirements: !ruby/object:Gem::Requirement
|
113
119
|
requirements:
|
114
|
-
- - "
|
120
|
+
- - ">="
|
115
121
|
- !ruby/object:Gem::Version
|
116
|
-
version: 0
|
122
|
+
version: '0'
|
117
123
|
- !ruby/object:Gem::Dependency
|
118
124
|
name: redcarpet
|
119
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -199,6 +205,7 @@ files:
|
|
199
205
|
- lib/premailer/adapter.rb
|
200
206
|
- lib/premailer/adapter/hpricot.rb
|
201
207
|
- lib/premailer/adapter/nokogiri.rb
|
208
|
+
- lib/premailer/adapter/nokogiri_fast.rb
|
202
209
|
- lib/premailer/adapter/nokogumbo.rb
|
203
210
|
- lib/premailer/executor.rb
|
204
211
|
- lib/premailer/html_to_plain_text.rb
|
@@ -207,7 +214,8 @@ files:
|
|
207
214
|
- misc/client_support.yaml
|
208
215
|
homepage: http://premailer.dialect.ca/
|
209
216
|
licenses: []
|
210
|
-
metadata:
|
217
|
+
metadata:
|
218
|
+
yard.run: yri
|
211
219
|
post_install_message:
|
212
220
|
rdoc_options: []
|
213
221
|
require_paths:
|
@@ -229,4 +237,3 @@ signing_key:
|
|
229
237
|
specification_version: 4
|
230
238
|
summary: Preflight for HTML e-mail.
|
231
239
|
test_files: []
|
232
|
-
has_rdoc: true
|