bootstrap-email 1.0.0.alpha1.2 → 1.0.0.alpha4
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-email.scss +2 -0
- data/core/bootstrap-head.scss +89 -16
- data/core/scss/_selectors_for_utils.scss +24 -0
- data/core/scss/_utilities.scss +24 -4
- data/core/scss/components/_grid.scss +25 -17
- data/core/scss/components/_stack.scss +39 -0
- data/core/scss/utilities/_sizing.scss +21 -2
- data/core/scss/utilities/_typography.scss +4 -10
- data/core/scss/utilities/_valign.scss +5 -0
- data/core/templates/body.html +9 -0
- data/core/templates/{container.html.erb → container.html} +4 -4
- 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 +5 -0
- data/lib/{bootstrap_email.rb → bootstrap-email.rb} +6 -3
- data/lib/bootstrap-email/bootstrap_email_cli.rb +6 -7
- data/lib/bootstrap-email/compiler.rb +50 -65
- data/lib/bootstrap-email/config.rb +50 -0
- data/lib/bootstrap-email/{components → converters}/alert.rb +1 -1
- data/lib/bootstrap-email/converters/align.rb +22 -0
- data/lib/bootstrap-email/{components → converters}/badge.rb +1 -1
- data/lib/bootstrap-email/converters/base.rb +63 -0
- data/lib/bootstrap-email/converters/block.rb +13 -0
- data/lib/bootstrap-email/converters/body.rb +10 -0
- data/lib/bootstrap-email/{components → converters}/button.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/card.rb +3 -3
- 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/converters/grid.rb +14 -0
- data/lib/bootstrap-email/converters/head_style.rb +33 -0
- data/lib/bootstrap-email/{components → converters}/hr.rb +2 -2
- data/lib/bootstrap-email/{components → converters}/margin.rb +3 -3
- data/lib/bootstrap-email/converters/padding.rb +16 -0
- data/lib/bootstrap-email/converters/paragraph.rb +13 -0
- data/lib/bootstrap-email/converters/preview_text.rb +18 -0
- data/lib/bootstrap-email/{components → converters}/spacer.rb +1 -1
- data/lib/bootstrap-email/{components → converters}/spacing.rb +5 -6
- data/lib/bootstrap-email/converters/stack.rb +30 -0
- data/lib/bootstrap-email/{components → converters}/table.rb +1 -1
- data/lib/bootstrap-email/converters/version_comment.rb +15 -0
- data/lib/bootstrap-email/erb.rb +9 -0
- data/lib/bootstrap-email/rails/action_mailer.rb +7 -2
- data/lib/bootstrap-email/sass_cache.rb +43 -26
- data/lib/bootstrap-email/setup.rb +27 -0
- metadata +44 -32
- data/core/templates/body.html.erb +0 -9
- data/core/templates/col.html.erb +0 -3
- data/core/templates/div.html.erb +0 -3
- data/core/templates/row.html.erb +0 -7
- data/core/templates/table-left.html.erb +0 -9
- data/core/templates/table.html.erb +0 -9
- data/lib/bootstrap-email/components/align.rb +0 -21
- data/lib/bootstrap-email/components/base.rb +0 -26
- data/lib/bootstrap-email/components/body.rb +0 -22
- data/lib/bootstrap-email/components/grid.rb +0 -14
- data/lib/bootstrap-email/components/padding.rb +0 -16
- data/lib/bootstrap-email/components/paragraph.rb +0 -24
- data/lib/bootstrap-email/initialize.rb +0 -1
@@ -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,12 +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'
|
20
|
+
require_relative 'bootstrap-email/erb'
|
18
21
|
require_relative 'bootstrap-email/compiler'
|
19
22
|
require_relative 'bootstrap-email/sass_cache'
|
20
23
|
require_relative 'bootstrap-email/version'
|
21
|
-
require_relative 'bootstrap-email/
|
22
|
-
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 }
|
23
26
|
|
24
27
|
if defined?(Rails)
|
25
28
|
require_relative 'bootstrap-email/rails/action_mailer'
|
@@ -1,10 +1,9 @@
|
|
1
|
-
require_relative '../
|
1
|
+
require_relative '../bootstrap-email'
|
2
2
|
require 'optparse'
|
3
|
-
require 'fileutils'
|
4
3
|
|
5
4
|
input = nil
|
6
5
|
options = {
|
7
|
-
destination: '
|
6
|
+
destination: 'compiled',
|
8
7
|
type: :file
|
9
8
|
}
|
10
9
|
|
@@ -17,7 +16,7 @@ parser = OptionParser.new do |opts|
|
|
17
16
|
opts.separator ' bootstrap-email email.html > out.html'
|
18
17
|
opts.separator ' bootstrap-email ./public/index.html'
|
19
18
|
opts.separator ' bootstrap-email -s \'<a href="#" class="btn btn-primary">Some Button</a>\''
|
20
|
-
opts.separator ' bootstrap-email -p \'emails/*\' -d emails/
|
19
|
+
opts.separator ' bootstrap-email -p \'emails/*\' -d emails/compiled'
|
21
20
|
opts.separator ' bootstrap-email -p \'views/emails/*\' -d \'views/compiled_emails\''
|
22
21
|
opts.separator ' cat input.html | bootstrap-email'
|
23
22
|
opts.separator ''
|
@@ -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
|
@@ -13,106 +14,90 @@ module BootstrapEmail
|
|
13
14
|
when :file
|
14
15
|
html = File.read(input)
|
15
16
|
end
|
16
|
-
|
17
|
+
html = add_layout!(html)
|
18
|
+
sass_load_paths
|
19
|
+
build_premailer_doc(html)
|
17
20
|
end
|
18
21
|
|
19
22
|
def perform_full_compile
|
20
23
|
compile_html!
|
21
24
|
inline_css!
|
22
|
-
|
23
|
-
inject_comment!
|
25
|
+
configure_html!
|
24
26
|
finalize_document!
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
28
30
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
31
|
+
def add_layout!(html)
|
32
|
+
document = Nokogiri::HTML(html)
|
33
|
+
return html unless document.at_css('head').nil?
|
34
|
+
|
35
|
+
BootstrapEmail::Erb.template(
|
36
|
+
File.expand_path('../../core/layout.html.erb', __dir__),
|
37
|
+
contents: html
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
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)
|
33
47
|
self.premailer = Premailer.new(
|
34
48
|
html,
|
35
49
|
with_html_string: true,
|
36
50
|
preserve_reset: false,
|
51
|
+
adapter: :nokogiri_fast,
|
52
|
+
output_encoding: 'US-ASCII',
|
37
53
|
css_string: css_string
|
38
54
|
)
|
39
55
|
self.doc = premailer.doc
|
40
56
|
end
|
41
57
|
|
42
|
-
def
|
43
|
-
|
44
|
-
|
58
|
+
def compile_html!
|
59
|
+
BootstrapEmail::Converter::Body.build(doc)
|
60
|
+
BootstrapEmail::Converter::Block.build(doc)
|
45
61
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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)
|
50
70
|
|
51
|
-
|
52
|
-
BootstrapEmail::
|
53
|
-
BootstrapEmail::
|
54
|
-
BootstrapEmail::
|
55
|
-
BootstrapEmail::
|
56
|
-
BootstrapEmail::
|
57
|
-
|
58
|
-
BootstrapEmail::
|
59
|
-
BootstrapEmail::
|
60
|
-
BootstrapEmail::Component::Align.build(doc)
|
61
|
-
BootstrapEmail::Component::Color.build(doc)
|
62
|
-
BootstrapEmail::Component::Padding.build(doc)
|
63
|
-
BootstrapEmail::Component::Margin.build(doc)
|
64
|
-
BootstrapEmail::Component::Spacing.build(doc)
|
65
|
-
BootstrapEmail::Component::Spacer.build(doc)
|
66
|
-
BootstrapEmail::Component::Table.build(doc)
|
67
|
-
BootstrapEmail::Component::Body.build(doc)
|
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)
|
77
|
+
|
78
|
+
BootstrapEmail::Converter::PreviewText.build(doc)
|
79
|
+
BootstrapEmail::Converter::Table.build(doc)
|
68
80
|
end
|
69
81
|
|
70
82
|
def inline_css!
|
71
83
|
premailer.to_inline_css
|
72
84
|
end
|
73
85
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
def inject_comment!
|
79
|
-
doc.at_css('head').prepend_child(bootstrap_email_comment)
|
86
|
+
def configure_html!
|
87
|
+
BootstrapEmail::Converter::HeadStyle.build(doc)
|
88
|
+
BootstrapEmail::Converter::VersionComment.build(doc)
|
80
89
|
end
|
81
90
|
|
82
91
|
def finalize_document!
|
92
|
+
html = doc.to_html(encoding: 'US-ASCII')
|
93
|
+
html = BootstrapEmail::Converter::ForceEncoding.replace(html)
|
83
94
|
case type
|
84
95
|
when :rails
|
85
|
-
(@mail.html_part || @mail).body =
|
96
|
+
(@mail.html_part || @mail).body = html
|
86
97
|
@mail
|
87
98
|
when :string, :file
|
88
|
-
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def bootstrap_email_head
|
93
|
-
html_string = <<-INLINE
|
94
|
-
<style type="text/css">
|
95
|
-
#{purged_css_from_head}
|
96
|
-
</style>
|
97
|
-
INLINE
|
98
|
-
html_string
|
99
|
-
end
|
100
|
-
|
101
|
-
def bootstrap_email_comment
|
102
|
-
"\n <!-- Compiled with Bootstrap Email version: #{BootstrapEmail::VERSION} -->"
|
103
|
-
end
|
104
|
-
|
105
|
-
def purged_css_from_head
|
106
|
-
default, custom = BootstrapEmail::SassCache.compile('bootstrap-head').split('/*! allow_purge_after */')
|
107
|
-
# get each CSS declaration
|
108
|
-
custom.scan(/\w*\.[\w\-]*[\s\S\n]+?(?=})}{1}/).each do |group|
|
109
|
-
# get the first class for each comma separated CSS declaration
|
110
|
-
exist = group.scan(/(\.[\w\-]*).*?((,+?)|{+?)/).map(&:first).uniq.any? do |selector|
|
111
|
-
!doc.at_css(selector).nil?
|
112
|
-
end
|
113
|
-
custom.sub!(group, '') unless exist
|
99
|
+
html
|
114
100
|
end
|
115
|
-
(default + custom).gsub(/\n\s*\n+/, "\n")
|
116
101
|
end
|
117
102
|
end
|
118
103
|
end
|
@@ -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
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Converter
|
3
|
+
class Align < Base
|
4
|
+
def build
|
5
|
+
['left', 'center', 'right'].each do |type|
|
6
|
+
full_type = "ax-#{type}"
|
7
|
+
each_node(".#{full_type}") do |node|
|
8
|
+
align_helper(node, full_type, type)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def align_helper(node, full_type, type)
|
14
|
+
unless table?(node) || td?(node)
|
15
|
+
node['class'] = node['class'].sub(full_type, '').strip
|
16
|
+
node = node.replace(template('table', classes: full_type, contents: node.to_html))[0]
|
17
|
+
end
|
18
|
+
node['align'] = type
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Converter
|
3
|
+
class Base
|
4
|
+
attr_reader :doc
|
5
|
+
def initialize(doc)
|
6
|
+
@doc = doc
|
7
|
+
@cached_templates = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.build(doc)
|
11
|
+
new(doc).build
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def template(file, locals_hash = {})
|
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
|
30
|
+
end
|
31
|
+
|
32
|
+
def each_node(css_lookup, &blk)
|
33
|
+
# sort by youngest child and traverse backwards up the tree
|
34
|
+
doc.css(css_lookup).sort_by { |n| n.ancestors.size }.reverse!.each(&blk)
|
35
|
+
end
|
36
|
+
|
37
|
+
def add_class(node, class_name)
|
38
|
+
node['class'] ||= ''
|
39
|
+
node['class'] += node['class'].length.zero? ? class_name : " #{class_name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def margin?(node)
|
43
|
+
margin_top?(node) || margin_bottom?(node)
|
44
|
+
end
|
45
|
+
|
46
|
+
def margin_top?(node)
|
47
|
+
node['class'].to_s.match?(/m[ty]{1}-(lg-)?\d+/)
|
48
|
+
end
|
49
|
+
|
50
|
+
def margin_bottom?(node)
|
51
|
+
node['class'].to_s.match?(/m[by]{1}-(lg-)?\d+/)
|
52
|
+
end
|
53
|
+
|
54
|
+
def table?(node)
|
55
|
+
node.name == 'table'
|
56
|
+
end
|
57
|
+
|
58
|
+
def td?(node)
|
59
|
+
node.name == 'td'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module BootstrapEmail
|
2
|
+
module Converter
|
3
|
+
class Block < Base
|
4
|
+
def build
|
5
|
+
each_node('block, .to-table') do |node|
|
6
|
+
# add .to-table if it's not already there
|
7
|
+
class_name = node['class'].to_s.split << 'to-table'
|
8
|
+
node.replace(template('table', classes: class_name.uniq.join(' '), contents: node.inner_html))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|