derelicte 0.0.1-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +55 -0
- data/Rakefile +1 -0
- data/derelicte.gemspec +27 -0
- data/lib/derelicte.rb +40 -0
- data/lib/derelicte/inliner.rb +11 -0
- data/lib/derelicte/inliner_job.rb +38 -0
- data/lib/derelicte/version.rb +3 -0
- data/lib/jars/antlr-runtime-3.1.jar +0 -0
- data/lib/jars/jstyleparser-1.7.0.jar +0 -0
- data/lib/jars/slf4j-api-1.5.2.jar +0 -0
- data/lib/jars/slf4j-nop-1.5.2.jar +0 -0
- data/lib/jars/xml-apis-1.3.04.jar +0 -0
- data/spec/benchmarks.rb +40 -0
- data/spec/files/ink.css +648 -0
- data/spec/files/ink_boilerplate.html +20 -0
- data/spec/files/simple_dom.html +11 -0
- data/spec/lib/derelicte/inliner_spec.rb +18 -0
- data/spec/spec_helper.rb +14 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ccaedcd6c8d51acebdb783ccffcd591d41c91585
|
4
|
+
data.tar.gz: 2a4345aab83316215d91eb1b7353a55da60c7cdb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 275a0759779c45f3c59dd26f5e06093d7c224181f07373818a5b64faeaed6fcca62c0d414eb0ba1aea44cf34787a7a8ebde3d4927821242aed9289310effd043
|
7
|
+
data.tar.gz: fb9d2393398a19974b7aa4eb677a0ab72ff6d340b72d66dc538488e98ef7db1bbad138bc3704c8a2a6d940407edb81d765a521721737287e3830a9d5936bfbdf
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
derelicte
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
jruby-1.7.10
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Michael Ries
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Derelicte
|
2
|
+
|
3
|
+
![Derelicte Gif](http://24.media.tumblr.com/d7c64874eeae527c2661cda9c107984c/tumblr_msas87gWdt1qaehqco1_400.gif)
|
4
|
+
|
5
|
+
A JRuby specific gem that takes an html template and css file and inlines the css rules into style attributes. This is mostly useful for sending emails.
|
6
|
+
|
7
|
+
This gem attempts to make the inlining process very performant. If you want a richer feature set please see [Roadie](https://github.com/Mange/roadie) or [PreMailer](http://premailer.dialect.ca/).
|
8
|
+
|
9
|
+
This gem was only created because I was involved in a project where the above options were too slow to be feasible in day-to-day operations.
|
10
|
+
|
11
|
+
## How much time are we talking about?
|
12
|
+
|
13
|
+
I used an example welcome email from my current job that uses the [Zurb Ink](https://github.com/zurb/ink) email css framework. It has a slightly complex DOM and lots of CSS. Both benchmarks have a warmup phase where they inline 1,000 emails before being measured (to account for JVM and JIT warming up)
|
14
|
+
|
15
|
+
```bash
|
16
|
+
user system total real
|
17
|
+
roadie 3 0.430000 0.030000 0.460000 ( 0.499000)
|
18
|
+
derelicte 0.000000 0.000000 0.000000 ( 0.010000)
|
19
|
+
```
|
20
|
+
|
21
|
+
Saving roughly 50x is not too shabby, but we definitely make some tradeoffs to get that performance.
|
22
|
+
|
23
|
+
Derelicte uses a handful of native jars (can only used with JRuby) and does not change url paths or inline images via base64 data in tags.
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Simplest possible usage:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
html = "<p>ohai</p>"
|
31
|
+
css = "p { color: #ff0000; }"
|
32
|
+
|
33
|
+
inliner = CSS::Inliner.new
|
34
|
+
inlined_html = inliner.inline(html, css) # => "<p style=\"color: #ff0000;\">ohai</p>"
|
35
|
+
```
|
36
|
+
|
37
|
+
|
38
|
+
## Current Issues
|
39
|
+
|
40
|
+
* doctypes with full URLs (like xhtml) will make the process extremely slow since the standard java libraries attempt to fetch and parse the DTD. It is highly recommended that you simply use an HTML 5 style doctype (<code><!DOCTYPE html></code>)
|
41
|
+
* Since we use a basic XML parser it is pretty sensitive to bad markup. Things like unclosed breaks will cause it to choke. Make sure you double-check your templates.
|
42
|
+
|
43
|
+
```html
|
44
|
+
<!-- Things that will break the XML parsing -->
|
45
|
+
<br>
|
46
|
+
<img href="/icon.png">
|
47
|
+
<meta charset='utf-8'>
|
48
|
+
<hr>
|
49
|
+
|
50
|
+
<!-- These things are okay for the XML parser -->
|
51
|
+
<br/>
|
52
|
+
<img href="/icon.png" />
|
53
|
+
<meta charset='utf-8' />
|
54
|
+
<hr/>
|
55
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/derelicte.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'derelicte/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "derelicte"
|
8
|
+
spec.version = Derelicte::VERSION
|
9
|
+
spec.authors = ["Michael Ries"]
|
10
|
+
spec.email = ["michael@riesd.com"]
|
11
|
+
spec.summary = "JRuby specific css inliner aiming for maximum performance"
|
12
|
+
spec.description = "A JRuby specific gem that takes an html template and css file and inlines the css rules into style attributes. This is mostly useful for sending emails."
|
13
|
+
spec.homepage = "https://github.com/hqmq/derelicte"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.platform = "java"
|
16
|
+
|
17
|
+
spec.files = `git ls-files`.split($/)
|
18
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
23
|
+
spec.add_development_dependency "rake"
|
24
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
25
|
+
spec.add_development_dependency "better_receive", '~> 0.6'
|
26
|
+
spec.add_development_dependency "pry-nav"
|
27
|
+
end
|
data/lib/derelicte.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require "derelicte/version"
|
2
|
+
|
3
|
+
# Load jars
|
4
|
+
require 'jars/antlr-runtime-3.1.jar'
|
5
|
+
require 'jars/slf4j-api-1.5.2.jar'
|
6
|
+
require 'jars/slf4j-nop-1.5.2.jar'
|
7
|
+
require 'jars/xml-apis-1.3.04.jar'
|
8
|
+
require 'jars/jstyleparser-1.7.0.jar'
|
9
|
+
|
10
|
+
# Load local classes
|
11
|
+
require 'derelicte/inliner'
|
12
|
+
require 'derelicte/inliner_job'
|
13
|
+
|
14
|
+
module Derelicte
|
15
|
+
DOCTYPE = "<!DOCTYPE html>"
|
16
|
+
|
17
|
+
def self.css_analyzer_from_str(str)
|
18
|
+
stylesheet = Java::CzVutbrWebCss::CSSFactory.parse(str)
|
19
|
+
Java::CzVutbrWebDomassign::Analyzer.new(stylesheet)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.doc_from_str(str)
|
23
|
+
reader = java.io.StringReader.new(str.to_java_string)
|
24
|
+
source = Java::OrgXmlSax::InputSource.new(reader)
|
25
|
+
fac = Java::JavaxXmlParsers::DocumentBuilderFactory.newInstance()
|
26
|
+
fac.set_validating(false)
|
27
|
+
fac.set_namespace_aware(false)
|
28
|
+
builder = fac.new_document_builder
|
29
|
+
builder.parse(source)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.doc_to_str(doc)
|
33
|
+
serializer = doc.get_implementation
|
34
|
+
.get_feature('LS','3.0')
|
35
|
+
.create_ls_serializer
|
36
|
+
serializer.get_dom_config.set_parameter('xml-declaration', false)
|
37
|
+
doc_str = serializer.write_to_string(doc)
|
38
|
+
DOCTYPE + "\n" + doc_str
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Derelicte
|
2
|
+
class Inliner
|
3
|
+
def inline(html_str, css_str)
|
4
|
+
doc = ::Derelicte.doc_from_str(html_str)
|
5
|
+
analyzer = ::Derelicte.css_analyzer_from_str(css_str)
|
6
|
+
job = InlinerJob.new(doc, analyzer)
|
7
|
+
job.apply_rules_to_doc
|
8
|
+
::Derelicte.doc_to_str(job.doc)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'pry-nav'
|
2
|
+
module Derelicte
|
3
|
+
class InlinerJob
|
4
|
+
attr_reader :doc
|
5
|
+
|
6
|
+
def initialize(doc, analyzer)
|
7
|
+
@doc, @analyzer = doc, analyzer
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply_rules_to_doc
|
11
|
+
each_elements do |element|
|
12
|
+
apply_rules_to(element)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
attr_reader :analyzer
|
18
|
+
|
19
|
+
def apply_rules_to(element)
|
20
|
+
rules = assignments.get(element)
|
21
|
+
return nil if rules.nil?
|
22
|
+
rule_str = rules.map(&:to_s).map(&:chomp).join
|
23
|
+
element.set_attribute('style', rule_str)
|
24
|
+
end
|
25
|
+
|
26
|
+
def assignments
|
27
|
+
@assignments ||= analyzer.assing_declarations_to_dom(doc, "screen", false)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each_elements
|
31
|
+
elements = doc.get_elements_by_tag_name "*"
|
32
|
+
0.upto(elements.get_length - 1).each do |idx|
|
33
|
+
element = elements.item(idx)
|
34
|
+
yield(element)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/benchmarks.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'java_css_inliner'
|
3
|
+
require 'benchmark'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
files_dir = File.join(File.dirname(__FILE__), 'files')
|
7
|
+
html_file = File.join(files_dir, 'ink_boilerplate.html')
|
8
|
+
css_file = File.join(files_dir, 'ink.css')
|
9
|
+
html_str = File.read(html_file)
|
10
|
+
css_str = File.read(css_file)
|
11
|
+
|
12
|
+
output_dir = File.join(File.dirname(__FILE__), '..', 'tmp')
|
13
|
+
output_file = File.join(output_dir, 'ink_boilerplate.inlined.html')
|
14
|
+
FileUtils.mkdir_p(output_dir)
|
15
|
+
|
16
|
+
inliner = CSS::Inliner.new
|
17
|
+
|
18
|
+
# Get an initial benchmark
|
19
|
+
Benchmark.bm(40) do |bm|
|
20
|
+
bm.report('initial run') do
|
21
|
+
inliner.inline(html_str, css_str)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Write a sample file out to disk for inspection
|
26
|
+
File.open(output_file, 'w') do |f|
|
27
|
+
f.write inliner.inline(html_str, css_str)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Warmup the JIT
|
31
|
+
500.times do
|
32
|
+
inliner.inline(html_str, css_str)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Benchmark the JIT optimized time
|
36
|
+
Benchmark.bm(40) do |bm|
|
37
|
+
bm.report('after warmup') do
|
38
|
+
inliner.inline(html_str, css_str)
|
39
|
+
end
|
40
|
+
end
|
data/spec/files/ink.css
ADDED
@@ -0,0 +1,648 @@
|
|
1
|
+
/**********************************************
|
2
|
+
* Ink v1.0.4 - Copyright 2013 ZURB Inc *
|
3
|
+
**********************************************/
|
4
|
+
|
5
|
+
/* Client-specific Styles & Reset */
|
6
|
+
|
7
|
+
#outlook a {
|
8
|
+
padding:0;
|
9
|
+
}
|
10
|
+
|
11
|
+
body{
|
12
|
+
width:100% !important;
|
13
|
+
-webkit-text-size-adjust:100%;
|
14
|
+
-ms-text-size-adjust:100%;
|
15
|
+
margin:0;
|
16
|
+
padding:0;
|
17
|
+
}
|
18
|
+
|
19
|
+
.ExternalClass {
|
20
|
+
width:100%;
|
21
|
+
}
|
22
|
+
|
23
|
+
.ExternalClass,
|
24
|
+
.ExternalClass p,
|
25
|
+
.ExternalClass span,
|
26
|
+
.ExternalClass font,
|
27
|
+
.ExternalClass td,
|
28
|
+
.ExternalClass div {
|
29
|
+
line-height: 100%;
|
30
|
+
}
|
31
|
+
|
32
|
+
#backgroundTable {
|
33
|
+
margin:0;
|
34
|
+
padding:0;
|
35
|
+
width:100% !important;
|
36
|
+
line-height: 100% !important;
|
37
|
+
}
|
38
|
+
|
39
|
+
img {
|
40
|
+
outline:none;
|
41
|
+
text-decoration:none;
|
42
|
+
-ms-interpolation-mode: bicubic;
|
43
|
+
width: auto;
|
44
|
+
max-width: 100%;
|
45
|
+
float: left;
|
46
|
+
clear: both;
|
47
|
+
display: block;
|
48
|
+
}
|
49
|
+
|
50
|
+
center {
|
51
|
+
width: 100%;
|
52
|
+
min-width: 580px;
|
53
|
+
}
|
54
|
+
|
55
|
+
a img {
|
56
|
+
border: none;
|
57
|
+
}
|
58
|
+
|
59
|
+
p {
|
60
|
+
margin: 0 0 0 10px;
|
61
|
+
}
|
62
|
+
|
63
|
+
table {
|
64
|
+
border-spacing: 0;
|
65
|
+
border-collapse: collapse;
|
66
|
+
}
|
67
|
+
|
68
|
+
td {
|
69
|
+
word-break: break-word;
|
70
|
+
-webkit-hyphens: auto;
|
71
|
+
-moz-hyphens: auto;
|
72
|
+
hyphens: auto;
|
73
|
+
border-collapse: collapse !important;
|
74
|
+
}
|
75
|
+
|
76
|
+
table, tr, td {
|
77
|
+
padding: 0;
|
78
|
+
vertical-align: top;
|
79
|
+
text-align: left;
|
80
|
+
}
|
81
|
+
|
82
|
+
hr {
|
83
|
+
color: #d9d9d9;
|
84
|
+
background-color: #d9d9d9;
|
85
|
+
height: 1px;
|
86
|
+
border: none;
|
87
|
+
}
|
88
|
+
|
89
|
+
/* Responsive Grid */
|
90
|
+
|
91
|
+
table.body {
|
92
|
+
height: 100%;
|
93
|
+
width: 100%;
|
94
|
+
}
|
95
|
+
|
96
|
+
table.container {
|
97
|
+
width: 580px;
|
98
|
+
margin: 0 auto;
|
99
|
+
text-align: inherit;
|
100
|
+
}
|
101
|
+
|
102
|
+
table.row {
|
103
|
+
padding: 0px;
|
104
|
+
width: 100%;
|
105
|
+
position: relative;
|
106
|
+
}
|
107
|
+
|
108
|
+
table.container table.row {
|
109
|
+
display: block;
|
110
|
+
}
|
111
|
+
|
112
|
+
td.wrapper {
|
113
|
+
padding: 10px 20px 0px 0px;
|
114
|
+
position: relative;
|
115
|
+
}
|
116
|
+
|
117
|
+
table.columns,
|
118
|
+
table.column {
|
119
|
+
margin: 0 auto;
|
120
|
+
}
|
121
|
+
|
122
|
+
table.columns td,
|
123
|
+
table.column td {
|
124
|
+
padding: 0px 0px 10px;
|
125
|
+
}
|
126
|
+
|
127
|
+
table.columns td.sub-columns,
|
128
|
+
table.column td.sub-columns,
|
129
|
+
table.columns td.sub-column,
|
130
|
+
table.column td.sub-column {
|
131
|
+
padding-right: 10px;
|
132
|
+
}
|
133
|
+
|
134
|
+
td.sub-column, td.sub-columns {
|
135
|
+
min-width: 0px;
|
136
|
+
}
|
137
|
+
|
138
|
+
table.row td.last,
|
139
|
+
table.container td.last {
|
140
|
+
padding-right: 0px;
|
141
|
+
}
|
142
|
+
|
143
|
+
table.one { width: 30px; }
|
144
|
+
table.two { width: 80px; }
|
145
|
+
table.three { width: 130px; }
|
146
|
+
table.four { width: 180px; }
|
147
|
+
table.five { width: 230px; }
|
148
|
+
table.six { width: 280px; }
|
149
|
+
table.seven { width: 330px; }
|
150
|
+
table.eight { width: 380px; }
|
151
|
+
table.nine { width: 430px; }
|
152
|
+
table.ten { width: 480px; }
|
153
|
+
table.eleven { width: 530px; }
|
154
|
+
table.twelve { width: 580px; }
|
155
|
+
|
156
|
+
table.one center { min-width: 30px; }
|
157
|
+
table.two center { min-width: 80px; }
|
158
|
+
table.three center { min-width: 130px; }
|
159
|
+
table.four center { min-width: 180px; }
|
160
|
+
table.five center { min-width: 230px; }
|
161
|
+
table.six center { min-width: 280px; }
|
162
|
+
table.seven center { min-width: 330px; }
|
163
|
+
table.eight center { min-width: 380px; }
|
164
|
+
table.nine center { min-width: 430px; }
|
165
|
+
table.ten center { min-width: 480px; }
|
166
|
+
table.eleven center { min-width: 530px; }
|
167
|
+
table.twelve center { min-width: 580px; }
|
168
|
+
|
169
|
+
.body .columns td.one,
|
170
|
+
.body .column td.one, { width: 8.333333% !important; }
|
171
|
+
.body .columns td.two,
|
172
|
+
.body .column td.two { width: 16.666666% !important; }
|
173
|
+
.body .columns td.three,
|
174
|
+
.body .column td.three { width: 25% !important; }
|
175
|
+
.body .columns td.four,
|
176
|
+
.body .column td.four { width: 33.333333% !important; }
|
177
|
+
.body .columns td.five,
|
178
|
+
.body .column td.five { width: 41.666666% !important; }
|
179
|
+
.body .columns td.six,
|
180
|
+
.body .column td.six { width: 50% !important; }
|
181
|
+
.body .columns td.seven,
|
182
|
+
.body .column td.seven { width: 58.333333% !important; }
|
183
|
+
.body .columns td.eight,
|
184
|
+
.body .column td.eight { width: 66.666666% !important; }
|
185
|
+
.body .columns td.nine,
|
186
|
+
.body .column td.nine { width: 75% !important; }
|
187
|
+
.body .columns td.ten,
|
188
|
+
.body .column td.ten { width: 83.333333% !important; }
|
189
|
+
.body .columns td.eleven,
|
190
|
+
.body .column td.eleven { width: 91.666666% !important; }
|
191
|
+
.body .columns td.twelve,
|
192
|
+
.body .column td.twelve { width: 100% !important; }
|
193
|
+
|
194
|
+
td.offset-by-one { padding-left: 50px; }
|
195
|
+
td.offset-by-two { padding-left: 100px; }
|
196
|
+
td.offset-by-three { padding-left: 150px; }
|
197
|
+
td.offset-by-four { padding-left: 200px; }
|
198
|
+
td.offset-by-five { padding-left: 250px; }
|
199
|
+
td.offset-by-six { padding-left: 300px; }
|
200
|
+
td.offset-by-seven { padding-left: 350px; }
|
201
|
+
td.offset-by-eight { padding-left: 400px; }
|
202
|
+
td.offset-by-nine { padding-left: 450px; }
|
203
|
+
td.offset-by-ten { padding-left: 500px; }
|
204
|
+
td.offset-by-eleven { padding-left: 550px; }
|
205
|
+
|
206
|
+
td.sub-offset-by-one { padding-left: 5.172413% !important; }
|
207
|
+
td.sub-offset-by-two { padding-left: 13.793102% !important; }
|
208
|
+
td.sub-offset-by-three { padding-left: 22.413791% !important; }
|
209
|
+
td.sub-offset-by-four { padding-left: 31.034480% !important; }
|
210
|
+
td.sub-offset-by-five { padding-left: 39.655169% !important; }
|
211
|
+
td.sub-offset-by-six { padding-left: 48.275858% !important; }
|
212
|
+
td.sub-offset-by-seven { padding-left: 56.896547% !important; }
|
213
|
+
td.sub-offset-by-eight { padding-left: 65.517236% !important; }
|
214
|
+
td.sub-offset-by-nine { padding-left: 74.137925% !important; }
|
215
|
+
td.sub-offset-by-ten { padding-left: 82.758614% !important; }
|
216
|
+
td.sub-offset-by-eleven { padding-left: 91.379303% !important; }
|
217
|
+
|
218
|
+
td.expander {
|
219
|
+
visibility: hidden;
|
220
|
+
width: 0px;
|
221
|
+
padding: 0 !important;
|
222
|
+
}
|
223
|
+
|
224
|
+
table.columns .text-pad,
|
225
|
+
table.column .text-pad {
|
226
|
+
padding-left: 10px;
|
227
|
+
padding-right: 10px;
|
228
|
+
}
|
229
|
+
|
230
|
+
table.columns .left-text-pad,
|
231
|
+
table.columns .text-pad-left,
|
232
|
+
table.column .left-text-pad,
|
233
|
+
table.column .text-pad-left {
|
234
|
+
padding-left: 10px;
|
235
|
+
}
|
236
|
+
|
237
|
+
table.columns .right-text-pad,
|
238
|
+
table.columns .text-pad-right,
|
239
|
+
table.column .right-text-pad,
|
240
|
+
table.column .text-pad-right {
|
241
|
+
padding-right: 10px;
|
242
|
+
}
|
243
|
+
|
244
|
+
/* Block Grid */
|
245
|
+
|
246
|
+
.block-grid {
|
247
|
+
width: 100%;
|
248
|
+
max-width: 580px;
|
249
|
+
}
|
250
|
+
|
251
|
+
.block-grid td {
|
252
|
+
display: inline-block;
|
253
|
+
padding:10px;
|
254
|
+
}
|
255
|
+
|
256
|
+
.two-up td {
|
257
|
+
width:270px;
|
258
|
+
}
|
259
|
+
|
260
|
+
.three-up td {
|
261
|
+
width:173px;
|
262
|
+
}
|
263
|
+
|
264
|
+
.four-up td {
|
265
|
+
width:125px;
|
266
|
+
}
|
267
|
+
|
268
|
+
.five-up td {
|
269
|
+
width:96px;
|
270
|
+
}
|
271
|
+
|
272
|
+
.six-up td {
|
273
|
+
width:76px;
|
274
|
+
}
|
275
|
+
|
276
|
+
.seven-up td {
|
277
|
+
width:62px;
|
278
|
+
}
|
279
|
+
|
280
|
+
.eight-up td {
|
281
|
+
width:52px;
|
282
|
+
}
|
283
|
+
|
284
|
+
/* Alignment & Visibility Classes */
|
285
|
+
|
286
|
+
table.center, td.center {
|
287
|
+
text-align: center;
|
288
|
+
}
|
289
|
+
|
290
|
+
h1.center,
|
291
|
+
h2.center,
|
292
|
+
h3.center,
|
293
|
+
h4.center,
|
294
|
+
h5.center,
|
295
|
+
h6.center {
|
296
|
+
text-align: center;
|
297
|
+
}
|
298
|
+
|
299
|
+
span.center {
|
300
|
+
display: block;
|
301
|
+
width: 100%;
|
302
|
+
text-align: center;
|
303
|
+
}
|
304
|
+
|
305
|
+
img.center {
|
306
|
+
margin: 0 auto;
|
307
|
+
float: none;
|
308
|
+
}
|
309
|
+
|
310
|
+
.show-for-small,
|
311
|
+
.hide-for-desktop {
|
312
|
+
display: none;
|
313
|
+
}
|
314
|
+
|
315
|
+
/* Typography */
|
316
|
+
|
317
|
+
body, table.body, h1, h2, h3, h4, h5, h6, p {
|
318
|
+
color: #222222;
|
319
|
+
font-family: "Helvetica", "Arial", sans-serif;
|
320
|
+
font-weight: normal;
|
321
|
+
padding:0;
|
322
|
+
margin: 0;
|
323
|
+
text-align: left;
|
324
|
+
line-height: 1.3;
|
325
|
+
}
|
326
|
+
|
327
|
+
h1, h2, h3, h4, h5, h6 {
|
328
|
+
word-break: normal;
|
329
|
+
}
|
330
|
+
|
331
|
+
h1 {font-size: 40px;}
|
332
|
+
h2 {font-size: 36px;}
|
333
|
+
h3 {font-size: 32px;}
|
334
|
+
h4 {font-size: 28px;}
|
335
|
+
h5 {font-size: 24px;}
|
336
|
+
h6 {font-size: 20px;}
|
337
|
+
body, table.body, p {font-size: 14px;line-height:19px;}
|
338
|
+
|
339
|
+
p {
|
340
|
+
padding-bottom: 10px;
|
341
|
+
}
|
342
|
+
|
343
|
+
small {
|
344
|
+
font-size: 10px;
|
345
|
+
}
|
346
|
+
|
347
|
+
a {
|
348
|
+
color: #2ba6cb;
|
349
|
+
text-decoration: none;
|
350
|
+
}
|
351
|
+
|
352
|
+
a:hover {
|
353
|
+
color: #2795b6 !important;
|
354
|
+
}
|
355
|
+
|
356
|
+
a:active {
|
357
|
+
color: #2795b6 !important;
|
358
|
+
}
|
359
|
+
|
360
|
+
a:visited {
|
361
|
+
color: #2ba6cb !important;
|
362
|
+
}
|
363
|
+
|
364
|
+
h1 a,
|
365
|
+
h2 a,
|
366
|
+
h3 a,
|
367
|
+
h4 a,
|
368
|
+
h5 a,
|
369
|
+
h6 a {
|
370
|
+
color: #2ba6cb;
|
371
|
+
}
|
372
|
+
|
373
|
+
h1 a:active,
|
374
|
+
h2 a:active,
|
375
|
+
h3 a:active,
|
376
|
+
h4 a:active,
|
377
|
+
h5 a:active,
|
378
|
+
h6 a:active {
|
379
|
+
color: #2ba6cb !important;
|
380
|
+
}
|
381
|
+
|
382
|
+
h1 a:visited,
|
383
|
+
h2 a:visited,
|
384
|
+
h3 a:visited,
|
385
|
+
h4 a:visited,
|
386
|
+
h5 a:visited,
|
387
|
+
h6 a:visited {
|
388
|
+
color: #2ba6cb !important;
|
389
|
+
}
|
390
|
+
|
391
|
+
/* Panels */
|
392
|
+
|
393
|
+
td.panel {
|
394
|
+
background: #f2f2f2;
|
395
|
+
border: 1px solid #d9d9d9;
|
396
|
+
padding: 10px !important;
|
397
|
+
}
|
398
|
+
|
399
|
+
/* Buttons */
|
400
|
+
|
401
|
+
table.button,
|
402
|
+
table.tiny-button,
|
403
|
+
table.small-button,
|
404
|
+
table.medium-button,
|
405
|
+
table.large-button {
|
406
|
+
width: 100%;
|
407
|
+
overflow: hidden;
|
408
|
+
}
|
409
|
+
|
410
|
+
table.button td,
|
411
|
+
table.tiny-button td,
|
412
|
+
table.small-button td,
|
413
|
+
table.medium-button td,
|
414
|
+
table.large-button td {
|
415
|
+
display: block;
|
416
|
+
width: auto !important;
|
417
|
+
text-align: center;
|
418
|
+
background: #2ba6cb;
|
419
|
+
border: 1px solid #2284a1;
|
420
|
+
color: #ffffff;
|
421
|
+
padding: 8px 0;
|
422
|
+
}
|
423
|
+
|
424
|
+
table.tiny-button td {
|
425
|
+
padding: 5px 0 4px;
|
426
|
+
}
|
427
|
+
|
428
|
+
table.small-button td {
|
429
|
+
padding: 8px 0 7px;
|
430
|
+
}
|
431
|
+
|
432
|
+
table.medium-button td {
|
433
|
+
padding: 12px 0 10px;
|
434
|
+
}
|
435
|
+
|
436
|
+
table.large-button td {
|
437
|
+
padding: 21px 0 18px;
|
438
|
+
}
|
439
|
+
|
440
|
+
table.button td a,
|
441
|
+
table.tiny-button td a,
|
442
|
+
table.small-button td a,
|
443
|
+
table.medium-button td a,
|
444
|
+
table.large-button td a {
|
445
|
+
font-weight: bold;
|
446
|
+
text-decoration: none;
|
447
|
+
font-family: Helvetica, Arial, sans-serif;
|
448
|
+
color: #ffffff;
|
449
|
+
font-size: 16px;
|
450
|
+
}
|
451
|
+
|
452
|
+
table.tiny-button td a {
|
453
|
+
font-size: 12px;
|
454
|
+
font-weight: normal;
|
455
|
+
}
|
456
|
+
|
457
|
+
table.small-button td a {
|
458
|
+
font-size: 16px;
|
459
|
+
}
|
460
|
+
|
461
|
+
table.medium-button td a {
|
462
|
+
font-size: 20px;
|
463
|
+
}
|
464
|
+
|
465
|
+
table.large-button td a {
|
466
|
+
font-size: 24px;
|
467
|
+
}
|
468
|
+
|
469
|
+
table.button:hover td,
|
470
|
+
table.button:visited td,
|
471
|
+
table.button:active td {
|
472
|
+
background: #2795b6 !important;
|
473
|
+
}
|
474
|
+
|
475
|
+
table.button:hover td a,
|
476
|
+
table.button:visited td a,
|
477
|
+
table.button:active td a {
|
478
|
+
color: #fff !important;
|
479
|
+
}
|
480
|
+
|
481
|
+
table.button:hover td,
|
482
|
+
table.tiny-button:hover td,
|
483
|
+
table.small-button:hover td,
|
484
|
+
table.medium-button:hover td,
|
485
|
+
table.large-button:hover td {
|
486
|
+
background: #2795b6 !important;
|
487
|
+
}
|
488
|
+
|
489
|
+
table.button:hover td a,
|
490
|
+
table.button:active td a,
|
491
|
+
table.button td a:visited,
|
492
|
+
table.tiny-button:hover td a,
|
493
|
+
table.tiny-button:active td a,
|
494
|
+
table.tiny-button td a:visited,
|
495
|
+
table.small-button:hover td a,
|
496
|
+
table.small-button:active td a,
|
497
|
+
table.small-button td a:visited,
|
498
|
+
table.medium-button:hover td a,
|
499
|
+
table.medium-button:active td a,
|
500
|
+
table.medium-button td a:visited,
|
501
|
+
table.large-button:hover td a,
|
502
|
+
table.large-button:active td a,
|
503
|
+
table.large-button td a:visited {
|
504
|
+
color: #ffffff !important;
|
505
|
+
}
|
506
|
+
|
507
|
+
table.secondary td {
|
508
|
+
background: #e9e9e9;
|
509
|
+
border-color: #d0d0d0;
|
510
|
+
color: #555;
|
511
|
+
}
|
512
|
+
|
513
|
+
table.secondary td a {
|
514
|
+
color: #555;
|
515
|
+
}
|
516
|
+
|
517
|
+
table.secondary:hover td {
|
518
|
+
background: #d0d0d0 !important;
|
519
|
+
color: #555;
|
520
|
+
}
|
521
|
+
|
522
|
+
table.secondary:hover td a,
|
523
|
+
table.secondary td a:visited,
|
524
|
+
table.secondary:active td a {
|
525
|
+
color: #555 !important;
|
526
|
+
}
|
527
|
+
|
528
|
+
table.success td {
|
529
|
+
background: #5da423;
|
530
|
+
border-color: #457a1a;
|
531
|
+
}
|
532
|
+
|
533
|
+
table.success:hover td {
|
534
|
+
background: #457a1a !important;
|
535
|
+
}
|
536
|
+
|
537
|
+
table.alert td {
|
538
|
+
background: #c60f13;
|
539
|
+
border-color: #970b0e;
|
540
|
+
}
|
541
|
+
|
542
|
+
table.alert:hover td {
|
543
|
+
background: #970b0e !important;
|
544
|
+
}
|
545
|
+
|
546
|
+
table.radius td {
|
547
|
+
-webkit-border-radius: 3px;
|
548
|
+
-moz-border-radius: 3px;
|
549
|
+
border-radius: 3px;
|
550
|
+
}
|
551
|
+
|
552
|
+
table.round td {
|
553
|
+
-webkit-border-radius: 500px;
|
554
|
+
-moz-border-radius: 500px;
|
555
|
+
border-radius: 500px;
|
556
|
+
}
|
557
|
+
|
558
|
+
/* Outlook First */
|
559
|
+
|
560
|
+
body.outlook p {
|
561
|
+
display: inline !important;
|
562
|
+
}
|
563
|
+
|
564
|
+
/* Media Queries */
|
565
|
+
|
566
|
+
@media only screen and (max-width: 600px) {
|
567
|
+
|
568
|
+
table[class="body"] img {
|
569
|
+
width: auto !important;
|
570
|
+
height: auto !important;
|
571
|
+
}
|
572
|
+
|
573
|
+
table[class="body"] center {
|
574
|
+
min-width: 0 !important;
|
575
|
+
}
|
576
|
+
|
577
|
+
table[class="body"] .container {
|
578
|
+
width: 95% !important;
|
579
|
+
}
|
580
|
+
|
581
|
+
table[class="body"] .row {
|
582
|
+
width: 100% !important;
|
583
|
+
display: block !important;
|
584
|
+
}
|
585
|
+
|
586
|
+
table[class="body"] .wrapper {
|
587
|
+
display: block !important;
|
588
|
+
padding-right: 0 !important;
|
589
|
+
}
|
590
|
+
|
591
|
+
table[class="body"] .columns,
|
592
|
+
table[class="body"] .column {
|
593
|
+
table-layout: fixed !important;
|
594
|
+
float: none !important;
|
595
|
+
width: 100% !important;
|
596
|
+
padding-right: 0px !important;
|
597
|
+
padding-left: 0px !important;
|
598
|
+
display: block !important;
|
599
|
+
}
|
600
|
+
|
601
|
+
table[class="body"] .wrapper.first .columns,
|
602
|
+
table[class="body"] .wrapper.first .column {
|
603
|
+
display: table !important;
|
604
|
+
}
|
605
|
+
|
606
|
+
table[class="body"] table.columns td,
|
607
|
+
table[class="body"] table.column td {
|
608
|
+
width: 100% !important;
|
609
|
+
}
|
610
|
+
|
611
|
+
table[class="body"] td.offset-by-one,
|
612
|
+
table[class="body"] td.offset-by-two,
|
613
|
+
table[class="body"] td.offset-by-three,
|
614
|
+
table[class="body"] td.offset-by-four,
|
615
|
+
table[class="body"] td.offset-by-five,
|
616
|
+
table[class="body"] td.offset-by-six,
|
617
|
+
table[class="body"] td.offset-by-seven,
|
618
|
+
table[class="body"] td.offset-by-eight,
|
619
|
+
table[class="body"] td.offset-by-nine,
|
620
|
+
table[class="body"] td.offset-by-ten,
|
621
|
+
table[class="body"] td.offset-by-eleven {
|
622
|
+
padding-left: 0 !important;
|
623
|
+
}
|
624
|
+
|
625
|
+
table[class="body"] .expander {
|
626
|
+
width: 9999px !important;
|
627
|
+
}
|
628
|
+
|
629
|
+
table[class="body"] .right-text-pad,
|
630
|
+
table[class="body"] .text-pad-right {
|
631
|
+
padding-left: 10px !important;
|
632
|
+
}
|
633
|
+
|
634
|
+
table[class="body"] .left-text-pad,
|
635
|
+
table[class="body"] .text-pad-left {
|
636
|
+
padding-right: 10px !important;
|
637
|
+
}
|
638
|
+
|
639
|
+
table[class="body"] .hide-for-small,
|
640
|
+
table[class="body"] .show-for-desktop {
|
641
|
+
display: none !important;
|
642
|
+
}
|
643
|
+
|
644
|
+
table[class="body"] .show-for-small,
|
645
|
+
table[class="body"] .hide-for-desktop {
|
646
|
+
display: inherit !important;
|
647
|
+
}
|
648
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
5
|
+
<meta name="viewport" content="width=device-width"/>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<table class="body">
|
9
|
+
<tr>
|
10
|
+
<td class="center" align="center" valign="top">
|
11
|
+
<center>
|
12
|
+
|
13
|
+
<!-- Email Content -->
|
14
|
+
|
15
|
+
</center>
|
16
|
+
</td>
|
17
|
+
</tr>
|
18
|
+
</table>
|
19
|
+
</body>
|
20
|
+
</html>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Derelicte::Inliner do
|
4
|
+
let(:css) { "p { color: #ff0000; }" }
|
5
|
+
let(:html) { "<div><p>ohai</p></div>" }
|
6
|
+
|
7
|
+
subject { described_class.new }
|
8
|
+
it "should apply css rules to html elements" do
|
9
|
+
inlined_html = subject.inline(html, css)
|
10
|
+
expect(inlined_html).to include('<div><p style="color: #ff0000;">ohai</p></div>')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should keep doctypes intact" do
|
14
|
+
html = file_contents('simple_dom.html')
|
15
|
+
inlined_html = subject.inline(html, "")
|
16
|
+
expect(inlined_html).to eq(html.chomp)
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'rspec'
|
3
|
+
require 'better_receive'
|
4
|
+
require 'derelicte'
|
5
|
+
|
6
|
+
RSpec.configure do |c|
|
7
|
+
c.color = true
|
8
|
+
c.order = :rand
|
9
|
+
end
|
10
|
+
|
11
|
+
def file_contents(basename)
|
12
|
+
path = File.join( File.dirname(__FILE__), 'files', basename )
|
13
|
+
File.read(path)
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: derelicte
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: java
|
6
|
+
authors:
|
7
|
+
- Michael Ries
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
version_requirements: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
requirement: !ruby/object:Gem::Requirement
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: '1.5'
|
25
|
+
prerelease: false
|
26
|
+
type: :development
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
prerelease: false
|
40
|
+
type: :development
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.14'
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '2.14'
|
53
|
+
prerelease: false
|
54
|
+
type: :development
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: better_receive
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.6'
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ~>
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0.6'
|
67
|
+
prerelease: false
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-nav
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
prerelease: false
|
82
|
+
type: :development
|
83
|
+
description: A JRuby specific gem that takes an html template and css file and inlines the css rules into style attributes. This is mostly useful for sending emails.
|
84
|
+
email:
|
85
|
+
- michael@riesd.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- .gitignore
|
91
|
+
- .ruby-gemset
|
92
|
+
- .ruby-version
|
93
|
+
- Gemfile
|
94
|
+
- LICENSE.txt
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- derelicte.gemspec
|
98
|
+
- lib/derelicte.rb
|
99
|
+
- lib/derelicte/inliner.rb
|
100
|
+
- lib/derelicte/inliner_job.rb
|
101
|
+
- lib/derelicte/version.rb
|
102
|
+
- lib/jars/antlr-runtime-3.1.jar
|
103
|
+
- lib/jars/jstyleparser-1.7.0.jar
|
104
|
+
- lib/jars/slf4j-api-1.5.2.jar
|
105
|
+
- lib/jars/slf4j-nop-1.5.2.jar
|
106
|
+
- lib/jars/xml-apis-1.3.04.jar
|
107
|
+
- spec/benchmarks.rb
|
108
|
+
- spec/files/ink.css
|
109
|
+
- spec/files/ink_boilerplate.html
|
110
|
+
- spec/files/simple_dom.html
|
111
|
+
- spec/lib/derelicte/inliner_spec.rb
|
112
|
+
- spec/spec_helper.rb
|
113
|
+
homepage: https://github.com/hqmq/derelicte
|
114
|
+
licenses:
|
115
|
+
- MIT
|
116
|
+
metadata: {}
|
117
|
+
post_install_message:
|
118
|
+
rdoc_options: []
|
119
|
+
require_paths:
|
120
|
+
- lib
|
121
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - '>='
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
requirements: []
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 2.2.1
|
134
|
+
signing_key:
|
135
|
+
specification_version: 4
|
136
|
+
summary: JRuby specific css inliner aiming for maximum performance
|
137
|
+
test_files:
|
138
|
+
- spec/benchmarks.rb
|
139
|
+
- spec/files/ink.css
|
140
|
+
- spec/files/ink_boilerplate.html
|
141
|
+
- spec/files/simple_dom.html
|
142
|
+
- spec/lib/derelicte/inliner_spec.rb
|
143
|
+
- spec/spec_helper.rb
|