bootstrap-email 1.0.0.alpha2.1 → 1.0.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/VERSION +1 -1
- data/core/bootstrap-head.scss +88 -15
- data/core/scss/_utilities.scss +1 -1
- data/core/scss/components/_grid.scss +25 -20
- data/core/scss/components/_stack.scss +18 -12
- data/core/scss/utilities/_sizing.scss +4 -0
- data/core/scss/utilities/_valign.scss +1 -1
- data/core/templates/body.html +9 -0
- data/core/templates/{container.html.erb → container.html} +2 -2
- data/core/templates/div.html +3 -0
- data/core/templates/table-left.html +9 -0
- data/core/templates/table-to-tbody.html +5 -0
- data/core/templates/table-to-tr.html +7 -0
- data/core/templates/table.html +9 -0
- data/core/templates/td.html +3 -0
- data/core/templates/{tr.html.erb → tr.html} +1 -1
- data/lib/bootstrap-email/bootstrap_email_cli.rb +3 -4
- data/lib/bootstrap-email/compiler.rb +33 -28
- data/lib/bootstrap-email/config.rb +50 -0
- data/lib/bootstrap-email/{components → converters}/alert.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/align.rb +3 -3
- data/lib/bootstrap-email/{components → converters}/badge.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/base.rb +16 -7
- data/lib/bootstrap-email/{components → converters}/block.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/body.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/button.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/card.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/color.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/container.rb +1 -1
- data/lib/bootstrap-email/converters/force_encoding.rb +14 -0
- data/lib/bootstrap-email/{components → converters}/grid.rb +2 -2
- data/lib/bootstrap-email/{components → converters}/head_style.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/hr.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/margin.rb +3 -3
- data/lib/bootstrap-email/converters/padding.rb +16 -0
- data/lib/bootstrap-email/{components → converters}/paragraph.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/preview_text.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/spacer.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/spacing.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/stack.rb +7 -7
- data/lib/bootstrap-email/{components → converters}/table.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/version_comment.rb +1 -1
- data/lib/bootstrap-email/sass_cache.rb +16 -19
- data/lib/bootstrap-email/setup.rb +27 -0
- data/lib/bootstrap-email.rb +5 -3
- metadata +38 -37
- data/core/templates/body.html.erb +0 -9
- data/core/templates/div.html.erb +0 -3
- data/core/templates/table-left.html.erb +0 -9
- data/core/templates/table-to-tbody.html.erb +0 -5
- data/core/templates/table-to-tr.html.erb +0 -7
- data/core/templates/table.html.erb +0 -9
- data/core/templates/td.html.erb +0 -3
- data/lib/bootstrap-email/components/force_encoding.rb +0 -16
- data/lib/bootstrap-email/components/padding.rb +0 -16
- data/lib/bootstrap-email/initialize.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0363b6b03e3d4fbca8e0e52c55c2a3120671c720807fb970f96c2a711bedc60
|
4
|
+
data.tar.gz: 8462e35921bcfe274abed980d895e1ca57fc06f30d91e8c70ff15f9355175ef5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50e8c9a0967f48e016b24cb8f46f92e5ff4fb3b8682b23131bea81cd73d6aec5334c6098c77ce2ecf1a811acc0f459c8feae122bfb997815fc955c2a29a4525d
|
7
|
+
data.tar.gz: 68650d469df9644d5c1c9f2ad990ee33697f297382c9f81111f5553d01a14c7071de409a82ab5353178a961397d9e496dd30aaf42b5d99b1c9e2bcb86450bbcb
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.0.0
|
1
|
+
1.0.0
|
data/core/bootstrap-head.scss
CHANGED
@@ -8,6 +8,11 @@
|
|
8
8
|
@import 'scss/selectors_for_utils';
|
9
9
|
@import 'scss/helper_groups';
|
10
10
|
|
11
|
+
// Force base font in Outlook
|
12
|
+
body, table, td {
|
13
|
+
font-family: $font-family-base !important;
|
14
|
+
}
|
15
|
+
|
11
16
|
// Forces Outlook.com to display emails at full width
|
12
17
|
.ExternalClass {
|
13
18
|
width: 100%;
|
@@ -66,18 +71,57 @@ table:not([class^=s-]) {
|
|
66
71
|
/*! allow_purge_after */
|
67
72
|
|
68
73
|
@media screen and (max-width: 600px) {
|
74
|
+
// Row Gap
|
75
|
+
@each $space, $value in $spacers {
|
76
|
+
.gap-#{$space}.row,
|
77
|
+
.gap-x-#{$space}.row {
|
78
|
+
margin-right: -1 * $value !important;
|
79
|
+
& > table > tbody > tr > td {
|
80
|
+
padding-right: $value !important;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
.gap-#{$space}.row,
|
84
|
+
.gap-y-#{$space}.row {
|
85
|
+
margin-bottom: -1 * $value !important;
|
86
|
+
& > table > tbody > tr > td {
|
87
|
+
padding-bottom: $value !important;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
// Stack Gap
|
93
|
+
@each $space, $value in $spacers {
|
94
|
+
table.gap-lg-#{$space}.stack-x > tbody > tr > td {
|
95
|
+
padding-right: 0 !important;
|
96
|
+
}
|
97
|
+
table.gap-lg-#{$space}.stack-y > tbody > tr > td {
|
98
|
+
padding-bottom: 0 !important;
|
99
|
+
}
|
100
|
+
}
|
101
|
+
@each $space, $value in $spacers {
|
102
|
+
table.gap-#{$space}.stack-x > tbody > tr > td {
|
103
|
+
padding-right: $value !important;
|
104
|
+
}
|
105
|
+
table.gap-#{$space}.stack-y > tbody > tr > td {
|
106
|
+
padding-bottom: $value !important;
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
69
110
|
// Grid
|
70
111
|
@each $key, $value in $grid-cols {
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
padding-left: 0 !important;
|
77
|
-
padding-right: 0 !important;
|
78
|
-
}
|
112
|
+
td.col-lg-#{$key} {
|
113
|
+
display: block;
|
114
|
+
width: 100% !important;
|
115
|
+
padding-left: 0 !important;
|
116
|
+
padding-right: 0 !important;
|
79
117
|
}
|
80
118
|
}
|
119
|
+
td.col-lg {
|
120
|
+
display: block;
|
121
|
+
width: 100% !important;
|
122
|
+
padding-left: 0 !important;
|
123
|
+
padding-right: 0 !important;
|
124
|
+
}
|
81
125
|
|
82
126
|
// Display
|
83
127
|
@each $display in $display-type {
|
@@ -91,26 +135,56 @@ table:not([class^=s-]) {
|
|
91
135
|
}
|
92
136
|
}
|
93
137
|
|
94
|
-
// Sizing
|
138
|
+
// Sizing Max Width / Height
|
139
|
+
@each $name, $property in $sizing-types {
|
140
|
+
@include sizing-util('.max-#{$name}-lg-full') {
|
141
|
+
max-#{$property}: unset !important;
|
142
|
+
#{$property}: auto !important;
|
143
|
+
}
|
144
|
+
@each $size, $value in $sizing {
|
145
|
+
@include sizing-util('.max-#{$name}-lg-#{$size}') {
|
146
|
+
max-#{$property}: unset !important;
|
147
|
+
#{$property}: auto !important;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
@each $name, $property in $sizing-types {
|
152
|
+
@include sizing-util('.max-#{$name}-full') {
|
153
|
+
max-#{$property}: 100% !important;
|
154
|
+
#{$property}: 100% !important;
|
155
|
+
}
|
156
|
+
@each $size, $value in $sizing {
|
157
|
+
@include sizing-util('.max-#{$name}-#{$size}') {
|
158
|
+
max-#{$property}: $value !important;
|
159
|
+
#{$property}: 100% !important;
|
160
|
+
}
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
// Sizing Width / Height
|
95
165
|
@each $name, $property in $sizing-types {
|
96
166
|
@include sizing-util('.#{$name}-lg-full') {
|
97
|
-
|
167
|
+
#{$property}: auto !important;
|
168
|
+
}
|
169
|
+
@include sizing-util('.#{$name}-lg-auto') {
|
98
170
|
#{$property}: auto !important;
|
99
171
|
}
|
100
172
|
@each $size, $value in $sizing {
|
101
173
|
@include sizing-util('.#{$name}-lg-#{$size}') {
|
102
|
-
max-#{$property}: auto !important;
|
103
174
|
#{$property}: auto !important;
|
104
175
|
}
|
105
176
|
}
|
177
|
+
}
|
178
|
+
@each $name, $property in $sizing-types {
|
106
179
|
@include sizing-util('.#{$name}-full') {
|
107
|
-
max-#{$property}: 100% !important;
|
108
180
|
#{$property}: 100% !important;
|
109
181
|
}
|
182
|
+
@include sizing-util('.#{$name}-auto') {
|
183
|
+
#{$property}: auto !important;
|
184
|
+
}
|
110
185
|
@each $size, $value in $sizing {
|
111
186
|
@include sizing-util('.#{$name}-#{$size}') {
|
112
|
-
|
113
|
-
#{$property}: 100% !important;
|
187
|
+
#{$property}: $value !important;
|
114
188
|
}
|
115
189
|
}
|
116
190
|
}
|
@@ -132,7 +206,6 @@ table:not([class^=s-]) {
|
|
132
206
|
line-height: 0 !important;
|
133
207
|
height: 0 !important;
|
134
208
|
}
|
135
|
-
|
136
209
|
@each $size, $value in $spacers {
|
137
210
|
@include spacer-util('.s-#{$size}') {
|
138
211
|
font-size: $value !important;
|
data/core/scss/_utilities.scss
CHANGED
@@ -1,13 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
.row {
|
2
|
+
margin-right: -24px;
|
3
|
+
& > table {
|
4
|
+
table-layout: fixed;
|
5
|
+
-premailer-width: 100%;
|
6
|
+
width: 100%;
|
7
|
+
& > tbody > tr > td {
|
8
|
+
min-height: 1px;
|
9
|
+
font-weight: normal;
|
10
|
+
padding-right: 24px;
|
11
|
+
vertical-align: top;
|
12
|
+
text-align: left;
|
13
|
+
}
|
11
14
|
}
|
12
15
|
}
|
13
16
|
|
@@ -19,19 +22,21 @@ table.row {
|
|
19
22
|
}
|
20
23
|
|
21
24
|
@each $space, $value in $spacers {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
.row.gap-#{$space},
|
26
|
+
.row.gap-x-#{$space},
|
27
|
+
.row.gap-lg-#{$space},
|
28
|
+
.row.gap-x-lg-#{$space} {
|
29
|
+
margin-right: -1 * $value;
|
30
|
+
& > table > tbody > tr > td {
|
27
31
|
padding-right: $value;
|
28
32
|
}
|
29
33
|
}
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
.row.gap-#{$space},
|
35
|
+
.row.gap-y-#{$space},
|
36
|
+
.row.gap-lg-#{$space},
|
37
|
+
.row.gap-y-lg-#{$space} {
|
38
|
+
margin-bottom: -1 * $value;
|
39
|
+
& > table > tbody > tr > td {
|
35
40
|
padding-bottom: $value;
|
36
41
|
}
|
37
42
|
}
|
@@ -1,16 +1,22 @@
|
|
1
1
|
@each $space, $value in $spacers {
|
2
|
-
table.stack-
|
3
|
-
|
4
|
-
|
5
|
-
padding-right:
|
2
|
+
table.stack-row.gap-#{$space},
|
3
|
+
table.stack-row.gap-lg-#{$space} {
|
4
|
+
& > tbody > tr > td {
|
5
|
+
padding-right: $value;
|
6
|
+
&:last-child {
|
7
|
+
padding-right: 0;
|
8
|
+
}
|
6
9
|
}
|
7
10
|
}
|
8
|
-
table.stack-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
table.stack-col.gap-#{$space},
|
12
|
+
table.stack-col.gap-lg-#{$space} {
|
13
|
+
& > tbody > tr {
|
14
|
+
& > td {
|
15
|
+
padding-bottom: $value;
|
16
|
+
}
|
17
|
+
&:last-child > td {
|
18
|
+
padding-bottom: 0;
|
19
|
+
}
|
14
20
|
}
|
15
21
|
}
|
16
22
|
}
|
@@ -21,13 +27,13 @@
|
|
21
27
|
}
|
22
28
|
|
23
29
|
@each $align in $vertical-align {
|
24
|
-
table.stack-
|
30
|
+
table.stack-ay-#{$align} > tbody > tr > td {
|
25
31
|
vertical-align: $align;
|
26
32
|
}
|
27
33
|
}
|
28
34
|
|
29
35
|
@each $align in $text-align {
|
30
|
-
table.stack-
|
36
|
+
table.stack-ax-#{$align} > tbody > tr > td {
|
31
37
|
text-align: $align;
|
32
38
|
}
|
33
39
|
}
|
@@ -21,6 +21,10 @@
|
|
21
21
|
-premailer-#{$property}: 100%;
|
22
22
|
#{$property}: 100%;
|
23
23
|
}
|
24
|
+
@include sizing-util('.#{$name}-#{$prefix}auto') {
|
25
|
+
-premailer-#{$property}: auto;
|
26
|
+
#{$property}: auto;
|
27
|
+
}
|
24
28
|
@each $size, $value in $sizing {
|
25
29
|
@include sizing-util('.#{$name}-#{$prefix}#{$size}') {
|
26
30
|
-premailer-#{$property}: strip-unit($value);
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<table class="
|
1
|
+
<table class="{{ classes }}" role="presentation">
|
2
2
|
<tbody>
|
3
3
|
<tr>
|
4
4
|
<td align="center">
|
@@ -12,7 +12,7 @@
|
|
12
12
|
<tbody>
|
13
13
|
<tr>
|
14
14
|
<td>
|
15
|
-
|
15
|
+
{{ contents }}
|
16
16
|
</td>
|
17
17
|
</tr>
|
18
18
|
</tbody>
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require_relative '../bootstrap-email'
|
2
2
|
require 'optparse'
|
3
|
-
require 'fileutils'
|
4
3
|
|
5
4
|
input = nil
|
6
5
|
options = {
|
@@ -37,7 +36,7 @@ parser = OptionParser.new do |opts|
|
|
37
36
|
options[:destination] = v
|
38
37
|
end
|
39
38
|
|
40
|
-
opts.on('-c', '--config STRING', String, 'Relative path to
|
39
|
+
opts.on('-c', '--config STRING', String, 'Relative path to ruby config file to customize Bootstrap Email.') do |v|
|
41
40
|
options[:config] = File.expand_path(v, Dir.pwd)
|
42
41
|
end
|
43
42
|
|
@@ -82,9 +81,9 @@ if input
|
|
82
81
|
end
|
83
82
|
when :file
|
84
83
|
path = File.expand_path(input, Dir.pwd)
|
85
|
-
puts BootstrapEmail::Compiler.new(path, type: :file, options: {config_path: options[:config]}).perform_full_compile
|
84
|
+
puts BootstrapEmail::Compiler.new(path, type: :file, options: {config_path: options[:config], sass_log_enabled: false}).perform_full_compile
|
86
85
|
when :string
|
87
|
-
puts BootstrapEmail::Compiler.new(input, options: {config_path: options[:config]}).perform_full_compile
|
86
|
+
puts BootstrapEmail::Compiler.new(input, options: {config_path: options[:config], sass_log_enabled: false}).perform_full_compile
|
88
87
|
end
|
89
88
|
else
|
90
89
|
puts opts
|
@@ -3,6 +3,7 @@ module BootstrapEmail
|
|
3
3
|
attr_accessor :type, :doc, :premailer
|
4
4
|
|
5
5
|
def initialize(input, type: :string, options: {})
|
6
|
+
BootstrapEmail.load_options(options)
|
6
7
|
self.type = type
|
7
8
|
case type
|
8
9
|
when :rails
|
@@ -14,7 +15,8 @@ module BootstrapEmail
|
|
14
15
|
html = File.read(input)
|
15
16
|
end
|
16
17
|
html = add_layout!(html)
|
17
|
-
|
18
|
+
sass_load_paths
|
19
|
+
build_premailer_doc(html)
|
18
20
|
end
|
19
21
|
|
20
22
|
def perform_full_compile
|
@@ -36,42 +38,45 @@ module BootstrapEmail
|
|
36
38
|
)
|
37
39
|
end
|
38
40
|
|
39
|
-
def
|
40
|
-
SassC.load_paths <<
|
41
|
-
|
41
|
+
def sass_load_paths
|
42
|
+
SassC.load_paths << BootstrapEmail.config.sass_load_paths
|
43
|
+
end
|
44
|
+
|
45
|
+
def build_premailer_doc(html)
|
46
|
+
css_string = BootstrapEmail::SassCache.compile('bootstrap-email', style: :expanded)
|
42
47
|
self.premailer = Premailer.new(
|
43
48
|
html,
|
44
49
|
with_html_string: true,
|
45
50
|
preserve_reset: false,
|
51
|
+
adapter: :nokogiri_fast,
|
52
|
+
output_encoding: 'US-ASCII',
|
46
53
|
css_string: css_string
|
47
54
|
)
|
48
55
|
self.doc = premailer.doc
|
49
56
|
end
|
50
57
|
|
51
58
|
def compile_html!
|
52
|
-
BootstrapEmail::
|
53
|
-
|
54
|
-
BootstrapEmail::Component::Button.build(doc)
|
55
|
-
BootstrapEmail::Component::Badge.build(doc)
|
56
|
-
BootstrapEmail::Component::Alert.build(doc)
|
57
|
-
BootstrapEmail::Component::Card.build(doc)
|
58
|
-
# BootstrapEmail::Component::Paragraph.build(doc) this might be too much
|
59
|
-
BootstrapEmail::Component::Hr.build(doc)
|
60
|
-
BootstrapEmail::Component::Container.build(doc)
|
61
|
-
BootstrapEmail::Component::Grid.build(doc)
|
62
|
-
BootstrapEmail::Component::Stack.build(doc)
|
59
|
+
BootstrapEmail::Converter::Body.build(doc)
|
60
|
+
BootstrapEmail::Converter::Block.build(doc)
|
63
61
|
|
64
|
-
BootstrapEmail::
|
65
|
-
BootstrapEmail::
|
66
|
-
BootstrapEmail::
|
67
|
-
BootstrapEmail::
|
62
|
+
BootstrapEmail::Converter::Button.build(doc)
|
63
|
+
BootstrapEmail::Converter::Badge.build(doc)
|
64
|
+
BootstrapEmail::Converter::Alert.build(doc)
|
65
|
+
BootstrapEmail::Converter::Card.build(doc)
|
66
|
+
BootstrapEmail::Converter::Hr.build(doc)
|
67
|
+
BootstrapEmail::Converter::Container.build(doc)
|
68
|
+
BootstrapEmail::Converter::Grid.build(doc)
|
69
|
+
BootstrapEmail::Converter::Stack.build(doc)
|
68
70
|
|
69
|
-
BootstrapEmail::
|
70
|
-
BootstrapEmail::
|
71
|
-
BootstrapEmail::
|
72
|
-
BootstrapEmail::
|
71
|
+
BootstrapEmail::Converter::Color.build(doc)
|
72
|
+
BootstrapEmail::Converter::Spacing.build(doc)
|
73
|
+
BootstrapEmail::Converter::Margin.build(doc)
|
74
|
+
BootstrapEmail::Converter::Spacer.build(doc)
|
75
|
+
BootstrapEmail::Converter::Align.build(doc)
|
76
|
+
BootstrapEmail::Converter::Padding.build(doc)
|
73
77
|
|
74
|
-
BootstrapEmail::
|
78
|
+
BootstrapEmail::Converter::PreviewText.build(doc)
|
79
|
+
BootstrapEmail::Converter::Table.build(doc)
|
75
80
|
end
|
76
81
|
|
77
82
|
def inline_css!
|
@@ -79,13 +84,13 @@ module BootstrapEmail
|
|
79
84
|
end
|
80
85
|
|
81
86
|
def configure_html!
|
82
|
-
BootstrapEmail::
|
83
|
-
BootstrapEmail::
|
84
|
-
BootstrapEmail::Component::VersionComment.build(doc)
|
87
|
+
BootstrapEmail::Converter::HeadStyle.build(doc)
|
88
|
+
BootstrapEmail::Converter::VersionComment.build(doc)
|
85
89
|
end
|
86
90
|
|
87
91
|
def finalize_document!
|
88
|
-
html =
|
92
|
+
html = doc.to_html(encoding: 'US-ASCII')
|
93
|
+
html = BootstrapEmail::Converter::ForceEncoding.replace(html)
|
89
94
|
case type
|
90
95
|
when :rails
|
91
96
|
(@mail.html_part || @mail).body = html
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
class Config
|
3
|
+
attr_writer :sass_email_location # path to main sass file
|
4
|
+
attr_writer :sass_head_location # path to head sass file
|
5
|
+
attr_writer :sass_load_paths # array of directories for loading sass imports
|
6
|
+
attr_writer :sass_cache_location # path to tmp folder for sass cache
|
7
|
+
attr_writer :sass_log_enabled # turn on or off sass log when caching new sass
|
8
|
+
|
9
|
+
def load_options(options)
|
10
|
+
file = File.expand_path('bootstrap-email.config.rb', Dir.pwd)
|
11
|
+
if options[:config_path]
|
12
|
+
require_relative options[:config_path]
|
13
|
+
elsif File.exist?(file)
|
14
|
+
require_relative file
|
15
|
+
end
|
16
|
+
options.each { |name, value| instance_variable_set("@#{name}", value) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def sass_location_for(type:)
|
20
|
+
ivar = instance_variable_get("@sass_#{type.sub('bootstrap-', '')}_location")
|
21
|
+
return ivar if ivar
|
22
|
+
|
23
|
+
lookup_locations = ["#{type}.scss", "app/assets/stylesheets/#{type}.scss"]
|
24
|
+
locations = lookup_locations.map { |location| File.expand_path(location, Dir.pwd) }.select { |location| File.exist?(location) }
|
25
|
+
locations.first if locations.any?
|
26
|
+
end
|
27
|
+
|
28
|
+
def sass_load_paths
|
29
|
+
paths_array = [SassCache::SASS_DIR]
|
30
|
+
@sass_load_paths ||= []
|
31
|
+
paths_array.concat(@sass_load_paths)
|
32
|
+
end
|
33
|
+
|
34
|
+
def sass_cache_location
|
35
|
+
@sass_cache_location ||= begin
|
36
|
+
if defined?(::Rails) && ::Rails.root
|
37
|
+
::Rails.root.join('tmp', 'cache', 'bootstrap-email', '.sass-cache')
|
38
|
+
elsif File.writable?(Dir.pwd)
|
39
|
+
File.join(Dir.pwd, '.sass-cache', 'bootstrap-email')
|
40
|
+
else
|
41
|
+
File.join(Dir.tmpdir, '.sass-cache', 'bootstrap-email')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def sass_log_enabled?
|
47
|
+
defined?(@sass_log_enabled) ? @sass_log_enabled : true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module BootstrapEmail
|
2
|
-
module
|
2
|
+
module Converter
|
3
3
|
class Align < Base
|
4
4
|
def build
|
5
5
|
['left', 'center', 'right'].each do |type|
|
6
|
-
full_type = "
|
6
|
+
full_type = "ax-#{type}"
|
7
7
|
each_node(".#{full_type}") do |node|
|
8
8
|
align_helper(node, full_type, type)
|
9
9
|
end
|
@@ -12,7 +12,7 @@ module BootstrapEmail
|
|
12
12
|
|
13
13
|
def align_helper(node, full_type, type)
|
14
14
|
unless table?(node) || td?(node)
|
15
|
-
node['class'] = node['class'].sub(full_type, '')
|
15
|
+
node['class'] = node['class'].sub(full_type, '').strip
|
16
16
|
node = node.replace(template('table', classes: full_type, contents: node.to_html))[0]
|
17
17
|
end
|
18
18
|
node['align'] = type
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module BootstrapEmail
|
2
|
-
module
|
2
|
+
module Converter
|
3
3
|
class Base
|
4
4
|
attr_reader :doc
|
5
5
|
def initialize(doc)
|
6
6
|
@doc = doc
|
7
|
+
@cached_templates = {}
|
7
8
|
end
|
8
9
|
|
9
10
|
def self.build(doc)
|
@@ -13,11 +14,19 @@ module BootstrapEmail
|
|
13
14
|
private
|
14
15
|
|
15
16
|
def template(file, locals_hash = {})
|
16
|
-
locals_hash[:classes] = locals_hash[:classes].split.join(' ')
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
locals_hash[:classes] = locals_hash[:classes].to_s.split.join(' ')
|
18
|
+
locals_hash[:content] ||= nil
|
19
|
+
if @cached_templates[file]
|
20
|
+
string = @cached_templates[file]
|
21
|
+
else
|
22
|
+
path = File.expand_path("../../../core/templates/#{file}.html", __dir__)
|
23
|
+
string = File.read(path).chop # read and remove trailing newline
|
24
|
+
@cached_templates[file] = string
|
25
|
+
end
|
26
|
+
locals_hash.each do |key, value|
|
27
|
+
string = string.sub("{{ #{key} }}", value.to_s)
|
28
|
+
end
|
29
|
+
string
|
21
30
|
end
|
22
31
|
|
23
32
|
def each_node(css_lookup, &blk)
|
@@ -27,7 +36,7 @@ module BootstrapEmail
|
|
27
36
|
|
28
37
|
def add_class(node, class_name)
|
29
38
|
node['class'] ||= ''
|
30
|
-
node['class'] += class_name
|
39
|
+
node['class'] += node['class'].length.zero? ? class_name : " #{class_name}"
|
31
40
|
end
|
32
41
|
|
33
42
|
def margin?(node)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Converter
|
3
|
+
class ForceEncoding < Base
|
4
|
+
def self.replace(html)
|
5
|
+
# force utf-8 character encoded in iOS Mail: https://github.com/bootstrap-email/bootstrap-email/issues/50
|
6
|
+
# this needs to be done after the document has been outputted to a ascii string so it doesn't get converted
|
7
|
+
html.sub(
|
8
|
+
'<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">',
|
9
|
+
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8">'
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module BootstrapEmail
|
2
|
-
module
|
2
|
+
module Converter
|
3
3
|
class Grid < Base
|
4
4
|
def build
|
5
5
|
each_node('.row') do |node|
|
6
|
-
node.replace(template('
|
6
|
+
node.replace(template('div', classes: node['class'], contents: template('table-to-tr', contents: node.inner_html)))
|
7
7
|
end
|
8
8
|
each_node('*[class*=col]') do |node|
|
9
9
|
node.replace(template('td', classes: node['class'], contents: node.inner_html))
|
@@ -1,11 +1,11 @@
|
|
1
1
|
module BootstrapEmail
|
2
|
-
module
|
2
|
+
module Converter
|
3
3
|
class Margin < Base
|
4
4
|
def build
|
5
|
-
each_node('*[class*=my-], *[class*=mt-], *[class*=mb-]
|
5
|
+
each_node("*[class^='my-'], *[class^='mt-'], *[class^='mb-'], *[class*=' my-'], *[class*=' mt-'], *[class*=' mb-']") do |node|
|
6
6
|
top_class = node['class'][/m[ty]{1}-(lg-)?(\d+)/]
|
7
7
|
bottom_class = node['class'][/m[by]{1}-(lg-)?(\d+)/]
|
8
|
-
node['class'] = node['class'].gsub(/(m[tby]{1}-(lg-)?\d+)/, '')
|
8
|
+
node['class'] = node['class'].gsub(/(m[tby]{1}-(lg-)?\d+)/, '').strip
|
9
9
|
html = ''
|
10
10
|
if top_class
|
11
11
|
html += template('div', classes: "s-#{top_class.gsub(/m[ty]{1}-/, '')}", contents: nil)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Converter
|
3
|
+
class Padding < Base
|
4
|
+
def build
|
5
|
+
each_node("*[class^=p-], *[class^=pt-], *[class^=pr-], *[class^=pb-], *[class^=pl-], *[class^=px-], *[class^=py-], *[class*=' p-'], *[class*=' pt-'], *[class*=' pr-'], *[class*=' pb-'], *[class*=' pl-'], *[class*=' px-'], *[class*=' py-']") do |node|
|
6
|
+
next if ['table', 'td', 'a'].include?(node.name)
|
7
|
+
|
8
|
+
padding_regex = /(p[trblxy]?-(lg-)?\d+)/
|
9
|
+
classes = node['class'].gsub(padding_regex).to_a.join(' ')
|
10
|
+
node['class'] = node['class'].gsub(padding_regex, '').strip
|
11
|
+
node.replace(template('table', classes: classes, contents: node.to_html))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module BootstrapEmail
|
2
|
-
module
|
2
|
+
module Converter
|
3
3
|
class Stack < Base
|
4
4
|
def build
|
5
|
-
|
6
|
-
|
5
|
+
stack_row
|
6
|
+
stack_col
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
each_node('.stack-
|
9
|
+
def stack_row
|
10
|
+
each_node('.stack-row') do |node|
|
11
11
|
html = ''
|
12
12
|
node.xpath('./*').each do |child|
|
13
13
|
html += template('td', classes: 'stack-cell', contents: child.to_html)
|
@@ -16,8 +16,8 @@ module BootstrapEmail
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
each_node('.stack-
|
19
|
+
def stack_col
|
20
|
+
each_node('.stack-col') do |node|
|
21
21
|
html = ''
|
22
22
|
node.xpath('./*').each do |child|
|
23
23
|
html += template('tr', classes: 'stack-cell', contents: child.to_html)
|
@@ -1,24 +1,27 @@
|
|
1
1
|
module BootstrapEmail
|
2
2
|
class SassCache
|
3
|
-
CACHE_DIR = File.expand_path('../../.sass-cache', __dir__)
|
4
3
|
SASS_DIR = File.expand_path('../../core', __dir__)
|
5
4
|
|
6
|
-
def self.compile(type,
|
7
|
-
new(type,
|
5
|
+
def self.compile(type, style: :compressed)
|
6
|
+
new(type, style).compile
|
8
7
|
end
|
9
8
|
|
10
9
|
attr_accessor :type, :style, :file_path, :config_file, :checksum
|
11
10
|
|
12
|
-
def initialize(type,
|
11
|
+
def initialize(type, style)
|
13
12
|
self.type = type
|
14
13
|
self.style = style
|
15
14
|
self.file_path = "#{SASS_DIR}/#{type}"
|
16
|
-
self.config_file = load_config
|
15
|
+
self.config_file = load_config
|
17
16
|
self.checksum = checksum_files
|
18
17
|
end
|
19
18
|
|
19
|
+
def cache_dir
|
20
|
+
BootstrapEmail.config.sass_cache_location
|
21
|
+
end
|
22
|
+
|
20
23
|
def compile
|
21
|
-
cache_path = "#{
|
24
|
+
cache_path = "#{cache_dir}/#{checksum}/#{type}.css"
|
22
25
|
unless cached?(cache_path)
|
23
26
|
compile_and_cache_scss(cache_path)
|
24
27
|
end
|
@@ -27,16 +30,9 @@ module BootstrapEmail
|
|
27
30
|
|
28
31
|
private
|
29
32
|
|
30
|
-
def load_config
|
31
|
-
|
32
|
-
|
33
|
-
if config_path && File.exist?(config_path)
|
34
|
-
# check if custom config was passed in
|
35
|
-
replace_config(File.read(config_path))
|
36
|
-
elsif locations.any?
|
37
|
-
# look for common lookup locations of config
|
38
|
-
replace_config(File.read(File.expand_path(locations.first, Dir.pwd)))
|
39
|
-
end
|
33
|
+
def load_config
|
34
|
+
path = BootstrapEmail.config.sass_location_for(type: type)
|
35
|
+
replace_config(File.read(path)) if path
|
40
36
|
end
|
41
37
|
|
42
38
|
def replace_config(config_file)
|
@@ -58,10 +54,11 @@ module BootstrapEmail
|
|
58
54
|
def compile_and_cache_scss(cache_path)
|
59
55
|
file = config_file || File.read("#{file_path}.scss")
|
60
56
|
css = SassC::Engine.new(file, style: style).render
|
61
|
-
|
62
|
-
Dir.mkdir("#{CACHE_DIR}/#{checksum}") unless File.directory?("#{CACHE_DIR}/#{checksum}")
|
57
|
+
FileUtils.mkdir_p("#{cache_dir}/#{checksum}") unless File.directory?("#{cache_dir}/#{checksum}")
|
63
58
|
File.write(cache_path, css)
|
64
|
-
|
59
|
+
if BootstrapEmail.config.sass_log_enabled?
|
60
|
+
puts "New css file cached for #{type}"
|
61
|
+
end
|
65
62
|
end
|
66
63
|
end
|
67
64
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
class << self
|
3
|
+
def config
|
4
|
+
@config ||= BootstrapEmail::Config.new
|
5
|
+
@config
|
6
|
+
end
|
7
|
+
|
8
|
+
def load_options(options)
|
9
|
+
@config ||= BootstrapEmail::Config.new
|
10
|
+
@config.load_options(options)
|
11
|
+
@config
|
12
|
+
end
|
13
|
+
|
14
|
+
def configure(&proc)
|
15
|
+
@config ||= BootstrapEmail::Config.new
|
16
|
+
yield @config
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset_config!
|
20
|
+
remove_instance_variable :@config if defined?(@config)
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear_sass_cache!
|
24
|
+
FileUtils.rm_rf(BootstrapEmail.config.sass_cache_location)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/bootstrap-email.rb
CHANGED
@@ -5,6 +5,7 @@ require 'premailer'
|
|
5
5
|
require 'sassc'
|
6
6
|
require 'digest/sha1'
|
7
7
|
require 'css_parser'
|
8
|
+
require 'fileutils'
|
8
9
|
|
9
10
|
begin
|
10
11
|
require 'rails'
|
@@ -14,13 +15,14 @@ if defined?(Rails)
|
|
14
15
|
require 'action_mailer'
|
15
16
|
end
|
16
17
|
|
17
|
-
require_relative 'bootstrap-email/
|
18
|
+
require_relative 'bootstrap-email/config'
|
19
|
+
require_relative 'bootstrap-email/setup'
|
18
20
|
require_relative 'bootstrap-email/erb'
|
19
21
|
require_relative 'bootstrap-email/compiler'
|
20
22
|
require_relative 'bootstrap-email/sass_cache'
|
21
23
|
require_relative 'bootstrap-email/version'
|
22
|
-
require_relative 'bootstrap-email/
|
23
|
-
Dir[File.join(__dir__, 'bootstrap-email/
|
24
|
+
require_relative 'bootstrap-email/converters/base'
|
25
|
+
Dir[File.join(__dir__, 'bootstrap-email/converters', '*.rb')].each { |file| require_relative file }
|
24
26
|
|
25
27
|
if defined?(Rails)
|
26
28
|
require_relative 'bootstrap-email/rails/action_mailer'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bootstrap-email
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stuart Yamartino
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -92,46 +92,47 @@ files:
|
|
92
92
|
- core/scss/utilities/_text-decoration.scss
|
93
93
|
- core/scss/utilities/_typography.scss
|
94
94
|
- core/scss/utilities/_valign.scss
|
95
|
-
- core/templates/body.html
|
96
|
-
- core/templates/container.html
|
97
|
-
- core/templates/div.html
|
98
|
-
- core/templates/table-left.html
|
99
|
-
- core/templates/table-to-tbody.html
|
100
|
-
- core/templates/table-to-tr.html
|
101
|
-
- core/templates/table.html
|
102
|
-
- core/templates/td.html
|
103
|
-
- core/templates/tr.html
|
95
|
+
- core/templates/body.html
|
96
|
+
- core/templates/container.html
|
97
|
+
- core/templates/div.html
|
98
|
+
- core/templates/table-left.html
|
99
|
+
- core/templates/table-to-tbody.html
|
100
|
+
- core/templates/table-to-tr.html
|
101
|
+
- core/templates/table.html
|
102
|
+
- core/templates/td.html
|
103
|
+
- core/templates/tr.html
|
104
104
|
- lib/bootstrap-email.rb
|
105
105
|
- lib/bootstrap-email/bootstrap_email_cli.rb
|
106
106
|
- lib/bootstrap-email/compiler.rb
|
107
|
-
- lib/bootstrap-email/
|
108
|
-
- lib/bootstrap-email/
|
109
|
-
- lib/bootstrap-email/
|
110
|
-
- lib/bootstrap-email/
|
111
|
-
- lib/bootstrap-email/
|
112
|
-
- lib/bootstrap-email/
|
113
|
-
- lib/bootstrap-email/
|
114
|
-
- lib/bootstrap-email/
|
115
|
-
- lib/bootstrap-email/
|
116
|
-
- lib/bootstrap-email/
|
117
|
-
- lib/bootstrap-email/
|
118
|
-
- lib/bootstrap-email/
|
119
|
-
- lib/bootstrap-email/
|
120
|
-
- lib/bootstrap-email/
|
121
|
-
- lib/bootstrap-email/
|
122
|
-
- lib/bootstrap-email/
|
123
|
-
- lib/bootstrap-email/
|
124
|
-
- lib/bootstrap-email/
|
125
|
-
- lib/bootstrap-email/
|
126
|
-
- lib/bootstrap-email/
|
127
|
-
- lib/bootstrap-email/
|
128
|
-
- lib/bootstrap-email/
|
129
|
-
- lib/bootstrap-email/
|
107
|
+
- lib/bootstrap-email/config.rb
|
108
|
+
- lib/bootstrap-email/converters/alert.rb
|
109
|
+
- lib/bootstrap-email/converters/align.rb
|
110
|
+
- lib/bootstrap-email/converters/badge.rb
|
111
|
+
- lib/bootstrap-email/converters/base.rb
|
112
|
+
- lib/bootstrap-email/converters/block.rb
|
113
|
+
- lib/bootstrap-email/converters/body.rb
|
114
|
+
- lib/bootstrap-email/converters/button.rb
|
115
|
+
- lib/bootstrap-email/converters/card.rb
|
116
|
+
- lib/bootstrap-email/converters/color.rb
|
117
|
+
- lib/bootstrap-email/converters/container.rb
|
118
|
+
- lib/bootstrap-email/converters/force_encoding.rb
|
119
|
+
- lib/bootstrap-email/converters/grid.rb
|
120
|
+
- lib/bootstrap-email/converters/head_style.rb
|
121
|
+
- lib/bootstrap-email/converters/hr.rb
|
122
|
+
- lib/bootstrap-email/converters/margin.rb
|
123
|
+
- lib/bootstrap-email/converters/padding.rb
|
124
|
+
- lib/bootstrap-email/converters/paragraph.rb
|
125
|
+
- lib/bootstrap-email/converters/preview_text.rb
|
126
|
+
- lib/bootstrap-email/converters/spacer.rb
|
127
|
+
- lib/bootstrap-email/converters/spacing.rb
|
128
|
+
- lib/bootstrap-email/converters/stack.rb
|
129
|
+
- lib/bootstrap-email/converters/table.rb
|
130
|
+
- lib/bootstrap-email/converters/version_comment.rb
|
130
131
|
- lib/bootstrap-email/erb.rb
|
131
|
-
- lib/bootstrap-email/initialize.rb
|
132
132
|
- lib/bootstrap-email/rails/action_mailer.rb
|
133
133
|
- lib/bootstrap-email/rails/engine.rb
|
134
134
|
- lib/bootstrap-email/sass_cache.rb
|
135
|
+
- lib/bootstrap-email/setup.rb
|
135
136
|
- lib/bootstrap-email/version.rb
|
136
137
|
homepage: https://bootstrapemail.com
|
137
138
|
licenses:
|
@@ -148,9 +149,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
148
149
|
version: '2.0'
|
149
150
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
151
|
requirements:
|
151
|
-
- - "
|
152
|
+
- - ">="
|
152
153
|
- !ruby/object:Gem::Version
|
153
|
-
version:
|
154
|
+
version: '0'
|
154
155
|
requirements: []
|
155
156
|
rubygems_version: 3.0.3
|
156
157
|
signing_key:
|
data/core/templates/div.html.erb
DELETED
data/core/templates/td.html.erb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module BootstrapEmail
|
2
|
-
module Component
|
3
|
-
class ForceEncoding < Base
|
4
|
-
def build
|
5
|
-
body = doc.at_css('body')
|
6
|
-
body.add_child('<force-encoding></force-encoding>')
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.replace(html)
|
10
|
-
# force utf-8 character encoded in iOS Mail: https://github.com/bootstrap-email/bootstrap-email/issues/50
|
11
|
-
# this needs to be done after the document has been outputted to a string so it doesn't get converted
|
12
|
-
html.sub('<force-encoding></force-encoding>', '<div id="force-encoding-to-utf-8" style="display: none;">➿</div>')
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module BootstrapEmail
|
2
|
-
module Component
|
3
|
-
class Padding < Base
|
4
|
-
def build
|
5
|
-
each_node('*[class*=p-], *[class*=pt-], *[class*=pr-], *[class*=pb-], *[class*=pl-], *[class*=px-], *[class*=py-]') do |node|
|
6
|
-
next if ['table', 'td', 'a'].include?(node.name)
|
7
|
-
|
8
|
-
padding_regex = /(p[trblxy]?-\d+)/
|
9
|
-
classes = node['class'].scan(padding_regex).join(' ')
|
10
|
-
node['class'] = node['class'].gsub(padding_regex, '')
|
11
|
-
node.replace(template('table', classes: classes, contents: node.to_html))
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
Premailer::Adapter.use = :nokogiri_fast
|