bootstrap-email 0.3.1 → 1.0.0.alpha1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/bootstrap-email +2 -0
  4. data/core/bootstrap-email.scss +27 -18
  5. data/core/bootstrap-head.scss +143 -0
  6. data/core/layout.html.erb +11 -0
  7. data/core/scss/_colors.scss +161 -0
  8. data/core/scss/_functions.scss +29 -0
  9. data/core/scss/_helper_groups.scss +21 -0
  10. data/core/{sass → scss}/_reboot_email.scss +7 -22
  11. data/core/scss/_selectors_for_utils.scss +100 -0
  12. data/core/scss/_utilities.scss +125 -0
  13. data/core/scss/_variables.scss +52 -0
  14. data/core/{sass → scss/components}/_alert.scss +0 -1
  15. data/core/{sass → scss/components}/_badge.scss +8 -15
  16. data/core/{sass → scss/components}/_button.scss +21 -17
  17. data/core/{sass → scss/components}/_card.scss +3 -2
  18. data/core/{sass → scss/components}/_container.scss +0 -0
  19. data/core/scss/components/_grid.scss +35 -0
  20. data/core/scss/components/_hr.scss +8 -0
  21. data/core/{sass → scss/components}/_image.scss +2 -1
  22. data/core/{sass → scss/components}/_preview.scss +0 -0
  23. data/core/{sass → scss/components}/_table.scss +0 -0
  24. data/core/scss/utilities/_background.scss +5 -0
  25. data/core/scss/utilities/_border-radius.scss +21 -0
  26. data/core/scss/utilities/_border.scss +13 -0
  27. data/core/scss/utilities/_color.scss +9 -0
  28. data/core/scss/utilities/_display.scss +7 -0
  29. data/core/scss/utilities/_sizing.scss +16 -0
  30. data/core/scss/utilities/_spacing.scss +18 -0
  31. data/core/scss/utilities/_text-decoration.scss +9 -0
  32. data/core/scss/utilities/_typography.scss +54 -0
  33. data/core/templates/col.html.erb +2 -2
  34. data/core/templates/row.html.erb +2 -2
  35. data/lib/bootstrap-email/bootstrap_email_cli.rb +91 -0
  36. data/lib/bootstrap-email/compiler.rb +118 -0
  37. data/lib/bootstrap-email/components/alert.rb +11 -0
  38. data/lib/bootstrap-email/components/align.rb +21 -0
  39. data/lib/bootstrap-email/components/badge.rb +11 -0
  40. data/lib/bootstrap-email/components/base.rb +26 -0
  41. data/lib/bootstrap-email/components/body.rb +22 -0
  42. data/lib/bootstrap-email/components/button.rb +11 -0
  43. data/lib/bootstrap-email/components/card.rb +14 -0
  44. data/lib/bootstrap-email/components/color.rb +13 -0
  45. data/lib/bootstrap-email/components/container.rb +14 -0
  46. data/lib/bootstrap-email/components/grid.rb +14 -0
  47. data/lib/bootstrap-email/components/hr.rb +12 -0
  48. data/lib/bootstrap-email/components/margin.rb +22 -0
  49. data/lib/bootstrap-email/components/padding.rb +16 -0
  50. data/lib/bootstrap-email/components/paragraph.rb +24 -0
  51. data/lib/bootstrap-email/components/spacer.rb +11 -0
  52. data/lib/bootstrap-email/components/spacing.rb +18 -0
  53. data/lib/bootstrap-email/components/table.rb +13 -0
  54. data/lib/bootstrap-email/initialize.rb +1 -0
  55. data/lib/bootstrap-email/rails/action_mailer.rb +10 -0
  56. data/lib/bootstrap-email/{engine.rb → rails/engine.rb} +0 -0
  57. data/lib/bootstrap-email/sass_cache.rb +47 -0
  58. data/lib/bootstrap-email/version.rb +3 -5
  59. data/lib/bootstrap_email.rb +27 -0
  60. metadata +76 -64
  61. data/core/head.scss +0 -269
  62. data/core/sass/_border.scss +0 -73
  63. data/core/sass/_color.scss +0 -33
  64. data/core/sass/_display.scss +0 -7
  65. data/core/sass/_functions.scss +0 -14
  66. data/core/sass/_grid.scss +0 -131
  67. data/core/sass/_hr.scss +0 -14
  68. data/core/sass/_spacing.scss +0 -100
  69. data/core/sass/_typography.scss +0 -50
  70. data/core/sass/_variables.scss +0 -150
  71. data/core/template.html.erb +0 -11
  72. data/core/templates/align-center.html.erb +0 -9
  73. data/core/templates/align-left.html.erb +0 -9
  74. data/core/templates/align-right.html.erb +0 -9
  75. data/core/templates/hr.html.erb +0 -9
  76. data/lib/assets/stylesheets/bootstrap-email.scss +0 -1
  77. data/lib/bootstrap-email.rb +0 -230
  78. data/lib/bootstrap-email/action_mailer.rb +0 -12
  79. data/lib/bootstrap-email/premailer_railtie.rb +0 -5
