jekyll_img 0.1.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/.rubocop.yml +41 -0
- data/CHANGELOG.md +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +173 -0
- data/Rakefile +5 -0
- data/jekyll_img.gemspec +42 -0
- data/lib/img_builder.rb +75 -0
- data/lib/img_props.rb +97 -0
- data/lib/jekyll_img/version.rb +3 -0
- data/lib/jekyll_img.rb +41 -0
- data/spec/img_builder_spec.rb +64 -0
- data/spec/img_props_spec.rb +112 -0
- data/spec/jekyll_img_spec.rb +72 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/status_persistence.txt +10 -0
- metadata +184 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b26f4329c93cd70afacb09303d63ac2d437b70b559403c0b4dfb0dbde060794
|
4
|
+
data.tar.gz: 9df636b6567ca51f02314d892a38a885032c8c320809fc4609dc83e82bcaf361
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fed0376534157a78447d4e5db766f9913833d0acddb41fb150d877ba35620f4b55fc3c89f986f695574930df89026ef4b02259f42ff71442cd3b340d595c07ec
|
7
|
+
data.tar.gz: b9ac7072163f1be7d578fe2705b75b5489c6379e5a91e30882263bb62a78dfe53dde295d8842c86e1f87b55016ba827d310af9491eb2b4f7fd84e0c584650de0
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
AllCops:
|
2
|
+
Exclude:
|
3
|
+
- vendor/**/*
|
4
|
+
- exe/**/*
|
5
|
+
- Gemfile*
|
6
|
+
- Rakefile
|
7
|
+
NewCops: enable
|
8
|
+
TargetRubyVersion: 2.6
|
9
|
+
|
10
|
+
Layout/HashAlignment:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Layout/LineLength:
|
14
|
+
Max: 150
|
15
|
+
|
16
|
+
Layout/MultilineMethodCallIndentation:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Metrics/AbcSize:
|
20
|
+
Max: 40
|
21
|
+
|
22
|
+
Metrics/BlockLength:
|
23
|
+
Max: 30
|
24
|
+
Exclude:
|
25
|
+
- jekyll_img.gemspec
|
26
|
+
- spec/img_props_spec.rb
|
27
|
+
|
28
|
+
Metrics/CyclomaticComplexity:
|
29
|
+
Max: 10
|
30
|
+
|
31
|
+
Metrics/MethodLength:
|
32
|
+
Max: 30
|
33
|
+
|
34
|
+
Metrics/PerceivedComplexity:
|
35
|
+
Max: 10
|
36
|
+
|
37
|
+
Style/FrozenStringLiteralComment:
|
38
|
+
Enabled: false
|
39
|
+
|
40
|
+
Style/TrailingCommaInHashLiteral:
|
41
|
+
Enabled: false
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Mike Slinn
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
`jekyll_img`
|
2
|
+
[](https://badge.fury.io/rb/jekyll_img)
|
3
|
+
===========
|
4
|
+
|
5
|
+
`Jekyll_img` is a Jekyll plugin that displays images,
|
6
|
+
and provides support for captions and URLs.
|
7
|
+
|
8
|
+
See [demo/index.html](demo/index.html) for examples.
|
9
|
+
|
10
|
+
Run the demo website by typing:
|
11
|
+
```shell
|
12
|
+
$ demo/_bin/debug -r
|
13
|
+
```
|
14
|
+
... and point your web browser to http://localhost:4444
|
15
|
+
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
{% img [Options] src='path' %}
|
19
|
+
|
20
|
+
`Options` are:
|
21
|
+
|
22
|
+
- `align="left|inline|right|center"` Default value is `inline`
|
23
|
+
- `alt="Alt text"` Default value is the `caption` text, if provided
|
24
|
+
- `caption="A caption"` No default value
|
25
|
+
- `classes="class1 class2 classN"` Extra <img> classes; default is `rounded shadow`
|
26
|
+
- `id="someId"` No default value
|
27
|
+
- `nofollow` Generates `rel='nofollow'`; only applicable when `url` is specified
|
28
|
+
- `size='eighthsize|fullsize|halfsize|initial|quartersize|XXXYY|XXX%'`
|
29
|
+
Defines width of image.
|
30
|
+
- `initial` is the default behavior.
|
31
|
+
- `eighthsize`, `fullsize`, `halfsize`, and `quartersize` are relative to the enclosing tag's width.
|
32
|
+
- CSS units can also be used, for those cases `XXX` is a float and `YY` is `unit` (see below)
|
33
|
+
- `style='css goes here'` CSS style for <img>; no default
|
34
|
+
- `target='none|whatever'` Only applicable when `url` is specified; default value is `_blank`
|
35
|
+
- `title="A title"` Default value is `caption` text, if provided
|
36
|
+
- `url='https://domain.com'` No default value
|
37
|
+
- `wrapper_class='class1 class2 classN'` Extra CSS classes for wrapper <div>; no default value
|
38
|
+
- `wrapper_style='background-color: black;'` CSS style for wrapper <div>; no default value
|
39
|
+
|
40
|
+
[`unit`](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#numbers_lengths_and_percentages) is one of: `Q`, `ch`, `cm`, `em`, `dvh`, `dvw`, `ex`, `in`, `lh`,
|
41
|
+
`lvh`, `lvw`, `mm`, `pc`, `px`, `pt`, `rem`, `rlh`, `svh`, `svw`, `vb`,
|
42
|
+
`vh`, `vi`, `vmax`, `vmin`, or `vw`.
|
43
|
+
|
44
|
+
CSS classes referenced by the `jekyll_img` plugin are at the bottom of [demo/assets/css/style.css](demo/assets/css/style.css). CSS marker classes are included, so CSS selectors can be used for additional styling.
|
45
|
+
|
46
|
+
|
47
|
+
## Design
|
48
|
+
The most significant design issue was the decision that image size and formatting should not change
|
49
|
+
whether it had a caption.
|
50
|
+
HTML captions exist within a `<figure />` element, which also surrounds the image.
|
51
|
+
|
52
|
+
I also wanted to ensure that captions would wrap text under an image,
|
53
|
+
and would not be wider than the image they were associated with.
|
54
|
+
|
55
|
+
CSS behavior differs for `<figure />` and `<img />`.
|
56
|
+
For example, centering, floating right and left.
|
57
|
+
That means the CSS and where it would need to be applied are completely different for
|
58
|
+
naked `<img />` and `<figure />` tags.
|
59
|
+
Handling all possible situations of these two scenarios would significantly raise the complexity of the plugin code. I know, because I went down that rabbit hole.
|
60
|
+
|
61
|
+
|
62
|
+
### Wrapper <div />
|
63
|
+
To make the plugin code more manageable,
|
64
|
+
the plugin always encloses the generated HTML & CSS within a wrapper `<div />`.
|
65
|
+
The wrapper allows for a simple, consistent approach regardless of whether a caption is generated or not.
|
66
|
+
|
67
|
+
The wrapper width is identical to the displayed image width.
|
68
|
+
Within the wrapper `<div />`, the embedded `<img />` is displayed with `width=100%`.
|
69
|
+
If a caption is required, the generated `<figure />` only makes the space taken by the generated HTML longer;
|
70
|
+
the image width and height is not affected.
|
71
|
+
|
72
|
+
The wrapper will not exceed the width of the tag that encloses it if the `size` parameter has values `eighthsize`, `fullsize`, `halfsize`, `initial` or `quartersize`.
|
73
|
+
|
74
|
+
The wrapper's width can be defined independently of its enclosing tag by using [CSS units](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units#numbers_lengths_and_percentages) for the `size` parameter:
|
75
|
+
`Q`, `ch`, `cm`, `em`, `dvh`, `dvw`, `ex`, `in`, `lh`,
|
76
|
+
`lvh`, `lvw`, `mm`, `pc`, `px`, `pt`, `rem`, `rlh`, `svh`, `svw`, `vb`,
|
77
|
+
`vh`, `vi`, `vmax`, `vmin`, or `vw`.
|
78
|
+
Using CSS units means that large enough values could cause the image to exceed the width of its enclosing tag.
|
79
|
+
|
80
|
+
|
81
|
+
## Installation
|
82
|
+
|
83
|
+
Add this line to your Jekyll project's Gemfile, within the `jekyll_plugins` group:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
group :jekyll_plugins do
|
87
|
+
gem 'jekyll_img'
|
88
|
+
end
|
89
|
+
```
|
90
|
+
|
91
|
+
Also add it to `_config.yml`:
|
92
|
+
```yaml
|
93
|
+
plugins:
|
94
|
+
- jekyll_img
|
95
|
+
```
|
96
|
+
|
97
|
+
And then execute:
|
98
|
+
|
99
|
+
$ bundle install
|
100
|
+
|
101
|
+
|
102
|
+
## Additional Information
|
103
|
+
More information is available on
|
104
|
+
[Mike Slinn’s website](https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html).
|
105
|
+
|
106
|
+
|
107
|
+
## Development
|
108
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
109
|
+
|
110
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
111
|
+
|
112
|
+
|
113
|
+
To build and install this gem onto your local machine, run:
|
114
|
+
```shell
|
115
|
+
$ bundle exec rake install
|
116
|
+
jekyll_img 0.1.0 built to pkg/jekyll_img-0.1.0.gem.
|
117
|
+
jekyll_img (0.1.0) installed.
|
118
|
+
```
|
119
|
+
|
120
|
+
Examine the newly built gem:
|
121
|
+
```shell
|
122
|
+
$ gem info jekyll_img
|
123
|
+
|
124
|
+
*** LOCAL GEMS ***
|
125
|
+
|
126
|
+
jekyll_img (0.1.0)
|
127
|
+
Author: Mike Slinn
|
128
|
+
Homepage:
|
129
|
+
https://github.com/mslinn/jekyll_img
|
130
|
+
License: MIT
|
131
|
+
Installed at: /home/mslinn/.gems
|
132
|
+
|
133
|
+
Generates Jekyll logger with colored output.
|
134
|
+
```
|
135
|
+
|
136
|
+
### Testing
|
137
|
+
Examine the output by running:
|
138
|
+
```shell
|
139
|
+
$ demo/_bin/debug -r
|
140
|
+
```
|
141
|
+
... and pointing your web browser to http://localhost:4444/
|
142
|
+
|
143
|
+
### Unit Tests
|
144
|
+
Either run `rspec` from Visual Studio Code's *Run and Debug* environment
|
145
|
+
(<kbd>Ctrl</kbd>-<kbd>shift</kbd>-<kbd>D</kbd>) and view the *Debug Console* output,
|
146
|
+
or run it from the command line:
|
147
|
+
```shell
|
148
|
+
$ rspec
|
149
|
+
```
|
150
|
+
|
151
|
+
### Build and Push to RubyGems
|
152
|
+
To release a new version,
|
153
|
+
1. Update the version number in `version.rb`.
|
154
|
+
2. Commit all changes to git; if you don't the next step might fail with an unexplainable error message.
|
155
|
+
3. Run the following:
|
156
|
+
```shell
|
157
|
+
$ bundle exec rake release
|
158
|
+
```
|
159
|
+
The above creates a git tag for the version, commits the created tag,
|
160
|
+
and pushes the new `.gem` file to [RubyGems.org](https://rubygems.org).
|
161
|
+
|
162
|
+
|
163
|
+
## Contributing
|
164
|
+
|
165
|
+
1. Fork the project
|
166
|
+
2. Create a descriptively named feature branch
|
167
|
+
3. Add your feature
|
168
|
+
4. Submit a pull request
|
169
|
+
|
170
|
+
|
171
|
+
## License
|
172
|
+
|
173
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/jekyll_img.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative 'lib/jekyll_img/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
4
|
+
github = 'https://github.com/mslinn/jekyll_img'
|
5
|
+
|
6
|
+
spec.bindir = 'exe'
|
7
|
+
spec.authors = ['Mike Slinn']
|
8
|
+
spec.email = ['mslinn@mslinn.com']
|
9
|
+
spec.files = Dir['.rubocop.yml', 'LICENSE.*', 'Rakefile', '{lib,spec}/**/*', '*.gemspec', '*.md']
|
10
|
+
spec.homepage = 'https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#quote'
|
11
|
+
spec.license = 'MIT'
|
12
|
+
spec.metadata = {
|
13
|
+
'allowed_push_host' => 'https://rubygems.org',
|
14
|
+
'bug_tracker_uri' => "#{github}/issues",
|
15
|
+
'changelog_uri' => "#{github}/CHANGELOG.md",
|
16
|
+
'homepage_uri' => spec.homepage,
|
17
|
+
'source_code_uri' => github,
|
18
|
+
}
|
19
|
+
spec.name = 'jekyll_img'
|
20
|
+
spec.post_install_message = <<~END_MESSAGE
|
21
|
+
|
22
|
+
Thanks for installing #{spec.name}!
|
23
|
+
|
24
|
+
END_MESSAGE
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
spec.required_ruby_version = '>= 2.6.0'
|
27
|
+
spec.summary = 'Provides a Jekyll filter that creates formatted quotes.'
|
28
|
+
spec.test_files = spec.files.grep %r{^(test|spec|features)/}
|
29
|
+
spec.version = JekyllImgVersion::VERSION
|
30
|
+
|
31
|
+
spec.add_dependency 'jekyll', '>= 3.5.0'
|
32
|
+
spec.add_dependency 'jekyll_plugin_support', '>= 0.3.0'
|
33
|
+
|
34
|
+
# spec.add_development_dependency 'debase'
|
35
|
+
spec.add_development_dependency 'rspec'
|
36
|
+
spec.add_development_dependency 'rspec-match_ignoring_whitespace'
|
37
|
+
spec.add_development_dependency 'rubocop'
|
38
|
+
# spec.add_development_dependency 'rubocop-jekyll'
|
39
|
+
spec.add_development_dependency 'rubocop-rake'
|
40
|
+
spec.add_development_dependency 'rubocop-rspec'
|
41
|
+
spec.add_development_dependency 'ruby-debug-ide'
|
42
|
+
end
|
data/lib/img_builder.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Like RoR's squish method
|
2
|
+
class String
|
3
|
+
def squish
|
4
|
+
strip.gsub(/\s+/, ' ')
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Constructs HTML img tag from properties
|
9
|
+
class ImgBuilder
|
10
|
+
def initialize(props)
|
11
|
+
props.compute_dependant_properties
|
12
|
+
@props = props
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
@props.compute_dependant_properties
|
17
|
+
generate_wrapper
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
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_img}</a>"
|
29
|
+
else
|
30
|
+
generate_img
|
31
|
+
end
|
32
|
+
}
|
33
|
+
#{generate_figure_caption}
|
34
|
+
#{"</figure>\n" if @props.caption}
|
35
|
+
</div>
|
36
|
+
END_HTML
|
37
|
+
result.strip
|
38
|
+
end
|
39
|
+
|
40
|
+
def generate_figure_caption
|
41
|
+
return nil unless @props.caption
|
42
|
+
|
43
|
+
<<~END_CAPTION
|
44
|
+
<figcaption class='imgFigCaption #{@props.attr_size_class}'>
|
45
|
+
#{if @props.url
|
46
|
+
<<~END_URL
|
47
|
+
<a href="#{@props.url}" #{@props.attr_target} #{@props.attr_nofollow}>
|
48
|
+
#{@props.caption}
|
49
|
+
</a>
|
50
|
+
END_URL
|
51
|
+
else
|
52
|
+
@props.caption
|
53
|
+
end
|
54
|
+
}
|
55
|
+
</figcaption>
|
56
|
+
END_CAPTION
|
57
|
+
end
|
58
|
+
|
59
|
+
# See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture
|
60
|
+
def generate_img
|
61
|
+
img_classes = @props.classes || 'rounded shadow'
|
62
|
+
<<~END_IMG
|
63
|
+
<picture#{@props.attr_id} class='imgPicture'>
|
64
|
+
<source srcset="#{@props.src}" type="image/webp">
|
65
|
+
<source srcset="#{@props.src_png}" type="image/png">
|
66
|
+
<img #{@props.attr_alt}
|
67
|
+
class="imgImg #{img_classes.squish}"
|
68
|
+
src="#{@props.src_png}"
|
69
|
+
#{@props.attr_style_img}
|
70
|
+
#{@props.attr_title}
|
71
|
+
/>
|
72
|
+
</picture>
|
73
|
+
END_IMG
|
74
|
+
end
|
75
|
+
end
|
data/lib/img_props.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# Properties from user
|
2
|
+
# All methods are idempotent.
|
3
|
+
# attr_ methods can be called after compute_dependant_properties
|
4
|
+
# All methods except compute_dependant_properties can be called in any order
|
5
|
+
class ImgProperties
|
6
|
+
attr_accessor :align, :alt, :attr_wrapper_align_class, :caption, :classes, :id, :img_display, :nofollow, \
|
7
|
+
:src, :size, :style, :target, :title, :url, :wrapper_class, :wrapper_style
|
8
|
+
|
9
|
+
SIZES = %w[eighthsize fullsize halfsize initial quartersize].freeze
|
10
|
+
|
11
|
+
def attr_alt
|
12
|
+
"alt='#{@alt}'" if @alt
|
13
|
+
end
|
14
|
+
|
15
|
+
def attr_id
|
16
|
+
" id='#{@id}'" if @id
|
17
|
+
end
|
18
|
+
|
19
|
+
# <img> tag assets, except alignment classes (inline, left, center, right)
|
20
|
+
def attr_img_classes
|
21
|
+
@classes || 'rounded shadow'
|
22
|
+
end
|
23
|
+
|
24
|
+
def attr_nofollow
|
25
|
+
" rel='nofollow'" if @nofollow
|
26
|
+
end
|
27
|
+
|
28
|
+
def attr_size_class
|
29
|
+
return nil if @size.nil? || size_unit_specified?
|
30
|
+
|
31
|
+
abort "'#{@size}' is not a recognized size; must be one of #{SIZES.join(', ')}, or an explicit unit." \
|
32
|
+
unless SIZES.include?(@size)
|
33
|
+
@size
|
34
|
+
end
|
35
|
+
|
36
|
+
def attr_style_img
|
37
|
+
"style='width: 100%; #{@style}'".squish
|
38
|
+
end
|
39
|
+
|
40
|
+
def attr_target
|
41
|
+
return nil if @target == 'none'
|
42
|
+
|
43
|
+
target = @target || '_blank'
|
44
|
+
" target='#{target}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
def attr_title
|
48
|
+
"title='#{@title}'" if @title && !@title.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def attr_width_style
|
52
|
+
"width: #{@size};" if size_unit_specified?
|
53
|
+
end
|
54
|
+
|
55
|
+
def compute_dependant_properties
|
56
|
+
setup_src
|
57
|
+
|
58
|
+
@target ||= '_blank'
|
59
|
+
|
60
|
+
@img_display = @caption && @size ? 'imgBlock' : 'imgFlex'
|
61
|
+
|
62
|
+
@alt ||= @caption || @title
|
63
|
+
@title ||= @caption || @alt # rubocop:disable Naming/MemoizedInstanceVariableName
|
64
|
+
end
|
65
|
+
|
66
|
+
def src_png
|
67
|
+
abort 'src parameter was not specified' if @src.to_s.empty?
|
68
|
+
|
69
|
+
@src.gsub('.webp', '.png')
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.local_path?(src)
|
73
|
+
first_char = src[0]
|
74
|
+
first_char.match?(%r{[./]})
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def setup_src
|
80
|
+
@src = @src.to_s.strip
|
81
|
+
abort 'src parameter was not specified' if @src.empty?
|
82
|
+
|
83
|
+
@src = "/assets/images/#{@src}" unless ImgProperties.local_path?(@src) || url?(@src)
|
84
|
+
end
|
85
|
+
|
86
|
+
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
|
87
|
+
|
88
|
+
def size_unit_specified?
|
89
|
+
return false if @size.to_s.strip.empty?
|
90
|
+
|
91
|
+
@size.end_with?(*UNITS)
|
92
|
+
end
|
93
|
+
|
94
|
+
def url?(src)
|
95
|
+
src.start_with? 'http'
|
96
|
+
end
|
97
|
+
end
|
data/lib/jekyll_img.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'jekyll_plugin_support'
|
2
|
+
require 'jekyll_plugin_support_helper'
|
3
|
+
require_relative 'img_builder'
|
4
|
+
require_relative 'img_props'
|
5
|
+
require_relative 'jekyll_img/version'
|
6
|
+
|
7
|
+
# @author Copyright 2023 Michael Slinn
|
8
|
+
# @license SPDX-License-Identifier: Apache-2.0
|
9
|
+
|
10
|
+
module ImgModule
|
11
|
+
PLUGIN_NAME = 'img'.freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
module Jekyll
|
15
|
+
# Plugin implementation
|
16
|
+
class Img < JekyllSupport::JekyllTag
|
17
|
+
def render_impl
|
18
|
+
props = ImgProperties.new
|
19
|
+
props.align = @helper.parameter_specified?('align') || 'inline'
|
20
|
+
props.alt = @helper.parameter_specified? 'alt'
|
21
|
+
props.caption = @helper.parameter_specified? 'caption'
|
22
|
+
props.classes = @helper.parameter_specified? 'class'
|
23
|
+
props.id = @helper.parameter_specified? 'id'
|
24
|
+
props.nofollow = @helper.parameter_specified? 'nofollow'
|
25
|
+
props.size = @helper.parameter_specified?('size') || @helper.parameter_specified?('_size')
|
26
|
+
props.src = @helper.parameter_specified? 'src'
|
27
|
+
props.style = @helper.parameter_specified? 'style'
|
28
|
+
props.target = @helper.parameter_specified? 'target'
|
29
|
+
props.title = @helper.parameter_specified? 'title'
|
30
|
+
props.url = @helper.parameter_specified? 'url'
|
31
|
+
props.wrapper_class = @helper.parameter_specified? 'wrapper_class'
|
32
|
+
props.wrapper_style = @helper.parameter_specified? 'wrapper_style'
|
33
|
+
|
34
|
+
@builder = ImgBuilder.new(props)
|
35
|
+
@builder.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
PluginMetaLogger.instance.info { "Loaded #{ImgModule::PLUGIN_NAME} v0.1.0 plugin." }
|
41
|
+
Liquid::Template.register_tag(ImgModule::PLUGIN_NAME, Jekyll::Img)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rspec/match_ignoring_whitespace'
|
2
|
+
require_relative '../lib/img_builder'
|
3
|
+
require_relative '../lib/img_props'
|
4
|
+
|
5
|
+
# Test ImgProperties
|
6
|
+
class ImgPropertiesTest
|
7
|
+
RSpec.describe ImgBuilder do # rubocop:disable Metrics/BlockLength
|
8
|
+
it 'generates a default img' do
|
9
|
+
props = ImgProperties.new
|
10
|
+
props.src = 'blah.webp'
|
11
|
+
builder = ImgBuilder.new(props)
|
12
|
+
picture = <<~END_IMG
|
13
|
+
<div class='imgWrapper imgFlex' style=' '>
|
14
|
+
<picture class='imgPicture'>
|
15
|
+
<source srcset="/assets/images/blah.webp" type="image/webp">
|
16
|
+
<source srcset="/assets/images/blah.png" type="image/png">
|
17
|
+
<img class="imgImg rounded shadow"
|
18
|
+
src="/assets/images/blah.png"
|
19
|
+
style='width: 100%; '
|
20
|
+
/>
|
21
|
+
</picture>
|
22
|
+
</div>
|
23
|
+
END_IMG
|
24
|
+
|
25
|
+
expect(builder.send(:generate_figure_caption)).to be nil
|
26
|
+
expect(builder.send(:generate_wrapper)).to match_ignoring_whitespace(picture)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'generates an img with size and caption' do
|
30
|
+
props = ImgProperties.new
|
31
|
+
props.caption = 'This is a caption'
|
32
|
+
props.size = '123px'
|
33
|
+
props.src = 'blah.webp'
|
34
|
+
builder = ImgBuilder.new(props)
|
35
|
+
|
36
|
+
caption = <<~END_CAPTION
|
37
|
+
<figcaption class='imgFigCaption '>
|
38
|
+
This is a caption
|
39
|
+
</figcaption>
|
40
|
+
END_CAPTION
|
41
|
+
|
42
|
+
picture = <<~END_IMG
|
43
|
+
<div class='imgWrapper imgBlock' style='width: 123px; '>
|
44
|
+
<figure>
|
45
|
+
<picture class='imgPicture'>
|
46
|
+
<source srcset="/assets/images/blah.webp" type="image/webp">
|
47
|
+
<source srcset="/assets/images/blah.png" type="image/png">
|
48
|
+
<img alt='This is a caption'
|
49
|
+
class="imgImg rounded shadow"
|
50
|
+
src="/assets/images/blah.png"
|
51
|
+
style='width: 100%; '
|
52
|
+
title='This is a caption'
|
53
|
+
/>
|
54
|
+
</picture>
|
55
|
+
#{caption}
|
56
|
+
</figure>
|
57
|
+
</div>
|
58
|
+
END_IMG
|
59
|
+
|
60
|
+
expect(builder.send(:generate_figure_caption)).to match_ignoring_whitespace(caption)
|
61
|
+
expect(builder.send(:generate_wrapper)).to match_ignoring_whitespace(picture)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require_relative '../lib/img_props'
|
2
|
+
|
3
|
+
# Test ImgProperties
|
4
|
+
class ImgProperitesTest
|
5
|
+
RSpec.describe ImgProperties do # rubocop:disable Metrics/BlockLength
|
6
|
+
it 'detects relative paths' do
|
7
|
+
expect(ImgProperties.local_path?('abcdef')).to be false
|
8
|
+
expect(ImgProperties.local_path?('./abc')).to be true
|
9
|
+
expect(ImgProperties.local_path?('/abc')).to be true
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'has other class methods' do
|
13
|
+
props = ImgProperties.new
|
14
|
+
expect(props.send(:size_unit_specified?)).to be false
|
15
|
+
expect(props.send(:url?, 'blah')).to be false
|
16
|
+
expect(props.send(:url?, 'http://blah')).to be true
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not generate attributes for most empty properties' do
|
20
|
+
props = ImgProperties.new
|
21
|
+
expect(props.attr_alt).to be nil
|
22
|
+
expect(props.attr_img_classes).to eq('rounded shadow')
|
23
|
+
expect(props.attr_id).to be nil
|
24
|
+
expect(props.attr_nofollow).to be nil
|
25
|
+
expect(props.attr_size_class).to be nil
|
26
|
+
expect(props.attr_style_img).to eq("style='width: 100%; '")
|
27
|
+
expect(props.attr_target).to eq(" target='_blank'")
|
28
|
+
expect(props.attr_title).to be nil
|
29
|
+
expect(props.attr_width_style).to be nil
|
30
|
+
|
31
|
+
props.compute_dependant_properties
|
32
|
+
expect(props.attr_wrapper_align_class).to eq('inline')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'raises exception if src was not specified' do
|
36
|
+
props = ImgProperties.new
|
37
|
+
expect { props.src_png }.to raise_error(SystemExit)
|
38
|
+
expect { props.send(:setup_src) }.to raise_error(SystemExit)
|
39
|
+
|
40
|
+
props.src = 'relative/path.webp'
|
41
|
+
props.send(:setup_src)
|
42
|
+
expect(props.src).to eq('/assets/images/relative/path.webp')
|
43
|
+
|
44
|
+
props.src = './local/path.webp'
|
45
|
+
props.send(:setup_src)
|
46
|
+
expect(props.src).to eq('./local/path.webp')
|
47
|
+
|
48
|
+
props.src = '/absolute/path.webp'
|
49
|
+
props.send(:setup_src)
|
50
|
+
expect(props.src).to eq('/absolute/path.webp')
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'generates proper simple attributes' do # rubocop:disable Metrics/BlockLength
|
54
|
+
props = ImgProperties.new
|
55
|
+
|
56
|
+
props.alt = 'blah'
|
57
|
+
expect(props.attr_alt).to eq("alt='blah'")
|
58
|
+
|
59
|
+
props.classes = 'blah'
|
60
|
+
expect(props.attr_img_classes).to eq('blah')
|
61
|
+
|
62
|
+
props.id = 'blah'
|
63
|
+
expect(props.attr_id).to eq(" id='blah'")
|
64
|
+
|
65
|
+
props.nofollow = true
|
66
|
+
expect(props.attr_nofollow).to eq(" rel='nofollow'")
|
67
|
+
|
68
|
+
props.size = 'initial'
|
69
|
+
expect(props.attr_size_class).to eq('initial')
|
70
|
+
|
71
|
+
props.size = '100px'
|
72
|
+
expect(props.attr_size_class).to be nil
|
73
|
+
expect(props.attr_style_img).to eq("style='max-width: 100px;'")
|
74
|
+
expect(props.attr_width_style).to eq('width: 100px;')
|
75
|
+
|
76
|
+
props.size = '10%'
|
77
|
+
expect(props.attr_size_class).to be nil
|
78
|
+
expect(props.attr_style_img).to eq("style='max-width: 10%;'")
|
79
|
+
expect(props.attr_width_style).to eq('width: 10%;')
|
80
|
+
|
81
|
+
props.size = 'fullsize'
|
82
|
+
expect(props.attr_size_class).to eq('fullsize')
|
83
|
+
expect(props.attr_width_style).to be nil
|
84
|
+
|
85
|
+
props.style = 'width: 30rem;'
|
86
|
+
expect(props.attr_style_img).to eq("style='width: 30rem;'")
|
87
|
+
|
88
|
+
props.target = 'moon'
|
89
|
+
expect(props.attr_target).to eq(" target='moon'")
|
90
|
+
|
91
|
+
props.title = 'The End'
|
92
|
+
expect(props.attr_title).to eq("title='The End'")
|
93
|
+
|
94
|
+
props.size = '100px'
|
95
|
+
props.caption = 'A caption'
|
96
|
+
expect(props.attr_size_class).to be nil
|
97
|
+
expect(props.attr_width_style).to eq('width: 100px;')
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'generates proper alignment attributes' do
|
101
|
+
props = ImgProperties.new
|
102
|
+
|
103
|
+
props.align = 'inline'
|
104
|
+
props.compute_dependant_properties
|
105
|
+
expect(props.attr_wrapper_align_class).to eq('inline')
|
106
|
+
|
107
|
+
props.align = 'center'
|
108
|
+
props.compute_dependant_properties
|
109
|
+
expect(props.attr_wrapper_align_class).to eq('center')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jekyll'
|
4
|
+
require 'jekyll_plugin_logger'
|
5
|
+
require 'rspec/match_ignoring_whitespace'
|
6
|
+
require_relative '../lib/jekyll_img'
|
7
|
+
|
8
|
+
Registers = Struct.new(:page, :site)
|
9
|
+
|
10
|
+
# Mock for Collections
|
11
|
+
class Collections
|
12
|
+
def values
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Mock for Site
|
18
|
+
class SiteMock
|
19
|
+
attr_reader :config
|
20
|
+
|
21
|
+
def collections
|
22
|
+
Collections.new
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Mock for Liquid::ParseContent
|
27
|
+
class TestParseContext < Liquid::ParseContext
|
28
|
+
attr_reader :line_number, :registers
|
29
|
+
|
30
|
+
def initialize
|
31
|
+
super
|
32
|
+
@line_number = 123
|
33
|
+
|
34
|
+
@registers = Registers.new(
|
35
|
+
{ },
|
36
|
+
SiteMock.new
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# These tests all fail because I have not figured out how to provide a Jekyll block body to a test
|
42
|
+
class MyTest
|
43
|
+
RSpec.describe Jekyll::Img do
|
44
|
+
let(:logger) do
|
45
|
+
PluginMetaLogger.instance.new_logger(self, PluginMetaLogger.instance.config)
|
46
|
+
end
|
47
|
+
|
48
|
+
let(:parse_context) { TestParseContext.new }
|
49
|
+
|
50
|
+
let(:helper) do
|
51
|
+
JekyllPluginHelper.new(
|
52
|
+
'img',
|
53
|
+
'src="./blah.webp"',
|
54
|
+
logger
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'has no cite or url' do
|
59
|
+
helper.reinitialize('src="./blah.webp"')
|
60
|
+
quote = Jekyll::Img.send(
|
61
|
+
:new,
|
62
|
+
'img',
|
63
|
+
helper.markup.dup,
|
64
|
+
parse_context
|
65
|
+
)
|
66
|
+
result = quote.send(:render_impl, helper.markup)
|
67
|
+
expect(result).to match_ignoring_whitespace <<-END_RESULT
|
68
|
+
<img src="./blah.webp">
|
69
|
+
END_RESULT
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'jekyll'
|
2
|
+
require_relative '../lib/jekyll_img'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.filter_run :focus
|
6
|
+
config.order = 'random'
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
|
9
|
+
# See https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
|
10
|
+
config.example_status_persistence_file_path = 'spec/status_persistence.txt'
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
example_id | status | run_time |
|
2
|
+
------------------------------- | ------ | --------------- |
|
3
|
+
./spec/img_builder_spec.rb[1:1] | passed | 0.00073 seconds |
|
4
|
+
./spec/img_props_spec.rb[1:1] | passed | 0.0001 seconds |
|
5
|
+
./spec/img_props_spec.rb[1:2] | passed | 0.00008 seconds |
|
6
|
+
./spec/img_props_spec.rb[1:3] | failed | 0.00051 seconds |
|
7
|
+
./spec/img_props_spec.rb[1:4] | passed | 0.0011 seconds |
|
8
|
+
./spec/img_props_spec.rb[1:5] | passed | 0.00019 seconds |
|
9
|
+
./spec/img_props_spec.rb[1:6] | failed | 0.00008 seconds |
|
10
|
+
./spec/jekyll_img_spec.rb[1:1] | failed | 0.00037 seconds |
|
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll_img
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mike Slinn
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jekyll
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.5.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.5.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jekyll_plugin_support
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.3.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.3.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-match_ignoring_whitespace
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: ruby-debug-ide
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description:
|
126
|
+
email:
|
127
|
+
- mslinn@mslinn.com
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- ".rubocop.yml"
|
133
|
+
- CHANGELOG.md
|
134
|
+
- LICENSE.txt
|
135
|
+
- README.md
|
136
|
+
- Rakefile
|
137
|
+
- jekyll_img.gemspec
|
138
|
+
- lib/img_builder.rb
|
139
|
+
- lib/img_props.rb
|
140
|
+
- lib/jekyll_img.rb
|
141
|
+
- lib/jekyll_img/version.rb
|
142
|
+
- spec/img_builder_spec.rb
|
143
|
+
- spec/img_props_spec.rb
|
144
|
+
- spec/jekyll_img_spec.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
- spec/status_persistence.txt
|
147
|
+
homepage: https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#quote
|
148
|
+
licenses:
|
149
|
+
- MIT
|
150
|
+
metadata:
|
151
|
+
allowed_push_host: https://rubygems.org
|
152
|
+
bug_tracker_uri: https://github.com/mslinn/jekyll_img/issues
|
153
|
+
changelog_uri: https://github.com/mslinn/jekyll_img/CHANGELOG.md
|
154
|
+
homepage_uri: https://www.mslinn.com/blog/2020/10/03/jekyll-plugins.html#quote
|
155
|
+
source_code_uri: https://github.com/mslinn/jekyll_img
|
156
|
+
post_install_message: |2+
|
157
|
+
|
158
|
+
Thanks for installing jekyll_img!
|
159
|
+
|
160
|
+
rdoc_options: []
|
161
|
+
require_paths:
|
162
|
+
- lib
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
164
|
+
requirements:
|
165
|
+
- - ">="
|
166
|
+
- !ruby/object:Gem::Version
|
167
|
+
version: 2.6.0
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
169
|
+
requirements:
|
170
|
+
- - ">="
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '0'
|
173
|
+
requirements: []
|
174
|
+
rubygems_version: 3.3.3
|
175
|
+
signing_key:
|
176
|
+
specification_version: 4
|
177
|
+
summary: Provides a Jekyll filter that creates formatted quotes.
|
178
|
+
test_files:
|
179
|
+
- spec/img_builder_spec.rb
|
180
|
+
- spec/img_props_spec.rb
|
181
|
+
- spec/jekyll_img_spec.rb
|
182
|
+
- spec/spec_helper.rb
|
183
|
+
- spec/status_persistence.txt
|
184
|
+
...
|