jekyll_img 0.2.6 → 0.2.8
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/CHANGELOG.md +12 -1
- data/README.md +130 -1
- data/jekyll_img.gemspec +12 -10
- data/lib/img_builder.rb +68 -78
- data/lib/img_props.rb +4 -11
- data/lib/jekyll_img/version.rb +1 -1
- data/lib/jekyll_img.rb +4 -1
- data/lib/source.rb +98 -0
- data/spec/img_builder_spec.rb +40 -17
- data/spec/img_props_spec.rb +2 -2
- data/spec/jekyll_img_spec.rb +5 -4
- data/spec/source_spec.rb +87 -0
- data/spec/status_persistence.txt +11 -5
- metadata +22 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc81e3ae00a242cf25f3444f45da2f621c692d6c7ee4760a17b59e8acc672c4d
|
4
|
+
data.tar.gz: 8b10b63f09b8b586343fd7a4ccc42c205ad1618cb9b422712d2300838799ead3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97b5302cf184c4fa544498354f1a8bae327f1567c438d88a232eef174422425d9e416bd6b0568439c1df2544cfb9b534e3cf0603931e00b9b2ff6a708c00abd5
|
7
|
+
data.tar.gz: a3ebcc031fb6e63dc92073cf358a38c7092719df8fbf028a5e3376d5b3447e3a539647d0825f47a47c27b45121345c91fecd1af99fc00b1ee816906b71d9c638
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## 0.2.
|
3
|
+
## 0.2.8 / 2025-05-16
|
4
|
+
|
5
|
+
* An image can now be lazily loaded by providing the `lazy` keyword.
|
6
|
+
* An image can now be fetched with high priority by providing the `priority` keyword.
|
7
|
+
|
8
|
+
|
9
|
+
## 0.2.7 / 2024-09-11
|
10
|
+
|
11
|
+
* Further tweaking of the generated HTML.
|
12
|
+
|
13
|
+
|
14
|
+
## 0.2.6 / 2024-09-10
|
4
15
|
|
5
16
|
* Optimized the generated HTML.
|
6
17
|
For example, `srcset` elements are now only generated for images that actually exit locally.
|
data/README.md
CHANGED
@@ -74,6 +74,135 @@ If the browser did not support the `picture` element,
|
|
74
74
|
the `img src` attribute would be used to specify the image.
|
75
75
|
|
76
76
|
|
77
|
+
### Default and Relative Paths
|
78
|
+
|
79
|
+
Local images whose path does not start with a slash are assumed to be relative to `/assets/images`.
|
80
|
+
Simply specifying the filename of the image will cause it to be fetched from
|
81
|
+
`/assets/images/`.
|
82
|
+
For example, the following all fetch the same image:
|
83
|
+
|
84
|
+
```html
|
85
|
+
{% img src="/assets/images/blah.webp" %}
|
86
|
+
{% img src="blah.webp" %}
|
87
|
+
{% img src="blah" %}
|
88
|
+
```
|
89
|
+
|
90
|
+
To specify an image in a subdirectory of where the page resides,
|
91
|
+
prepend the relative path with a dot (`.`).
|
92
|
+
|
93
|
+
For example, if the current page resides in a [Jekyll collection](https://jekyllrb.com/docs/collections/)
|
94
|
+
with path `/collections/_av_studio/`,
|
95
|
+
and an image resides in the `/collections/_av_studio/images` subdirectory,
|
96
|
+
the following would result in the same image being displayed:
|
97
|
+
|
98
|
+
```html
|
99
|
+
{% img src="/av_studio/images/blah" %}
|
100
|
+
{% img src="./images/blah" %}
|
101
|
+
```
|
102
|
+
|
103
|
+
### Lazy Loading
|
104
|
+
|
105
|
+
An image can be lazily loaded by providing the `lazy` keyword.
|
106
|
+
This feature relies upon the lazy loading capability built into all modern browsers and does not rely upon JavaScript.
|
107
|
+
|
108
|
+
From [Browser-level image lazy loading for the web](https://web.dev/articles/browser-level-image-lazy-loading#dimension-attributes)
|
109
|
+
on `web.dev`:
|
110
|
+
|
111
|
+
> While the browser loads an image, it doesn't immediately know the image's dimensions,
|
112
|
+
> unless they're explicitly specified.
|
113
|
+
> To let the browser reserve enough space on a page for images, and avoid disruptive layout shifts,
|
114
|
+
> we recommend adding width and height attributes to all tags.
|
115
|
+
>
|
116
|
+
> <img src="image.png" loading="lazy" alt="…" width="200" height="200">
|
117
|
+
Alternatively, specify their values directly in an inline style:
|
118
|
+
>
|
119
|
+
> <img src="image.png" loading="lazy" alt="…" style="height:200px; width:200px;">
|
120
|
+
|
121
|
+
This would translate to:
|
122
|
+
|
123
|
+
```html
|
124
|
+
{% img lazy
|
125
|
+
src="blah.webp"
|
126
|
+
style="height:200px; width:200px"
|
127
|
+
%}
|
128
|
+
```
|
129
|
+
|
130
|
+
Note that the image must be fetched in order for HTML `img` attributes `height='auto'` and `width='auto'` to work.
|
131
|
+
Thus lazy loading is incompatible with a dimension whose value is specified as `auto`.
|
132
|
+
This means that, for lazily loaded images,
|
133
|
+
`height` and `width` must have values that are computable without loading the image.
|
134
|
+
Because the Jekyll `img` tag's `size` attribute only specifies the `width` attribute,
|
135
|
+
and sets `height` to `auto`, it should not be used with the `lazy` keyword.
|
136
|
+
|
137
|
+
The following examples of implicit and explicit `auto` dimensions will all give problems:
|
138
|
+
|
139
|
+
```html
|
140
|
+
{% img lazy
|
141
|
+
caption="A warning will be written to the logger"
|
142
|
+
size="200px"
|
143
|
+
src="blah.webp"
|
144
|
+
%}
|
145
|
+
|
146
|
+
{% img lazy
|
147
|
+
src="blah.webp"
|
148
|
+
style="height:auto; width:200px"
|
149
|
+
%}
|
150
|
+
|
151
|
+
{% img lazy
|
152
|
+
src="blah.webp"
|
153
|
+
style="height:200px; width:auto"
|
154
|
+
%}
|
155
|
+
|
156
|
+
{% img lazy
|
157
|
+
src="blah.webp"
|
158
|
+
style="height:200px"
|
159
|
+
%}
|
160
|
+
|
161
|
+
{% img lazy
|
162
|
+
src="blah.webp"
|
163
|
+
style="width:200px"
|
164
|
+
%}
|
165
|
+
```
|
166
|
+
|
167
|
+
|
168
|
+
### High Priority Loading
|
169
|
+
|
170
|
+
An image can be fetched with high priority by providing the `priority` keyword.
|
171
|
+
|
172
|
+
Sample usage:
|
173
|
+
|
174
|
+
```html
|
175
|
+
{% img priority src="blah.webp" %}
|
176
|
+
```
|
177
|
+
|
178
|
+
The above generates an HTML `img` tag with a `fetchpriority='high'` attribute.
|
179
|
+
|
180
|
+
From [Browser-level image lazy loading for the web](https://web.dev/articles/browser-level-image-lazy-loading#loading-priority)
|
181
|
+
on `web.dev`:
|
182
|
+
|
183
|
+
> If you want to increase the fetch priority of an important image, use `fetchpriority="high"`.
|
184
|
+
>
|
185
|
+
> An image with `loading="lazy"` and `fetchpriority="high"` is still delayed while it's off-screen,
|
186
|
+
> and then fetched with a high priority when it's almost within the viewport.
|
187
|
+
> This combination isn't really necessary because the browser would likely load that image with high priority anyway.
|
188
|
+
|
189
|
+
|
190
|
+
Note that the image must be fetched in order for `img` attributes `height='auto'` and `width='auto'` to work.
|
191
|
+
This means that `height` and `width` must have values that are computable without loading
|
192
|
+
the image or lazy loading will not work properly.
|
193
|
+
Because the `img` tag's `size` attribute only specifies the `width` attribute, and sets `height` to `auto`,
|
194
|
+
it should not be used.
|
195
|
+
|
196
|
+
Sample usage combining lazy and high priority loading:
|
197
|
+
|
198
|
+
```html
|
199
|
+
{% img lazy priority
|
200
|
+
src="blah.webp"
|
201
|
+
style="height:200px; width:200px"
|
202
|
+
%}
|
203
|
+
```
|
204
|
+
|
205
|
+
|
77
206
|
## Supported Filetypes
|
78
207
|
|
79
208
|
The following are listed in order of priority.
|
@@ -199,7 +328,7 @@ Using CSS units means that large enough values could cause the image to exceed t
|
|
199
328
|
|
200
329
|
## Installation
|
201
330
|
|
202
|
-
Add this line to your Jekyll project's Gemfile
|
331
|
+
Add this line to your Jekyll project's `Gemfile`, within the `jekyll_plugins` group:
|
203
332
|
|
204
333
|
```ruby
|
205
334
|
group :jekyll_plugins do
|
data/jekyll_img.gemspec
CHANGED
@@ -3,11 +3,11 @@ require_relative 'lib/jekyll_img/version'
|
|
3
3
|
Gem::Specification.new do |spec|
|
4
4
|
github = 'https://github.com/mslinn/jekyll_img'
|
5
5
|
|
6
|
-
spec.authors
|
7
|
-
spec.email
|
8
|
-
spec.files
|
6
|
+
spec.authors = ['Mike Slinn']
|
7
|
+
spec.email = ['mslinn@mslinn.com']
|
8
|
+
spec.files = Dir['.rubocop.yml', 'LICENSE.*', 'Rakefile', '{lib,spec}/**/*', '*.gemspec', '*.md']
|
9
9
|
spec.homepage = 'https://www.mslinn.com/jekyll_plugins/jekyll_img.html'
|
10
|
-
spec.license
|
10
|
+
spec.license = 'MIT'
|
11
11
|
spec.metadata = {
|
12
12
|
'allowed_push_host' => 'https://rubygems.org',
|
13
13
|
'bug_tracker_uri' => "#{github}/issues",
|
@@ -15,18 +15,20 @@ Gem::Specification.new do |spec|
|
|
15
15
|
'homepage_uri' => spec.homepage,
|
16
16
|
'source_code_uri' => github,
|
17
17
|
}
|
18
|
-
spec.name
|
18
|
+
spec.name = 'jekyll_img'
|
19
|
+
spec.platform = Gem::Platform::RUBY
|
19
20
|
spec.post_install_message = <<~END_MESSAGE
|
20
21
|
|
21
22
|
Thanks for installing #{spec.name}!
|
22
23
|
|
23
24
|
END_MESSAGE
|
24
|
-
spec.require_paths
|
25
|
+
spec.require_paths = ['lib']
|
25
26
|
spec.required_ruby_version = '>= 2.6.0'
|
26
|
-
spec.summary
|
27
|
-
spec.test_files
|
28
|
-
spec.version
|
27
|
+
spec.summary = 'Provides a Jekyll tag that generates images.'
|
28
|
+
spec.test_files = spec.files.grep %r{^(test|spec|features)/}
|
29
|
+
spec.version = JekyllImgVersion::VERSION
|
29
30
|
|
30
31
|
spec.add_dependency 'jekyll', '>= 3.5.0'
|
31
|
-
spec.add_dependency '
|
32
|
+
spec.add_dependency 'jekyll_draft', '>= 3.0.0'
|
33
|
+
spec.add_dependency 'jekyll_plugin_support', '>= 3.0.0'
|
32
34
|
end
|
data/lib/img_builder.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
# Like RoR's squish method
|
2
1
|
class String
|
2
|
+
def remove_blank_lines
|
3
|
+
strip.gsub(/^\s*$\n/, '')
|
4
|
+
end
|
5
|
+
|
6
|
+
# Like RoR's squish method
|
3
7
|
def squish
|
4
8
|
strip.gsub(/\s+/, ' ')
|
5
9
|
end
|
@@ -7,107 +11,93 @@ end
|
|
7
11
|
|
8
12
|
# Constructs HTML img tag from properties
|
9
13
|
class ImgBuilder
|
10
|
-
|
11
|
-
props.compute_dependant_properties
|
12
|
-
@props = props
|
13
|
-
end
|
14
|
+
attr_reader :img, :props, :source
|
14
15
|
|
15
|
-
def
|
16
|
+
def initialize(img, props)
|
17
|
+
@img = img
|
18
|
+
@props = props
|
16
19
|
@props.compute_dependant_properties
|
17
|
-
|
20
|
+
@source = Source.new @props.src
|
18
21
|
end
|
19
22
|
|
20
|
-
|
21
|
-
|
22
|
-
def generate_wrapper
|
23
|
-
classes = "imgWrapper #{@props.img_display} #{@props.align} #{@props.attr_size_class} #{@props.wrapper_class}".squish
|
24
|
-
result = <<~END_HTML
|
25
|
-
<div class='#{classes}' style='#{@props.attr_width_style} #{@props.wrapper_style}'>
|
26
|
-
#{"<figure>\n" if @props.caption}
|
27
|
-
#{ if @props.url
|
28
|
-
"<a href='#{@props.url}'#{@props.attr_target}#{@props.attr_nofollow} class='imgImgUrl'>#{generate_image}</a>"
|
29
|
-
else
|
30
|
-
generate_image
|
31
|
-
end
|
32
|
-
}
|
33
|
-
#{generate_figure_caption}
|
34
|
-
#{"</figure>\n" if @props.caption}
|
35
|
-
#{@props.attribute if @props.attribution}
|
36
|
-
</div>
|
37
|
-
END_HTML
|
38
|
-
result.strip.gsub(/^\s*$\n/, '')
|
39
|
-
end
|
40
|
-
|
41
|
-
def generate_figure_caption
|
42
|
-
return nil unless @props.caption
|
43
|
-
|
23
|
+
def generate_figcaption
|
44
24
|
<<~END_CAPTION
|
45
25
|
<figcaption class='imgFigCaption #{@props.attr_size_class}'>
|
46
|
-
#{
|
47
|
-
<<~END_URL
|
48
|
-
<a href="#{@props.url}" #{@props.attr_target} #{@props.attr_nofollow}>
|
49
|
-
#{@props.caption}
|
50
|
-
</a>
|
51
|
-
END_URL
|
52
|
-
else
|
53
|
-
@props.caption
|
54
|
-
end
|
55
|
-
}
|
26
|
+
#{@props.url ? generate_url_caption : @props.caption}
|
56
27
|
</figcaption>
|
57
28
|
END_CAPTION
|
58
29
|
end
|
59
30
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def generate_compact_sources
|
74
|
-
[
|
75
|
-
generate_sources(%w[svg], 'image/svg'),
|
76
|
-
generate_sources(%w[webp], 'image/webp'),
|
77
|
-
generate_sources(%w[png], 'image/png'),
|
78
|
-
generate_sources(%w[apng], 'image/apng'),
|
79
|
-
generate_sources(%w[jpg jpeg jfif pjpeg pjp], 'image/jpeg'),
|
80
|
-
generate_sources(%w[gif], 'image/gif'),
|
81
|
-
generate_sources(%w[tif tiff], 'image/tiff'),
|
82
|
-
generate_sources(%w[bmp], 'image/bmp'),
|
83
|
-
generate_sources(%w[cur ico], 'image/x-icon')
|
84
|
-
].compact.join("\n").strip.gsub(/^$\n/, '')
|
31
|
+
def generate_img
|
32
|
+
img_classes = @props.classes || 'rounded shadow'
|
33
|
+
<<~END_IMG
|
34
|
+
<img #{@props.attr_alt}
|
35
|
+
class="imgImg #{img_classes.squish}"
|
36
|
+
src="#{@source.src_fallback}"
|
37
|
+
#{@props.attr_style_img}
|
38
|
+
#{@props.attr_title}
|
39
|
+
#{@props.lazy}
|
40
|
+
#{@props.priority}
|
41
|
+
/>
|
42
|
+
END_IMG
|
85
43
|
end
|
86
44
|
|
87
45
|
# See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture
|
88
|
-
def
|
46
|
+
def generate_picture
|
47
|
+
if @props.lazy && @props.size
|
48
|
+
@img.logger.warn do
|
49
|
+
<<~END_MSG.squish
|
50
|
+
Warning: lazy loading was specified, but the size attribute was specified for the href tag
|
51
|
+
on line #{@img.line_number} (after front matter) of #{@img.page['path']}.
|
52
|
+
Specify dimensions via style or class attributes instead.
|
53
|
+
END_MSG
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
89
57
|
return generate_img if @props.src.start_with? 'http'
|
90
58
|
|
91
59
|
# avif is not well supported yet
|
92
60
|
# <source srcset="#{@props.src_any 'avif'}" type="image/avif">
|
93
61
|
result = <<~END_IMG
|
94
62
|
<picture#{@props.attr_id} class='imgPicture'>
|
95
|
-
#{
|
63
|
+
#{@source.generate.join("\n ")}
|
96
64
|
#{generate_img}
|
97
65
|
</picture>
|
98
66
|
END_IMG
|
99
67
|
result.strip
|
100
68
|
end
|
101
69
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
70
|
+
def generate_url_caption
|
71
|
+
<<~END_URL
|
72
|
+
<a href="#{@props.url}"#{@props.attr_target}#{@props.attr_nofollow}>
|
73
|
+
#{@props.caption}
|
74
|
+
</a>
|
75
|
+
END_URL
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate_url_wrapper
|
79
|
+
<<~END_HTML
|
80
|
+
<a href='#{@props.url}'#{@props.attr_target}#{@props.attr_nofollow} class='imgImgUrl'>
|
81
|
+
#{generate_picture}
|
82
|
+
</a>
|
83
|
+
END_HTML
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_wrapper
|
87
|
+
classes = "imgWrapper #{@props.img_display} #{@props.align} #{@props.attr_size_class} #{@props.wrapper_class}".squish
|
88
|
+
<<~END_HTML.remove_blank_lines
|
89
|
+
<div class='#{classes}' style='#{@props.attr_width_style} #{@props.wrapper_style}'>
|
90
|
+
#{"<figure>\n" if @props.caption}
|
91
|
+
#{@props.url ? generate_url_wrapper : generate_picture}
|
92
|
+
#{generate_figcaption if @props.caption}
|
93
|
+
#{"</figure>\n" if @props.caption}
|
94
|
+
#{@props.attribute if @props.attribution}
|
95
|
+
</div>
|
96
|
+
END_HTML
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
@props.compute_dependant_properties
|
101
|
+
generate_wrapper
|
112
102
|
end
|
113
103
|
end
|
data/lib/img_props.rb
CHANGED
@@ -6,10 +6,11 @@ require 'uri'
|
|
6
6
|
# All methods except compute_dependant_properties can be called in any order
|
7
7
|
class ImgProperties
|
8
8
|
attr_accessor :align, :alt, :attr_wrapper_align_class, :attribute, :attribution, :caption, :classes, :die_on_img_error,
|
9
|
-
:id, :img_display, :local_src, :nofollow, :src, :size, :style, :target, :title,
|
9
|
+
:id, :img_display, :lazy, :local_src, :nofollow, :priority, :src, :size, :style, :target, :title,
|
10
10
|
:url, :wrapper_class, :wrapper_style
|
11
11
|
|
12
12
|
SIZES = %w[eighthsize fullsize halfsize initial quartersize].freeze
|
13
|
+
UNITS = %w[Q ch cm em dvh dvw ex in lh lvh lvw mm pc px pt rem rlh svh svw vb vh vi vmax vmin vw %].freeze
|
13
14
|
|
14
15
|
def attr_alt
|
15
16
|
"alt='#{@alt}'" if @alt
|
@@ -69,13 +70,7 @@ class ImgProperties
|
|
69
70
|
end
|
70
71
|
|
71
72
|
def src_any(filetype)
|
72
|
-
@src.gsub(
|
73
|
-
end
|
74
|
-
|
75
|
-
def src_png
|
76
|
-
raise Jekyll::ImgError, "The 'src' parameter was not specified" if @src.to_s.empty?
|
77
|
-
|
78
|
-
@src.gsub('.webp', '.png')
|
73
|
+
@src.gsub(/\..*?$/, ".#{filetype}")
|
79
74
|
end
|
80
75
|
|
81
76
|
def self.local_path?(src)
|
@@ -86,7 +81,7 @@ class ImgProperties
|
|
86
81
|
private
|
87
82
|
|
88
83
|
def setup_src
|
89
|
-
raise Jekyll::ImgError, "The 'src' parameter was not specified" if @src.nil?
|
84
|
+
raise Jekyll::ImgError, "The 'src' parameter was not specified" if @src.nil? || [true, false].include?(@src)
|
90
85
|
|
91
86
|
raise Jekyll::ImgError, "The 'src' parameter was empty" if @src.empty?
|
92
87
|
|
@@ -103,8 +98,6 @@ class ImgProperties
|
|
103
98
|
# raise Jekyll::ImgError, "#{@src} does not exist" unless File.exist?(src)
|
104
99
|
end
|
105
100
|
|
106
|
-
UNITS = %w[Q ch cm em dvh dvw ex in lh lvh lvw mm pc px pt rem rlh svh svw vb vh vi vmax vmin vw %].freeze
|
107
|
-
|
108
101
|
def size_unit_specified?
|
109
102
|
return false if @size == false || @size.to_s.strip.empty?
|
110
103
|
|
data/lib/jekyll_img/version.rb
CHANGED
data/lib/jekyll_img.rb
CHANGED
@@ -3,6 +3,7 @@ require 'helper/jekyll_plugin_helper'
|
|
3
3
|
require 'pry'
|
4
4
|
require_relative 'img_builder'
|
5
5
|
require_relative 'img_props'
|
6
|
+
require_relative 'source'
|
6
7
|
require_relative 'jekyll_img/version'
|
7
8
|
|
8
9
|
# @author Copyright 2023 Michael Slinn
|
@@ -34,7 +35,9 @@ module Jekyll
|
|
34
35
|
props.classes = @helper.parameter_specified? 'class'
|
35
36
|
props.die_on_img_error = @die_on_img_error
|
36
37
|
props.id = @helper.parameter_specified? 'id'
|
38
|
+
props.lazy = ' loading="lazy"' if @helper.parameter_specified?('lazy')
|
37
39
|
props.nofollow = @helper.parameter_specified? 'nofollow'
|
40
|
+
props.priority = ' fetchpriority="high"' if @helper.parameter_specified?('priority')
|
38
41
|
props.size = @helper.parameter_specified?('size') || @helper.parameter_specified?('_size')
|
39
42
|
props.src = @helper.parameter_specified? 'src'
|
40
43
|
props.style = @helper.parameter_specified? 'style'
|
@@ -44,7 +47,7 @@ module Jekyll
|
|
44
47
|
props.wrapper_class = @helper.parameter_specified? 'wrapper_class'
|
45
48
|
props.wrapper_style = @helper.parameter_specified? 'wrapper_style'
|
46
49
|
|
47
|
-
@builder = ImgBuilder.new(props)
|
50
|
+
@builder = ImgBuilder.new(self, props)
|
48
51
|
@builder.to_s
|
49
52
|
rescue ImgError => e # jekyll_plugin_support handles StandardError
|
50
53
|
@logger.error { e.logger_message }
|
data/lib/source.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
class Source
|
2
|
+
RANKS = %w[svg webp apng png jpg jpeg jfif pjpeg pjp gif tif tiff bmp cur ico].freeze
|
3
|
+
RANKS_LENGTH = RANKS.length
|
4
|
+
|
5
|
+
def initialize(path)
|
6
|
+
raise Jekyll::ImgError, "The 'src' parameter was not specified" if path.nil?
|
7
|
+
raise Jekyll::ImgError, "The 'src' parameter was empty" if path.empty?
|
8
|
+
|
9
|
+
path.strip!
|
10
|
+
raise Jekyll::ImgError, "The 'src' parameter only contained whitespace" if path.empty?
|
11
|
+
|
12
|
+
@path = path
|
13
|
+
@absolute_path = @path.start_with?('/')
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return array of source statements for filetypes that exist locally;
|
17
|
+
# return nil if @path points to a remote image
|
18
|
+
def generate
|
19
|
+
return nil if @path.nil? || @path.start_with?('http')
|
20
|
+
|
21
|
+
result = sorted_files.map do |filename|
|
22
|
+
mtype = mimetype filename
|
23
|
+
next unless mtype
|
24
|
+
|
25
|
+
<<~END_HTML
|
26
|
+
<source srcset="#{filename}" type="#{mtype}">
|
27
|
+
END_HTML
|
28
|
+
end
|
29
|
+
result&.compact&.map(&:strip)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Webp is the least desired variant, png is most desired variant.
|
33
|
+
# @return URL of external image, otherwise return specified path unless it is a webp;
|
34
|
+
# in which case return most desired image variant that exists.
|
35
|
+
def src_fallback
|
36
|
+
return @path if @path.start_with? 'http'
|
37
|
+
|
38
|
+
result = @absolute_path ? @path.delete_prefix('.') : @path
|
39
|
+
png = result.gsub(/\.webp$/, '.png')
|
40
|
+
return png if File.exist? png
|
41
|
+
return result unless result.end_with? '.webp' # we know @path will be a webp after this
|
42
|
+
|
43
|
+
files = sorted_files
|
44
|
+
return files[0] if files.count == 1
|
45
|
+
|
46
|
+
files.each do |filename|
|
47
|
+
ext = File.extname filename
|
48
|
+
unless ext == '.webp'
|
49
|
+
return @absolute_path ? filename.delete_prefix('.') : filename
|
50
|
+
end
|
51
|
+
end
|
52
|
+
result
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Convert absolute paths (which reference the website root) to relative paths for globbing
|
58
|
+
def globbed_path
|
59
|
+
dir = File.dirname @path
|
60
|
+
dir = ".#{dir}" if dir.start_with?('/')
|
61
|
+
base = File.basename @path, ".*"
|
62
|
+
"#{dir}/#{base}.*"
|
63
|
+
end
|
64
|
+
|
65
|
+
def mimetype(path)
|
66
|
+
case File.extname(path)
|
67
|
+
when '.svg'
|
68
|
+
'image/svg'
|
69
|
+
when '.webp'
|
70
|
+
'image/webp'
|
71
|
+
when '.png'
|
72
|
+
'image/png'
|
73
|
+
when '.apng'
|
74
|
+
'image/apng'
|
75
|
+
when %w[.jpg .jpeg .jfif .pjpeg .pjp]
|
76
|
+
'image/jpeg'
|
77
|
+
when '.gif'
|
78
|
+
'image/gif'
|
79
|
+
when %w[.tif .tiff]
|
80
|
+
'image/tiff'
|
81
|
+
when '.bmp'
|
82
|
+
'image/bmp'
|
83
|
+
when %w[cur ico]
|
84
|
+
'image/x-icon'
|
85
|
+
# else
|
86
|
+
# raise Jekyll::ImgError, "#{path} has an unrecognized filetype."
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def sorted_files
|
91
|
+
sorted = Dir[globbed_path].sort_by do |path|
|
92
|
+
ext = File.extname(path)&.delete_prefix('.')
|
93
|
+
index = RANKS.index(ext)
|
94
|
+
index || RANKS_LENGTH
|
95
|
+
end
|
96
|
+
sorted.map { |x| @absolute_path ? x.delete_prefix('.') : x }
|
97
|
+
end
|
98
|
+
end
|
data/spec/img_builder_spec.rb
CHANGED
@@ -1,25 +1,50 @@
|
|
1
1
|
require 'rspec/match_ignoring_whitespace'
|
2
2
|
require_relative '../lib/img_builder'
|
3
3
|
require_relative '../lib/img_props'
|
4
|
+
require_relative '../lib/source'
|
4
5
|
|
5
6
|
# Test ImgProperties
|
6
7
|
class ImgPropertiesTest
|
7
8
|
RSpec.describe ImgBuilder do
|
8
9
|
Dir.chdir("demo")
|
10
|
+
props = ImgProperties.new
|
11
|
+
props.src = '/assets/images/jekyll.webp'
|
12
|
+
builder = described_class.new(props)
|
9
13
|
|
10
14
|
it 'generates sources' do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
actual = builder.source.generate
|
16
|
+
desired = [
|
17
|
+
'<source srcset="/assets/images/jekyll.webp" type="image/webp">',
|
18
|
+
'<source srcset="/assets/images/jekyll.png" type="image/png">'
|
19
|
+
]
|
20
|
+
expect(actual).to match_array(desired)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'generates a figcaption' do
|
24
|
+
desired = <<~END_STRING
|
25
|
+
<figcaption class='imgFigCaption '>
|
26
|
+
</figcaption>
|
27
|
+
END_STRING
|
28
|
+
expect(builder.generate_figcaption).to match_ignoring_whitespace(desired)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'generates a picture' do
|
32
|
+
desired = <<~END_DESIRED
|
33
|
+
<picture class='imgPicture'>
|
34
|
+
<source srcset="/assets/images/jekyll.webp" type="image/webp">
|
35
|
+
<source srcset="/assets/images/jekyll.png" type="image/png">
|
36
|
+
<img class="imgImg rounded shadow"
|
37
|
+
src="/assets/images/jekyll.png"
|
38
|
+
style='width: 100%; '
|
39
|
+
/>
|
40
|
+
</picture>
|
41
|
+
END_DESIRED
|
42
|
+
actual = builder.generate_picture
|
43
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
16
44
|
end
|
17
45
|
|
18
46
|
it 'generates a default img' do
|
19
|
-
|
20
|
-
props.src = 'jekyll.webp'
|
21
|
-
builder = described_class.new(props)
|
22
|
-
picture = <<~END_IMG
|
47
|
+
desired = <<~END_IMG
|
23
48
|
<div class='imgWrapper imgFlex' style=' '>
|
24
49
|
<picture class='imgPicture'>
|
25
50
|
<source srcset="/assets/images/jekyll.webp" type="image/webp">
|
@@ -32,15 +57,13 @@ class ImgPropertiesTest
|
|
32
57
|
</div>
|
33
58
|
END_IMG
|
34
59
|
|
35
|
-
|
36
|
-
expect(
|
60
|
+
actual = builder.generate_wrapper
|
61
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
37
62
|
end
|
38
63
|
|
39
|
-
it 'generates an img with size and caption' do
|
40
|
-
props = ImgProperties.new
|
64
|
+
it 'generates an img wrapper with size and caption' do
|
41
65
|
props.caption = 'This is a caption'
|
42
66
|
props.size = '123px'
|
43
|
-
props.src = 'jekyll.webp'
|
44
67
|
builder = described_class.new(props)
|
45
68
|
|
46
69
|
caption = <<~END_CAPTION
|
@@ -49,7 +72,7 @@ class ImgPropertiesTest
|
|
49
72
|
</figcaption>
|
50
73
|
END_CAPTION
|
51
74
|
|
52
|
-
|
75
|
+
desired = <<~END_IMG
|
53
76
|
<div class='imgWrapper imgBlock' style='width: 123px; '>
|
54
77
|
<figure>
|
55
78
|
<picture class='imgPicture'>
|
@@ -67,8 +90,8 @@ class ImgPropertiesTest
|
|
67
90
|
</div>
|
68
91
|
END_IMG
|
69
92
|
|
70
|
-
|
71
|
-
expect(
|
93
|
+
actual = builder.generate_wrapper
|
94
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
72
95
|
end
|
73
96
|
end
|
74
97
|
end
|
data/spec/img_props_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative '../lib/img_props'
|
2
2
|
|
3
|
-
class
|
3
|
+
class ImgPropertiesTest
|
4
4
|
RSpec.describe ImgProperties do
|
5
5
|
it 'detects relative paths' do
|
6
6
|
expect(described_class.local_path?('abcdef')).to be false
|
@@ -37,7 +37,7 @@ class ImgProperitesTest
|
|
37
37
|
|
38
38
|
it 'raises exception if src was not specified' do
|
39
39
|
props = described_class.new
|
40
|
-
expect { props.
|
40
|
+
expect { props.compute_dependant_properties }.to raise_error(Jekyll::ImgError)
|
41
41
|
expect { props.send(:setup_src) }.to raise_error(Jekyll::ImgError)
|
42
42
|
|
43
43
|
props.src = 'relative/path.webp'
|
data/spec/jekyll_img_spec.rb
CHANGED
@@ -56,7 +56,7 @@ class MyTest
|
|
56
56
|
)
|
57
57
|
end
|
58
58
|
|
59
|
-
|
59
|
+
xit 'has no cite or url' do
|
60
60
|
helper.reinitialize('src="./blah.webp"')
|
61
61
|
img = described_class.send(
|
62
62
|
:new,
|
@@ -64,10 +64,11 @@ class MyTest
|
|
64
64
|
helper.markup.dup,
|
65
65
|
parse_context
|
66
66
|
)
|
67
|
-
|
68
|
-
|
67
|
+
actual = img.render_impl
|
68
|
+
desired = <<~END_DESIRED
|
69
69
|
<img src="./blah.webp">
|
70
|
-
|
70
|
+
END_DESIRED
|
71
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
71
72
|
end
|
72
73
|
end
|
73
74
|
end
|
data/spec/source_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'rspec/match_ignoring_whitespace'
|
2
|
+
require_relative '../lib/source'
|
3
|
+
|
4
|
+
# Test ImgProperties
|
5
|
+
class ImgPropertiesTest
|
6
|
+
RSpec.describe Source do
|
7
|
+
Dir.chdir("#{__dir__}/../demo")
|
8
|
+
source_absolute = described_class.new('/assets/images/jekyll_240x103.webp')
|
9
|
+
source_relative = described_class.new('./assets/images/jekyll_240x103.webp')
|
10
|
+
|
11
|
+
it 'globs absolute paths' do
|
12
|
+
expect(source_absolute.send(:globbed_path)).to eq("./assets/images/jekyll_240x103.*")
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'globs relative paths' do
|
16
|
+
expect(source_relative.send(:globbed_path)).to eq("./assets/images/jekyll_240x103.*")
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'sorts files with absolute path' do
|
20
|
+
actual = source_absolute.send(:sorted_files)
|
21
|
+
desired = [
|
22
|
+
"/assets/images/jekyll_240x103.webp",
|
23
|
+
"/assets/images/jekyll_240x103.png",
|
24
|
+
"/assets/images/jekyll_240x103.jpg",
|
25
|
+
"/assets/images/jekyll_240x103.gif",
|
26
|
+
"/assets/images/jekyll_240x103.txt"
|
27
|
+
]
|
28
|
+
expect(actual).to eq(desired)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sorts files with relative path' do
|
32
|
+
actual = source_relative.send(:sorted_files)
|
33
|
+
desired = [
|
34
|
+
"./assets/images/jekyll_240x103.webp",
|
35
|
+
"./assets/images/jekyll_240x103.png",
|
36
|
+
"./assets/images/jekyll_240x103.jpg",
|
37
|
+
"./assets/images/jekyll_240x103.gif",
|
38
|
+
"./assets/images/jekyll_240x103.txt"
|
39
|
+
]
|
40
|
+
expect(actual).to eq(desired)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'generates mimetype for absolute paths' do
|
44
|
+
expect(source_absolute.send(:mimetype, 'blah.png')).to eq('image/png')
|
45
|
+
expect(source_absolute.send(:mimetype, 'blah.svg')).to eq('image/svg')
|
46
|
+
expect(source_absolute.send(:mimetype, 'blah.webp')).to eq('image/webp')
|
47
|
+
expect(source_absolute.send(:mimetype, 'blah.gif')).to eq('image/gif')
|
48
|
+
expect(source_absolute.send(:mimetype, 'blah.blah')).to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generates mimetype for relative paths' do
|
52
|
+
expect(source_relative.send(:mimetype, 'blah.png')).to eq('image/png')
|
53
|
+
expect(source_relative.send(:mimetype, 'blah.svg')).to eq('image/svg')
|
54
|
+
expect(source_relative.send(:mimetype, 'blah.webp')).to eq('image/webp')
|
55
|
+
expect(source_relative.send(:mimetype, 'blah.gif')).to eq('image/gif')
|
56
|
+
expect(source_relative.send(:mimetype, 'blah.blah')).to be_nil
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'generates source fallback for absolute paths' do
|
60
|
+
expect(source_absolute.src_fallback).to eq("/assets/images/jekyll_240x103.png")
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'generates source fallback for relative paths' do
|
64
|
+
expect(source_relative.src_fallback).to eq("./assets/images/jekyll_240x103.png")
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'generates sources for absolute paths' do
|
68
|
+
actual = source_absolute.generate.join("\n")
|
69
|
+
desired = <<~END_DESIRED
|
70
|
+
<source srcset="/assets/images/jekyll_240x103.webp" type="image/webp">
|
71
|
+
<source srcset="/assets/images/jekyll_240x103.png" type="image/png">
|
72
|
+
<source srcset="/assets/images/jekyll_240x103.gif" type="image/gif">
|
73
|
+
END_DESIRED
|
74
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'generates sources for relative paths' do
|
78
|
+
actual = source_relative.generate.join("\n")
|
79
|
+
desired = <<~END_DESIRED
|
80
|
+
<source srcset="./assets/images/jekyll_240x103.webp" type="image/webp">
|
81
|
+
<source srcset="./assets/images/jekyll_240x103.png" type="image/png">
|
82
|
+
<source srcset="./assets/images/jekyll_240x103.gif" type="image/gif">
|
83
|
+
END_DESIRED
|
84
|
+
expect(actual).to match_ignoring_whitespace(desired)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/status_persistence.txt
CHANGED
@@ -3,8 +3,14 @@ example_id | status | run_time |
|
|
3
3
|
./spec/img_builder_spec.rb[1:1] | failed | 14.37 seconds |
|
4
4
|
./spec/img_builder_spec.rb[1:2] | failed | 0.01498 seconds |
|
5
5
|
./spec/img_builder_spec.rb[1:3] | failed | 0.00093 seconds |
|
6
|
-
./spec/img_props_spec.rb[1:1] | passed | 0.
|
7
|
-
./spec/img_props_spec.rb[1:2] | passed | 0.
|
8
|
-
./spec/img_props_spec.rb[1:3] | passed | 0.
|
9
|
-
./spec/img_props_spec.rb[1:4] | passed |
|
10
|
-
./spec/img_props_spec.rb[1:5] | passed |
|
6
|
+
./spec/img_props_spec.rb[1:1] | passed | 0.00393 seconds |
|
7
|
+
./spec/img_props_spec.rb[1:2] | passed | 0.00006 seconds |
|
8
|
+
./spec/img_props_spec.rb[1:3] | passed | 0.00673 seconds |
|
9
|
+
./spec/img_props_spec.rb[1:4] | passed | 4.65 seconds |
|
10
|
+
./spec/img_props_spec.rb[1:5] | passed | 0.00009 seconds |
|
11
|
+
./spec/jekyll_img_spec.rb[1:1] | failed | 40.84 seconds |
|
12
|
+
./spec/source_spec.rb[1:1] | passed | 0.00089 seconds |
|
13
|
+
./spec/source_spec.rb[1:2] | passed | 0.0045 seconds |
|
14
|
+
./spec/source_spec.rb[1:3] | passed | 0.00687 seconds |
|
15
|
+
./spec/source_spec.rb[1:4] | passed | 0.00071 seconds |
|
16
|
+
./spec/source_spec.rb[1:5] | passed | 1.72 seconds |
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jekyll_img
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Slinn
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: jekyll
|
@@ -24,21 +23,34 @@ dependencies:
|
|
24
23
|
- - ">="
|
25
24
|
- !ruby/object:Gem::Version
|
26
25
|
version: 3.5.0
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: jekyll_draft
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 3.0.0
|
27
40
|
- !ruby/object:Gem::Dependency
|
28
41
|
name: jekyll_plugin_support
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
30
43
|
requirements:
|
31
44
|
- - ">="
|
32
45
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
46
|
+
version: 3.0.0
|
34
47
|
type: :runtime
|
35
48
|
prerelease: false
|
36
49
|
version_requirements: !ruby/object:Gem::Requirement
|
37
50
|
requirements:
|
38
51
|
- - ">="
|
39
52
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
41
|
-
description:
|
53
|
+
version: 3.0.0
|
42
54
|
email:
|
43
55
|
- mslinn@mslinn.com
|
44
56
|
executables: []
|
@@ -55,9 +67,11 @@ files:
|
|
55
67
|
- lib/img_props.rb
|
56
68
|
- lib/jekyll_img.rb
|
57
69
|
- lib/jekyll_img/version.rb
|
70
|
+
- lib/source.rb
|
58
71
|
- spec/img_builder_spec.rb
|
59
72
|
- spec/img_props_spec.rb
|
60
73
|
- spec/jekyll_img_spec.rb
|
74
|
+
- spec/source_spec.rb
|
61
75
|
- spec/spec_helper.rb
|
62
76
|
- spec/status_persistence.txt
|
63
77
|
homepage: https://www.mslinn.com/jekyll_plugins/jekyll_img.html
|
@@ -87,14 +101,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
101
|
- !ruby/object:Gem::Version
|
88
102
|
version: '0'
|
89
103
|
requirements: []
|
90
|
-
rubygems_version: 3.
|
91
|
-
signing_key:
|
104
|
+
rubygems_version: 3.6.9
|
92
105
|
specification_version: 4
|
93
106
|
summary: Provides a Jekyll tag that generates images.
|
94
107
|
test_files:
|
95
108
|
- spec/img_builder_spec.rb
|
96
109
|
- spec/img_props_spec.rb
|
97
110
|
- spec/jekyll_img_spec.rb
|
111
|
+
- spec/source_spec.rb
|
98
112
|
- spec/spec_helper.rb
|
99
113
|
- spec/status_persistence.txt
|
100
114
|
...
|