@@ -1,14 +0,0 @@
1
- div.hr {
2
- width: 100%;
3
- border: 0;
4
- margin: $font-size-base*1.25 0;
5
- & > table {
6
- width: 100%;
7
- & > tbody > tr > td {
8
- width: 100%;
9
- border-top: 1px solid #dddddd;
10
- height: 1px;
11
- width: 100%;
12
- }
13
- }
14
- }
@@ -1,100 +0,0 @@
1
- // Width Helper
2
- @each $size, $percentage in $widths {
3
- .w-#{$size} {
4
- width: $percentage;
5
- & > tbody > tr > td {
6
- width: $percentage;
7
- }
8
- }
9
- }
10
-
11
- @each $size, $percentage in $widths {
12
- .w-lg-#{$size} {
13
- width: $percentage;
14
- & > tbody > tr > td {
15
- width: $percentage;
16
- }
17
- }
18
- }
19
-
20
- // Padding Helper
21
- @each $size, $length in $spacers {
22
- .p-#{$size} {
23
- & > tbody > tr > td {
24
- padding: $length;
25
- }
26
- }
27
- .pt-#{$size},
28
- .py-#{$size} {
29
- & > tbody > tr > td {
30
- padding-top: $length;
31
- }
32
- }
33
- .pr-#{$size},
34
- .px-#{$size} {
35
- & > tbody > tr > td {
36
- padding-right: $length;
37
- }
38
- }
39
- .pb-#{$size},
40
- .py-#{$size} {
41
- & > tbody > tr > td {
42
- padding-bottom: $length;
43
- }
44
- }
45
- .pl-#{$size},
46
- .px-#{$size} {
47
- & > tbody > tr > td {
48
- padding-left: $length;
49
- }
50
- }
51
- }
52
-
53
- @each $size, $length in $spacers {
54
- .p-lg-#{$size} {
55
- & > tbody > tr > td {
56
- padding: $length;
57
- }
58
- }
59
- .pt-lg-#{$size},
60
- .py-lg-#{$size} {
61
- & > tbody > tr > td {
62
- padding-top: $length;
63
- }
64
- }
65
- .pr-lg-#{$size},
66
- .px-lg-#{$size} {
67
- & > tbody > tr > td {
68
- padding-right: $length;
69
- }
70
- }
71
- .pb-lg-#{$size},
72
- .py-lg-#{$size} {
73
- & > tbody > tr > td {
74
- padding-bottom: $length;
75
- }
76
- }
77
- .pl-lg-#{$size},
78
- .px-lg-#{$size} {
79
- & > tbody > tr > td {
80
- padding-left: $length;
81
- }
82
- }
83
- }
84
-
85
- // Spacing Helper
86
- @each $size, $length in $spacers {
87
- .s-#{$size} > tbody > tr > td {
88
- font-size: $length;
89
- line-height: $length;
90
- height: $length;
91
- }
92
- }
93
-
94
- @each $size, $length in $spacers {
95
- .s-lg-#{$size} > tbody > tr > td {
96
- font-size: $length;
97
- line-height: $length;
98
- height: $length;
99
- }
100
- }
@@ -1,50 +0,0 @@
1
- h1, h2, h3, h4, h5, h6,
2
- .h1, .h2, .h3, .h4, .h5, .h6 {
3
- margin-top: $headings-margin-top;
4
- margin-bottom: $headings-margin-bottom;
5
- font-family: $headings-font-family;
6
- font-weight: $headings-font-weight;
7
- color: $headings-color;
8
- text-align: left;
9
- vertical-align: baseline;
10
- }
11
-
12
- @for $n from 1 through 6 {
13
- h#{$n}, .h#{$n} {
14
- font-size: heading-size($n);
15
- line-height: heading-line-height($n);
16
- }
17
- }
18
-
19
- .font-weight-bold {
20
- font-weight: bold !important;
21
- }
22
-
23
- .font-weight-normal {
24
- font-weight: normal !important;
25
- }
26
-
27
- .text-left {
28
- text-align: left !important;
29
- }
30
-
31
- .text-right {
32
- text-align: right !important;
33
- }
34
-
35
- .text-center {
36
- text-align: center !important;
37
- }
38
-
39
- p {
40
- margin-bottom: $paragraph-margin-bottom;
41
- width: 100%;
42
- & > tbody > tr > td {
43
- margin: 0;
44
- padding: 0 0 $font-size-base * 1.25 0;
45
- & > p {
46
- margin: 0;
47
- padding: 0;
48
- }
49
- }
50
- }
@@ -1,150 +0,0 @@
1
- $font-size-base: 16px !default;
2
- $font-weight-base: normal !default;
3
- $font-family-sans-serif: Helvetica, Arial, sans-serif !default;
4
- $font-family-base: $font-family-sans-serif !default;
5
- $border-radius: $font-size-base * 0.25 !default;
6
- $border-radius-lg: $font-size-base * 0.3 !default;
7
- $border-radius-sm: $font-size-base * 0.2 !default;
8
- $border-width: 1px !default;
9
- $line-height-base: 1.5 !default;
10
- $headings-line-height: 1.2 !default;
11
- $headings-ratios: (1: 2.25, 2: 2, 3: 1.75, 4: 1.5, 5: 1.25, 6: 1) !default;
12
- $headings-margin-top: 0 !default;
13
- $headings-margin-bottom: 0 !default;
14
- $headings-font-family: null !default;
15
- $headings-font-weight: 500 !default;
16
- $headings-color: null !default;
17
- $paragraph-margin-bottom: 0 !default;
18
- $rounded-pill: 50 * $font-size-base !default;
19
-
20
- //
21
- // Color system
22
- //
23
-
24
- $white: #ffffff !default;
25
- $gray-100: #f8f9fa !default;
26
- $gray-200: #e9ecef !default;
27
- $gray-300: #dee2e6 !default;
28
- $gray-400: #ced4da !default;
29
- $gray-500: #adb5bd !default;
30
- $gray-600: #868e96 !default;
31
- $gray-700: #495057 !default;
32
- $gray-800: #343a40 !default;
33
- $gray-900: #212529 !default;
34
- $black: #000000 !default;
35
-
36
- $grays: (
37
- 100: $gray-100,
38
- 200: $gray-200,
39
- 300: $gray-300,
40
- 400: $gray-400,
41
- 500: $gray-500,
42
- 600: $gray-600,
43
- 700: $gray-700,
44
- 800: $gray-800,
45
- 900: $gray-900
46
- ) !default;
47
-
48
- $blue: #007bff !default;
49
- $indigo: #6610f2 !default;
50
- $purple: #6f42c1 !default;
51
- $pink: #e83e8c !default;
52
- $red: #dc3545 !default;
53
- $orange: #fd7e14 !default;
54
- $yellow: #ffc107 !default;
55
- $green: #28a745 !default;
56
- $teal: #20c997 !default;
57
- $cyan: #17a2b8 !default;
58
-
59
- $colors: (
60
- blue: $blue,
61
- indigo: $indigo,
62
- purple: $purple,
63
- pink: $pink,
64
- red: $red,
65
- orange: $orange,
66
- yellow: $yellow,
67
- green: $green,
68
- teal: $teal,
69
- cyan: $cyan,
70
- white: $white,
71
- gray: $gray-600,
72
- gray-dark: $gray-800
73
- ) !default;
74
-
75
- $theme-primary: $blue !default;
76
- $theme-secondary: $gray-600 !default;
77
- $theme-success: $green !default;
78
- $theme-danger: $red !default;
79
- $theme-warning: $yellow !default;
80
- $theme-info: $cyan !default;
81
- $theme-light: $gray-100 !default;
82
- $theme-dark: $gray-800 !default;
83
-
84
- $theme-colors: (
85
- primary: $theme-primary,
86
- secondary: $theme-secondary,
87
- success: $theme-success,
88
- danger: $theme-danger,
89
- warning: $theme-warning,
90
- info: $theme-info,
91
- light: $theme-light,
92
- dark: $theme-dark
93
- ) !default;
94
-
95
- $spacer: $font-size-base;
96
- $spacers: (
97
- 0: 0,
98
- 1: ($spacer * .25),
99
- 2: ($spacer * .5),
100
- 3: $spacer,
101
- 4: ($spacer * 1.5),
102
- 5: ($spacer * 3)
103
- ) !default;
104
-
105
- $widths: (
106
- 25: 25%,
107
- 50: 50%,
108
- 75: 75%,
109
- 100: 100%,
110
- auto: auto
111
- ) !default;
112
-
113
- $border-color: $gray-300 !default;
114
-
115
- $table-accent-bg: #f2f2f2 !default;
116
- $table-background-color: null !default;
117
- $table-border-color: $border-color !default;
118
- $table-border-width: $border-width !default;
119
-
120
- $body-color: $black !default;
121
- $body-bg: $white !default;
122
-
123
- $btn-padding-y: 8px !default;
124
- $btn-padding-x: 12px !default;
125
- $btn-font-family: $font-family-base !default;
126
- $btn-font-size: $font-size-base !default;
127
- $btn-line-height: 1.25 * $btn-font-size !default;
128
-
129
- $btn-padding-y-sm: 0.25 * $btn-font-size !default;
130
- $btn-padding-x-sm: 0.5 * $btn-font-size !default;
131
- $btn-font-size-sm: 0.875 * $btn-font-size !default;
132
- $btn-line-height-sm: $btn-line-height * 0.875 !default;
133
-
134
- $btn-padding-y-lg: 0.5 * $btn-font-size !default;
135
- $btn-padding-x-lg: $btn-font-size !default;
136
- $btn-font-size-lg: 1.25 * $btn-font-size !default;
137
- $btn-line-height-lg: $btn-line-height * 1.25 !default;
138
-
139
- $btn-border-width: $border-width !default;
140
-
141
- $btn-font-weight: $font-weight-base !default;
142
-
143
- // Allows for customizing button radius independently from global border radius
144
- $btn-border-radius: $border-radius !default;
145
- $btn-border-radius-lg: $border-radius-lg !default;
146
- $btn-border-radius-sm: $border-radius-sm !default;
147
-
148
- // Utility functions
149
- @function heading-size($n) { @return map-get($headings-ratios, $n) * $font-size-base; }
150
- @function heading-line-height($n) { @return heading-size($n) * $headings-line-height; }
@@ -1,11 +0,0 @@
1
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <link rel="stylesheet" href="../../css/email.css">
6
- <!-- inject-style src="./css/head.css" -->
7
- </head>
8
- <body>
9
- <%= contents %>
10
- </body>
11
- </html>
@@ -1,9 +0,0 @@
1
- <table class="mx-auto" align="center">
2
- <tbody>
3
- <tr>
4
- <td>
5
- <%= contents %>
6
- </td>
7
- </tr>
8
- </tbody>
9
- </table>
@@ -1,9 +0,0 @@
1
- <table class="float-left" align="left">
2
- <tbody>
3
- <tr>
4
- <td>
5
- <%= contents %>
6
- </td>
7
- </tr>
8
- </tbody>
9
- </table>
@@ -1,9 +0,0 @@
1
- <table class="float-right" align="right">
2
- <tbody>
3
- <tr>
4
- <td>
5
- <%= contents %>
6
- </td>
7
- </tr>
8
- </tbody>
9
- </table>
@@ -1,9 +0,0 @@
1
- <div class="<%= classes %>">
2
- <table>
3
- <tbody>
4
- <tr>
5
- <td></td>
6
- </tr>
7
- </tbody>
8
- </table>
9
- </div>
@@ -1 +0,0 @@
1
- @import '../../../core/bootstrap-email'
@@ -1,230 +0,0 @@
1
- require 'nokogiri'
2
- require 'erb'
3
- require 'ostruct'
4
- require 'action_mailer'
5
- require 'premailer'
6
- require 'premailer/rails'
7
- require 'rails'
8
-
9
- module BootstrapEmail
10
- class Compiler
11
- def initialize mail
12
- @mail = mail
13
- @source = mail.html_part || mail
14
- update_doc(@source.body.raw_source)
15
- end
16
-
17
- def update_doc source
18
- @doc = Nokogiri::HTML(source)
19
- end
20
-
21
- def perform_full_compile
22
- compile_html!
23
- inline_css!
24
- inject_head!
25
- update_mailer!
26
- end
27
-
28
- def compile_html!
29
- button
30
- badge
31
- alert
32
- card
33
- hr
34
- container
35
- grid
36
- align
37
- padding
38
- margin
39
- spacer
40
- table
41
- body
42
- end
43
-
44
- def inline_css!
45
- @source.body = @doc.to_html
46
- @mail = Premailer::Rails::Hook.perform(@mail)
47
- @mail.header[:skip_premailer] = true
48
- update_doc(@mail.html_part.body.raw_source)
49
- end
50
-
51
- def inject_head!
52
- @doc.at_css('head').add_child(bootstrap_email_head)
53
- end
54
-
55
- def update_mailer!
56
- @mail.html_part.body = @doc.to_html
57
- @mail
58
- end
59
-
60
- private
61
-
62
- def bootstrap_email_head
63
- engine = defined?(SassC::Engine).nil? ? Sass::Engine : SassC::Engine
64
- html_string = <<-HEREDOC
65
- <style type="text/css">
66
- #{engine.new(File.open(File.expand_path('../core/head.scss', __dir__)).read, {syntax: :scss, style: :compressed, cache: false, read_cache: false}).render}
67
- </style>
68
- HEREDOC
69
- html_string
70
- end
71
-
72
- def template file, locals_hash = {}
73
- namespace = OpenStruct.new(locals_hash)
74
- template_html = File.open(File.expand_path("../core/templates/#{file}.html.erb", __dir__)).read
75
- ERB.new(template_html).result(namespace.instance_eval { binding })
76
- end
77
-
78
- def each_node css_lookup, &blk
79
- # sort by youngest child and traverse backwards up the tree
80
- @doc.css(css_lookup).sort_by{ |n| n.ancestors.size }.reverse!.each(&blk)
81
- end
82
-
83
- def button
84
- each_node('.btn') do |node| # move all classes up and remove all classes from the element
85
- node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
86
- end
87
- end
88
-
89
- def badge
90
- each_node('.badge') do |node| # move all classes up and remove all classes from the element
91
- node.replace(template('table-left', classes: node['class'], contents: node.delete('class') && node.to_html))
92
- end
93
- end
94
-
95
- def alert
96
- each_node('.alert') do |node| # move all classes up and remove all classes from the element
97
- node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
98
- end
99
- end
100
-
101
- def align
102
- each_node('.float-left') do |node|
103
- align_helper(node, /float-left/, 'left')
104
- end
105
- each_node('.mx-auto') do |node|
106
- align_helper(node, /mx-auto/, 'center')
107
- end
108
- each_node('.float-right') do |node|
109
- align_helper(node, /float-right/, 'right')
110
- end
111
- end
112
-
113
- def align_helper node, klass, template
114
- if node.name != 'table' # if it is already on a table, set the proprieties on the current table
115
- node['class'] = node['class'].sub(klass, '')
116
- node.replace(template("align-#{template}", contents: node.to_html))
117
- else
118
- node['align'] = template
119
- end
120
- end
121
-
122
- def card
123
- each_node('.card') do |node| # move all classes up and remove all classes from element
124
- node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
125
- end
126
- each_node('.card-body') do |node| # move all classes up and remove all classes from element
127
- node.replace(template('table', classes: node['class'], contents: node.delete('class') && node.to_html))
128
- end
129
- end
130
-
131
- def hr
132
- each_node('hr') do |node| # drop hr in place of current
133
- node.replace(template('hr', classes: "hr #{node['class']}"))
134
- end
135
- end
136
-
137
- def container
138
- each_node('.container') do |node|
139
- node.replace(template('container', classes: node['class'], contents: node.inner_html))
140
- end
141
- each_node('.container-fluid') do |node|
142
- node.replace(template('table', classes: node['class'], contents: node.inner_html))
143
- end
144
- end
145
-
146
- def grid
147
- each_node('.row') do |node|
148
- node.replace(template('row', classes: node['class'], contents: node.inner_html))
149
- end
150
- each_node('*[class*=col]') do |node|
151
- node.replace(template('col', classes: node['class'], contents: node.inner_html))
152
- end
153
- end
154
-
155
- def padding
156
- each_node('*[class*=p-], *[class*=pt-], *[class*=pr-], *[class*=pb-], *[class*=pl-], *[class*=px-], *[class*=py-]') do |node|
157
- if node.name != 'table' # if it is already on a table, set the padding on the table, else wrap the content in a table
158
- padding_regex = /(p[trblxy]?-\d)/
159
- classes = node['class'].scan(padding_regex).join(' ')
160
- node['class'] = node['class'].gsub(padding_regex, '')
161
- node.replace(template('table', classes: classes, contents: node.to_html))
162
- end
163
- end
164
- end
165
-
166
- def margin
167
- each_node('*[class*=my-], *[class*=mt-], *[class*=mb-]') do |node|
168
- top_class = node['class'][/m[ty]{1}-(lg-)?(\d)/]
169
- bottom_class = node['class'][/m[by]{1}-(lg-)?(\d)/]
170
- node['class'] = node['class'].gsub(/(m[tby]{1}-(lg-)?\d)/, '')
171
- html = ''
172
- if top_class
173
- html += template('div', classes: "s-#{top_class.gsub(/m[ty]{1}-/, '')}", contents: nil)
174
- end
175
- html += node.to_html
176
- if bottom_class
177
- html += template('div', classes: "s-#{bottom_class.gsub(/m[by]{1}-/, '')}", contents: nil)
178
- end
179
- node.replace(html)
180
- end
181
- end
182
-
183
- def spacer
184
- spacers = {
185
- '0' => 0,
186
- '1' => (16 * 0.25),
187
- '2' => (16 * 0.5),
188
- '3' => 16,
189
- '4' => (16 * 1.5),
190
- '5' => (16 * 3)
191
- }
192
- each_node('*[class*=s-]') do |node|
193
- temp = Nokogiri::HTML::DocumentFragment.parse(template('table', classes: node['class'] + ' w-100', contents: '&nbsp;'))
194
- temp.at_css('td')['height'] = spacers[node['class'].gsub(/s-/, '')].to_i
195
- node.replace(temp)
196
- end
197
- end
198
-
199
- def table
200
- @doc.css('table').each do |node|
201
- # border="0" cellpadding="0" cellspacing="0"
202
- node['border'] = 0
203
- node['cellpadding'] = 0
204
- node['cellspacing'] = 0
205
- end
206
- end
207
-
208
- def body
209
- @doc.css('body').each do |node|
210
- node.replace('<body>' + preview_text.to_s + template('body', classes: "#{node['class']} body", contents: node.inner_html.to_s) + '</body>')
211
- end
212
- end
213
-
214
- def preview_text
215
- preview_node = @doc.at_css('preview')
216
- if preview_node.present?
217
- # apply spacing after the text max of 100 characters so it doesn't show body text
218
- preview_node.content += '&nbsp;' * (100 - preview_node.content.length.to_i)
219
- node = template('div', classes: 'preview', contents: preview_node.content)
220
- preview_node.remove
221
- return node
222
- end
223
- end
224
- end
225
- end
226
-
227
- require 'bootstrap-email/premailer_railtie'
228
- require 'bootstrap-email/action_mailer'
229
- require 'bootstrap-email/engine'
230
- require 'bootstrap-email/version'