better_image_tag 0.2.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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +311 -0
- data/Rakefile +12 -0
- data/app/controllers/concerns/better_image_tag/image_taggable.rb +48 -0
- data/better_image_tag.gemspec +46 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/inlineable +10 -0
- data/lib/better_image_tag/base_image_tag.rb +37 -0
- data/lib/better_image_tag/commands/clear_inline_cache.rb +29 -0
- data/lib/better_image_tag/commands/convert_jpg_to_avif.rb +52 -0
- data/lib/better_image_tag/commands/convert_jpg_to_webp.rb +52 -0
- data/lib/better_image_tag/engine.rb +6 -0
- data/lib/better_image_tag/errors.rb +13 -0
- data/lib/better_image_tag/image_tag.rb +172 -0
- data/lib/better_image_tag/inline_data.rb +102 -0
- data/lib/better_image_tag/picture_tag.rb +103 -0
- data/lib/better_image_tag/railtie.rb +15 -0
- data/lib/better_image_tag/rspec.rb +24 -0
- data/lib/better_image_tag/svg_tag.rb +36 -0
- data/lib/better_image_tag/tasks/clear_inline_cache.rake +10 -0
- data/lib/better_image_tag/tasks/convert_jpgs.rake +16 -0
- data/lib/better_image_tag/version.rb +3 -0
- data/lib/better_image_tag.rb +56 -0
- metadata +207 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterImageTag
|
4
|
+
module Commands
|
5
|
+
class ConvertJpgToAvif
|
6
|
+
def self.call
|
7
|
+
new.call
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@jpgs_converted = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
ensure_avif_present!
|
16
|
+
|
17
|
+
jpg_assets.each do |jpg|
|
18
|
+
avif = jpg.gsub(/\.jpe?g\z/i, '.avif')
|
19
|
+
next if File.exist? avif
|
20
|
+
|
21
|
+
@jpgs_converted += 1 if system("avif -q 32 -e #{jpg} -o #{avif}")
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "#{@jpgs_converted} jpgs converted to avif."
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def ensure_avif_present!
|
30
|
+
return if avif_exists?
|
31
|
+
|
32
|
+
raise(
|
33
|
+
BetterImageTag::Errors::AvifNotFound,
|
34
|
+
"'avif' not found. Please install go-avif."
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def avif_exists?
|
39
|
+
`which avif`
|
40
|
+
$CHILD_STATUS.success?
|
41
|
+
end
|
42
|
+
|
43
|
+
def jpg_assets
|
44
|
+
Dir.glob "#{asset_path}/**/*.{jpg,jpeg}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def asset_path
|
48
|
+
BetterImageTag.configuration.images_path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterImageTag
|
4
|
+
module Commands
|
5
|
+
class ConvertJpgToWebp
|
6
|
+
def self.call
|
7
|
+
new.call
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@jpgs_converted = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
ensure_convert_present!
|
16
|
+
|
17
|
+
jpg_assets.each do |jpg|
|
18
|
+
webp = jpg.gsub(/\.jpe?g\z/i, '.webp')
|
19
|
+
next if File.exist? webp
|
20
|
+
|
21
|
+
@jpgs_converted += 1 if system("convert #{jpg} #{webp}")
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "#{@jpgs_converted} jpgs converted to webp."
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def ensure_convert_present!
|
30
|
+
return if convert_exists?
|
31
|
+
|
32
|
+
raise(
|
33
|
+
BetterImageTag::Errors::ConvertNotFound,
|
34
|
+
"'convert' not found. Please install ImageMagick."
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def convert_exists?
|
39
|
+
`which convert`
|
40
|
+
$CHILD_STATUS.success?
|
41
|
+
end
|
42
|
+
|
43
|
+
def jpg_assets
|
44
|
+
Dir.glob "#{asset_path}/**/*.{jpg,jpeg}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def asset_path
|
48
|
+
BetterImageTag.configuration.images_path
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterImageTag
|
4
|
+
module Errors
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
class MissingAltTag < Error; end
|
8
|
+
class EarlyLazyLoad < Error; end
|
9
|
+
class ConvertNotFound < Error; end
|
10
|
+
class AvifNotFound < Error; end
|
11
|
+
class FileNotFound < Error; end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fastimage'
|
4
|
+
|
5
|
+
module BetterImageTag
|
6
|
+
class ImageTag
|
7
|
+
TRANSPARENT_GIF = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
|
8
|
+
|
9
|
+
attr_reader :view_context, :options, :images, :tablet_options, :desktop_options
|
10
|
+
attr_accessor :image
|
11
|
+
|
12
|
+
def initialize(view_context, image, options = {})
|
13
|
+
@view_context = view_context
|
14
|
+
@image = with_protocol(image)
|
15
|
+
@images = []
|
16
|
+
@options = options.symbolize_keys
|
17
|
+
|
18
|
+
avif(@options[:avif]) if @options[:avif]
|
19
|
+
webp(@options[:webp]) if @options[:webp]
|
20
|
+
enforce_requirements
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_size
|
24
|
+
return self if options[:width].present? || options[:height].present?
|
25
|
+
return self unless BetterImageTag.configuration.sizing_enabled
|
26
|
+
|
27
|
+
dims = cache("image_tag:with_size:#{image}") { FastImage.size(asset) }
|
28
|
+
options[:width] = dims&.first
|
29
|
+
options[:height] = dims&.last
|
30
|
+
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/AbcSize
|
35
|
+
def lazy_load(enabled: true)
|
36
|
+
return self unless enabled
|
37
|
+
|
38
|
+
options[:class] = Array(options.fetch(:class, [])).join(' ')
|
39
|
+
options[:class] = "#{options[:class]} lazyload".strip
|
40
|
+
options[:data] = options[:data]
|
41
|
+
.to_h
|
42
|
+
.merge(src: view_context.image_path(image))
|
43
|
+
|
44
|
+
@image = TRANSPARENT_GIF
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
# rubocop:enable Metrics/AbcSize
|
49
|
+
|
50
|
+
def webp(url = nil)
|
51
|
+
lazy_load_last!
|
52
|
+
@images << (url || image.gsub(/\.[a-z]{2,}*\z/, '.webp'))
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def avif(url = nil)
|
57
|
+
lazy_load_last!
|
58
|
+
@images << (url || image.gsub(/\.[a-z]{2,}*\z/, '.avif'))
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def inline
|
63
|
+
@image = InlineData.new(@image).inline_data
|
64
|
+
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_s
|
69
|
+
(svg_string || image_tag_string || picture_tag_string).html_safe
|
70
|
+
end
|
71
|
+
|
72
|
+
def picture_tag
|
73
|
+
result = view_context.image_tag(
|
74
|
+
image,
|
75
|
+
options.except(:webp, :avif).merge(use_super: true)
|
76
|
+
)
|
77
|
+
PictureTag.new(self, result)
|
78
|
+
end
|
79
|
+
|
80
|
+
def tablet_up(*tablet_options)
|
81
|
+
@tablet_options = tablet_options
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def desktop_up(*desktop_options)
|
86
|
+
@desktop_options = desktop_options
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def svg_string
|
93
|
+
return unless svg?
|
94
|
+
|
95
|
+
SvgTag.new(self).to_s
|
96
|
+
end
|
97
|
+
|
98
|
+
def image_tag_string
|
99
|
+
return if images.any? || tablet_options&.any? || desktop_options&.any?
|
100
|
+
|
101
|
+
view_context.image_tag(
|
102
|
+
image,
|
103
|
+
options.except(:webp, :avif).merge(super_options)
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
def picture_tag_string
|
108
|
+
picture_tag.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
def svg?
|
112
|
+
MimeMagic.by_magic(@image)&.type == 'image/svg+xml'
|
113
|
+
end
|
114
|
+
|
115
|
+
def super_options
|
116
|
+
if images.any?
|
117
|
+
{ use_picture: true }
|
118
|
+
else
|
119
|
+
{ use_super: true }
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def lazy_load_last!
|
124
|
+
return unless image.match?(/^data:/)
|
125
|
+
|
126
|
+
raise EarlyLazyLoad, 'Run lazy_load as the last method in chain'
|
127
|
+
end
|
128
|
+
|
129
|
+
def with_protocol(image)
|
130
|
+
image.match?(%r{^//}) ? "https:#{image}" : image
|
131
|
+
end
|
132
|
+
|
133
|
+
def asset
|
134
|
+
@_asset ||= begin
|
135
|
+
if image.match?(%r{https?://}) || !Object.const_defined?(:Rails)
|
136
|
+
image
|
137
|
+
elsif not_compiled?
|
138
|
+
Rails.application.assets[image].filename
|
139
|
+
else
|
140
|
+
file = Rails.application.assets_manifest.assets[image]
|
141
|
+
|
142
|
+
if file.nil?
|
143
|
+
raise(
|
144
|
+
BetterImageTag::Errors::FileNotFound,
|
145
|
+
"Not found in asset manifest: #{image}"
|
146
|
+
)
|
147
|
+
end
|
148
|
+
|
149
|
+
File.join(Rails.application.assets_manifest.directory, file)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def not_compiled?
|
155
|
+
!!Rails.application.assets
|
156
|
+
end
|
157
|
+
|
158
|
+
def enforce_requirements
|
159
|
+
if BetterImageTag.configuration.require_alt_tags && options[:alt].blank?
|
160
|
+
raise Errors::MissingAltTag, "#{image} is missing an alt tag"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def cache(tag, &block)
|
165
|
+
return unless block
|
166
|
+
|
167
|
+
return block.call unless BetterImageTag.configuration.cache_sizing_enabled
|
168
|
+
|
169
|
+
Rails.cache.fetch tag, &block
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mimemagic'
|
4
|
+
require 'base64'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
module BetterImageTag
|
8
|
+
class InlineData
|
9
|
+
HTTP_ERRORS = [
|
10
|
+
EOFError,
|
11
|
+
Errno::ECONNRESET,
|
12
|
+
Errno::EINVAL,
|
13
|
+
Net::HTTPBadResponse,
|
14
|
+
Net::HTTPHeaderSyntaxError,
|
15
|
+
Net::ProtocolError,
|
16
|
+
Timeout::Error,
|
17
|
+
OpenSSL::SSL::SSLError,
|
18
|
+
OpenURI::HTTPError
|
19
|
+
].freeze
|
20
|
+
|
21
|
+
CACHE_PREFIX = 'inline_data'
|
22
|
+
|
23
|
+
def self.inline_data(*args)
|
24
|
+
new(*args).inline_data
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :image
|
28
|
+
|
29
|
+
def initialize(image, local_file: false)
|
30
|
+
@image = image
|
31
|
+
@local_file = local_file
|
32
|
+
end
|
33
|
+
|
34
|
+
def inline_data
|
35
|
+
return image unless BetterImageTag.configuration.inlining_enabled
|
36
|
+
|
37
|
+
cache "#{CACHE_PREFIX}:#{image}" do
|
38
|
+
svg? ? contents : "data:#{content_type};base64,#{base64_contents}"
|
39
|
+
end
|
40
|
+
rescue *HTTP_ERRORS
|
41
|
+
image
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def cache(tag, &block)
|
47
|
+
return unless block
|
48
|
+
|
49
|
+
unless BetterImageTag.configuration.cache_inlining_enabled
|
50
|
+
return block.call
|
51
|
+
end
|
52
|
+
|
53
|
+
Rails.cache.fetch tag, &block
|
54
|
+
end
|
55
|
+
|
56
|
+
def svg?
|
57
|
+
content_type == "image/svg+xml"
|
58
|
+
end
|
59
|
+
|
60
|
+
def content_type
|
61
|
+
MimeMagic.by_magic(contents).type
|
62
|
+
end
|
63
|
+
|
64
|
+
def base64_contents
|
65
|
+
Base64.strict_encode64 contents
|
66
|
+
end
|
67
|
+
|
68
|
+
# rubocop:disable Security/Open
|
69
|
+
def contents
|
70
|
+
@_contents ||= begin
|
71
|
+
if image.match?(%r{https?://})
|
72
|
+
URI.open(image).read
|
73
|
+
elsif local_file?
|
74
|
+
File.read(image)
|
75
|
+
elsif not_compiled?
|
76
|
+
Rails.application.assets[image].to_s
|
77
|
+
else
|
78
|
+
file = Rails.application.assets_manifest.assets[image]
|
79
|
+
|
80
|
+
if file.nil?
|
81
|
+
raise(
|
82
|
+
BetterImageTag::Errors::FileNotFound,
|
83
|
+
"Not found in asset manifest: #{image}"
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
path = File.join(Rails.application.assets_manifest.directory, file)
|
88
|
+
File.read(path)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
# rubocop:enable Security/Open
|
93
|
+
|
94
|
+
def not_compiled?
|
95
|
+
!!Rails.application.assets
|
96
|
+
end
|
97
|
+
|
98
|
+
def local_file?
|
99
|
+
@local_file
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module BetterImageTag
|
6
|
+
class PictureTag
|
7
|
+
attr_reader :image_tag, :default_image_tag, :sources, :srcset
|
8
|
+
|
9
|
+
extend Forwardable
|
10
|
+
def_delegators :image_tag, :image, :images, :view_context
|
11
|
+
def_delegators :view_context, :image_path
|
12
|
+
def_delegators :config, :tablet_breakpoint, :desktop_breakpoint
|
13
|
+
|
14
|
+
def initialize(image_tag, default_image_tag)
|
15
|
+
@image_tag = image_tag
|
16
|
+
@default_image_tag = default_image_tag
|
17
|
+
@sources = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
output(:lazily) || output(:normally)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def config
|
27
|
+
BetterImageTag.configuration
|
28
|
+
end
|
29
|
+
|
30
|
+
def output(loading_style)
|
31
|
+
return if loading_style == :lazily && image != ImageTag::TRANSPARENT_GIF
|
32
|
+
|
33
|
+
@srcset = loading_style == :lazily ? 'data-srcset' : 'srcset'
|
34
|
+
css_class = css_class? ? %( class="#{css_classes}") : ''
|
35
|
+
|
36
|
+
populate_responsive_and_format_sources
|
37
|
+
populate_responsive_sources
|
38
|
+
populate_format_sources
|
39
|
+
|
40
|
+
<<~EOPICTURE
|
41
|
+
<picture#{css_class}>
|
42
|
+
<!--[if IE 9]><video style="display: none;"><![endif]-->
|
43
|
+
#{sources.join("\n ")}
|
44
|
+
<!--[if IE 9]></video><![endif]-->
|
45
|
+
#{default_image_tag}
|
46
|
+
</picture>
|
47
|
+
EOPICTURE
|
48
|
+
end
|
49
|
+
|
50
|
+
def css_class?
|
51
|
+
image_tag.options[:class].present?
|
52
|
+
end
|
53
|
+
|
54
|
+
def css_classes
|
55
|
+
image_tag.options[:class].split(' ').map do |css_class|
|
56
|
+
css_class == 'lazyload' ? 'lazyload' : "#{css_class}--picture"
|
57
|
+
end.join(" ")
|
58
|
+
end
|
59
|
+
|
60
|
+
def populate_responsive_and_format_sources
|
61
|
+
if image_tag.tablet_options&.second
|
62
|
+
if image_tag.tablet_options.second[:avif]
|
63
|
+
@sources << %(<source media="(min-width: #{tablet_breakpoint})" type="image/avif" #{srcset}="#{image_path image_tag.tablet_options.second[:avif]}">)
|
64
|
+
end
|
65
|
+
|
66
|
+
if image_tag.tablet_options.second[:webp]
|
67
|
+
@sources << %(<source media="(min-width: #{tablet_breakpoint})" type="image/webp" #{srcset}="#{image_path image_tag.tablet_options.second[:webp]}">)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if image_tag.desktop_options&.second
|
72
|
+
if image_tag.desktop_options.second[:avif]
|
73
|
+
@sources << %(<source media="(min-width: #{desktop_breakpoint})" type="image/avif" #{srcset}="#{image_path image_tag.desktop_options.second[:avif]}">)
|
74
|
+
end
|
75
|
+
|
76
|
+
if image_tag.desktop_options.second[:webp]
|
77
|
+
@sources << %(<source media="(min-width: #{desktop_breakpoint})" type="image/webp" #{srcset}="#{image_path image_tag.desktop_options.second[:webp]}">)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def populate_format_sources
|
83
|
+
return if images.empty?
|
84
|
+
|
85
|
+
@sources += images.map do |image|
|
86
|
+
type = image.match?(/webp$/) ? 'webp' : 'avif'
|
87
|
+
%(<source type="image/#{type}" #{srcset}="#{image_path image}">)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def populate_responsive_sources
|
92
|
+
return if image_tag.tablet_options.blank? && image_tag.desktop_options.blank?
|
93
|
+
|
94
|
+
if image_tag.tablet_options
|
95
|
+
@sources << %(<source media="(min-width: #{tablet_breakpoint})" #{srcset}="#{image_path image_tag.tablet_options.first}">)
|
96
|
+
end
|
97
|
+
|
98
|
+
if image_tag.desktop_options
|
99
|
+
@sources << %(<source media="(min-width: #{desktop_breakpoint})" #{srcset}="#{image_path image_tag.desktop_options.first}">)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'better_image_tag'
|
4
|
+
require 'rails'
|
5
|
+
|
6
|
+
module BetterImageTag
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
railtie_name :better_image_tag
|
9
|
+
|
10
|
+
rake_tasks do
|
11
|
+
path = File.expand_path(__dir__)
|
12
|
+
Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module BetterImageTag
|
2
|
+
module SpecHelpers
|
3
|
+
def disable_better_image_tag_sizing!
|
4
|
+
BetterImageTag.configuration.sizing_enabled = false
|
5
|
+
end
|
6
|
+
|
7
|
+
def disable_better_image_tag_inlining!
|
8
|
+
BetterImageTag.configuration.inlining_enabled = false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ViewSpecHelpers
|
13
|
+
def better_image_tag(options = {})
|
14
|
+
view.send(:extend, BetterImageTag::ImageTaggable)
|
15
|
+
|
16
|
+
without_partial_double_verification do
|
17
|
+
allow(view.class).
|
18
|
+
to receive(:better_image_tag_options).
|
19
|
+
and_return(options)
|
20
|
+
allow(view).to receive(:view_context).and_return(view)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BetterImageTag
|
4
|
+
class SvgTag
|
5
|
+
attr_reader :image_tag
|
6
|
+
|
7
|
+
def initialize(image_tag)
|
8
|
+
@image_tag = image_tag
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
image_tag.image.gsub!(/^\<svg /, %(<svg height="#{height}" )) if height
|
13
|
+
image_tag.image.gsub!(/^\<svg /, %(<svg width="#{width}" )) if width
|
14
|
+
|
15
|
+
if css_class
|
16
|
+
image_tag.image.gsub!(/^\<svg /, %(<svg class="#{css_class}" ))
|
17
|
+
end
|
18
|
+
|
19
|
+
image_tag.image
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def width
|
25
|
+
image_tag.options[:width]
|
26
|
+
end
|
27
|
+
|
28
|
+
def height
|
29
|
+
image_tag.options[:height]
|
30
|
+
end
|
31
|
+
|
32
|
+
def css_class
|
33
|
+
image_tag.options[:class]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'better_image_tag/commands/convert_jpg_to_webp'
|
4
|
+
require 'better_image_tag/commands/convert_jpg_to_avif'
|
5
|
+
|
6
|
+
namespace :better_image_tag do
|
7
|
+
desc 'Convert jpgs to webp'
|
8
|
+
task :convert_jpgs_to_webp do
|
9
|
+
BetterImageTag::Commands::ConvertJpgToWebp.call
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Convert jpgs to avif'
|
13
|
+
task :convert_jpgs_to_avif do
|
14
|
+
BetterImageTag::Commands::ConvertJpgToAvif.call
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'better_image_tag/version'
|
4
|
+
require 'better_image_tag/errors'
|
5
|
+
require 'better_image_tag/picture_tag'
|
6
|
+
require 'better_image_tag/svg_tag'
|
7
|
+
require 'better_image_tag/image_tag'
|
8
|
+
require 'better_image_tag/base_image_tag'
|
9
|
+
require 'better_image_tag/inline_data'
|
10
|
+
require_relative '../app/controllers/concerns/better_image_tag/image_taggable'
|
11
|
+
require 'better_image_tag/railtie' if Object.const_defined?(:Rails)
|
12
|
+
|
13
|
+
module BetterImageTag
|
14
|
+
class << self
|
15
|
+
attr_writer :configuration
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configuration
|
19
|
+
@configuration ||= Configuration.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configure
|
23
|
+
self.configuration ||= Configuration.new
|
24
|
+
yield(configuration)
|
25
|
+
end
|
26
|
+
|
27
|
+
class Configuration
|
28
|
+
attr_accessor(
|
29
|
+
:cache_inlining_enabled,
|
30
|
+
:cache_sizing_enabled,
|
31
|
+
:inlining_enabled,
|
32
|
+
:require_alt_tags,
|
33
|
+
:sizing_enabled,
|
34
|
+
:images_path,
|
35
|
+
:tablet_breakpoint,
|
36
|
+
:desktop_breakpoint
|
37
|
+
)
|
38
|
+
|
39
|
+
def initialize
|
40
|
+
@require_alt_tags = false
|
41
|
+
@cache_sizing_enabled = false
|
42
|
+
@cache_inlining_enabled = false
|
43
|
+
@inlining_enabled = true
|
44
|
+
@sizing_enabled = true
|
45
|
+
@tablet_breakpoint = '768px'
|
46
|
+
@desktop_breakpoint = '1280px'
|
47
|
+
@images_path = "#{rails_root}/app/assets/images"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def rails_root
|
53
|
+
Object.const_defined?(:Rails) ? Rails.root : '.'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|