jekyll_picture_tag 1.13.0 → 2.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.envrc +4 -0
- data/.github/workflows/code-checks.yml +33 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +29 -76
- data/.ruby-version +1 -1
- data/docs/.envrc +2 -0
- data/docs/devs/contributing/code.md +14 -4
- data/docs/devs/contributing/docs.md +24 -6
- data/docs/devs/contributing/setup.md +21 -1
- data/docs/devs/contributing/testing.md +19 -37
- data/docs/devs/releases.md +45 -4
- data/docs/index.md +43 -18
- data/docs/logo.png +0 -0
- data/docs/logo.svg +880 -0
- data/docs/users/configuration/disable.md +1 -1
- data/docs/users/configuration/ignore_missing.md +1 -1
- data/docs/users/configuration/kramdown_fix.md +1 -1
- data/docs/users/configuration/suppress_warnings.md +1 -1
- data/docs/users/configuration/urls.md +69 -0
- data/docs/users/deployment.md +49 -0
- data/docs/users/getting_started.md +55 -0
- data/docs/users/installation.md +18 -38
- data/docs/users/liquid_tag/argument_reference/crop.md +21 -36
- data/docs/users/liquid_tag/examples.md +13 -25
- data/docs/users/liquid_tag/index.md +1 -1
- data/docs/users/notes/{migration.md → migration_1.md} +1 -1
- data/docs/users/notes/migration_2.md +99 -0
- data/docs/users/presets/cropping.md +21 -22
- data/docs/users/presets/default.md +11 -2
- data/docs/users/presets/examples.md +77 -45
- data/docs/users/presets/fallback_image.md +1 -1
- data/docs/users/presets/html_attributes.md +1 -1
- data/docs/users/presets/image_formats.md +3 -3
- data/docs/users/presets/image_quality.md +71 -56
- data/docs/users/presets/index.md +19 -45
- data/docs/users/presets/link_source.md +1 -1
- data/docs/users/presets/media_queries.md +1 -1
- data/docs/users/presets/nomarkdown_override.md +1 -1
- data/docs/users/presets/pixel_ratio_srcsets.md +1 -1
- data/docs/users/presets/width_height_attributes.md +1 -1
- data/docs/users/presets/width_srcsets.md +61 -23
- data/docs/users/presets/writing_presets.md +65 -0
- data/docs/users/tutorial.md +97 -0
- data/jekyll_picture_tag.gemspec +38 -23
- data/lib/jekyll_picture_tag.rb +8 -5
- data/lib/jekyll_picture_tag/cache.rb +64 -3
- data/lib/jekyll_picture_tag/defaults/global.rb +18 -0
- data/lib/jekyll_picture_tag/defaults/presets.rb +57 -0
- data/lib/jekyll_picture_tag/images.rb +1 -0
- data/lib/jekyll_picture_tag/images/generated_image.rb +25 -60
- data/lib/jekyll_picture_tag/images/image_file.rb +105 -0
- data/lib/jekyll_picture_tag/images/img_uri.rb +3 -10
- data/lib/jekyll_picture_tag/images/source_image.rb +44 -9
- data/lib/jekyll_picture_tag/instructions.rb +70 -6
- data/lib/jekyll_picture_tag/instructions/children/config.rb +128 -0
- data/lib/jekyll_picture_tag/instructions/children/context.rb +24 -0
- data/lib/jekyll_picture_tag/instructions/children/params.rb +90 -0
- data/lib/jekyll_picture_tag/instructions/children/parsers.rb +48 -0
- data/lib/jekyll_picture_tag/instructions/children/preset.rb +182 -0
- data/lib/jekyll_picture_tag/instructions/parents/conditional_instruction.rb +69 -0
- data/lib/jekyll_picture_tag/instructions/parents/env_instruction.rb +29 -0
- data/lib/jekyll_picture_tag/output_formats/basic.rb +5 -17
- data/lib/jekyll_picture_tag/parsers.rb +6 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/arg_splitter.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/configuration.rb +28 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/html_attributes.rb +1 -1
- data/lib/jekyll_picture_tag/parsers/image_backend.rb +33 -0
- data/lib/jekyll_picture_tag/parsers/preset.rb +43 -0
- data/lib/jekyll_picture_tag/{instructions → parsers}/tag_parser.rb +15 -12
- data/lib/jekyll_picture_tag/router.rb +35 -93
- data/lib/jekyll_picture_tag/srcsets/basic.rb +4 -10
- data/lib/jekyll_picture_tag/utils.rb +10 -20
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +48 -9
- metadata +161 -80
- data/.travis.yml +0 -8
- data/Dockerfile +0 -9
- data/docs/users/configuration/cdn.md +0 -35
- data/docs/users/configuration/relative_urls.md +0 -15
- data/docs/users/notes/input_checking.md +0 -6
- data/jekyll-picture-tag.gemspec +0 -52
- data/lib/jekyll-picture-tag.rb +0 -25
- data/lib/jekyll_picture_tag/cache/base.rb +0 -59
- data/lib/jekyll_picture_tag/cache/generated.rb +0 -20
- data/lib/jekyll_picture_tag/cache/source.rb +0 -19
- data/lib/jekyll_picture_tag/defaults/global.yml +0 -11
- data/lib/jekyll_picture_tag/defaults/presets.yml +0 -11
- data/lib/jekyll_picture_tag/instructions/configuration.rb +0 -121
- data/lib/jekyll_picture_tag/instructions/preset.rb +0 -122
- data/lib/jekyll_picture_tag/instructions/set.rb +0 -75
@@ -5,7 +5,7 @@ module PictureTag
|
|
5
5
|
class SourceImage
|
6
6
|
attr_reader :shortname, :missing, :media_preset
|
7
7
|
|
8
|
-
include MiniMagick
|
8
|
+
# include MiniMagick
|
9
9
|
|
10
10
|
def initialize(relative_filename, media_preset = nil)
|
11
11
|
# /home/dave/my_blog/assets/images/somefolder/myimage.jpg
|
@@ -21,12 +21,38 @@ module PictureTag
|
|
21
21
|
@digest ||= cache[:digest] || ''
|
22
22
|
end
|
23
23
|
|
24
|
+
def crop
|
25
|
+
PictureTag.crop(media_preset)
|
26
|
+
end
|
27
|
+
|
28
|
+
def crop?
|
29
|
+
!crop.nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def keep
|
33
|
+
PictureTag.keep(media_preset)
|
34
|
+
end
|
35
|
+
|
36
|
+
def dimensions
|
37
|
+
[width, height]
|
38
|
+
end
|
39
|
+
|
24
40
|
def width
|
25
|
-
|
41
|
+
return raw_width unless crop?
|
42
|
+
|
43
|
+
[raw_width, (raw_height * cropped_aspect)].min.round
|
26
44
|
end
|
27
45
|
|
28
46
|
def height
|
29
|
-
|
47
|
+
return raw_height unless crop?
|
48
|
+
|
49
|
+
[raw_height, (raw_width / cropped_aspect)].min.round
|
50
|
+
end
|
51
|
+
|
52
|
+
def cropped_aspect
|
53
|
+
return Utils.aspect_float(raw_width, raw_height) unless crop?
|
54
|
+
|
55
|
+
Utils.aspect_float(*crop.split(':').map(&:to_f))
|
30
56
|
end
|
31
57
|
|
32
58
|
# /home/dave/my_blog/assets/images/somefolder/myimage.jpg
|
@@ -44,13 +70,23 @@ module PictureTag
|
|
44
70
|
# /home/dave/my_blog/assets/images/somefolder/myimage.jpg
|
45
71
|
# ^^^
|
46
72
|
def ext
|
47
|
-
@ext ||= File.extname(name)[1
|
73
|
+
@ext ||= File.extname(name)[1..].downcase
|
48
74
|
end
|
49
75
|
|
50
76
|
private
|
51
77
|
|
78
|
+
# pre-crop
|
79
|
+
def raw_width
|
80
|
+
@raw_width ||= @missing ? 999_999 : image.width
|
81
|
+
end
|
82
|
+
|
83
|
+
# pre-crop
|
84
|
+
def raw_height
|
85
|
+
@raw_height ||= @missing ? 999_999 : image.height
|
86
|
+
end
|
87
|
+
|
52
88
|
def cache
|
53
|
-
@cache ||= Cache
|
89
|
+
@cache ||= Cache.new(@shortname)
|
54
90
|
end
|
55
91
|
|
56
92
|
def missing?
|
@@ -75,14 +111,12 @@ module PictureTag
|
|
75
111
|
|
76
112
|
def update_cache
|
77
113
|
cache[:digest] = source_digest
|
78
|
-
cache[:width] = image.width
|
79
|
-
cache[:height] = image.height
|
80
114
|
|
81
115
|
cache.write
|
82
116
|
end
|
83
117
|
|
84
118
|
def image
|
85
|
-
@image ||= Image.
|
119
|
+
@image ||= Vips::Image.new_from_file(name)
|
86
120
|
end
|
87
121
|
|
88
122
|
def source_digest
|
@@ -90,7 +124,8 @@ module PictureTag
|
|
90
124
|
end
|
91
125
|
|
92
126
|
def missing_image_warning
|
93
|
-
"JPT Could not find #{name}.
|
127
|
+
"JPT Could not find #{name}. " \
|
128
|
+
'Your site will have broken images. Continuing.'
|
94
129
|
end
|
95
130
|
|
96
131
|
def missing_image_error
|
@@ -1,6 +1,70 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module PictureTag
|
2
|
+
# Instructions obtain, validate, and typecast/coerce input values. These
|
3
|
+
# inputs are either taken directly from jekyll's inputs, or handled by parsers
|
4
|
+
# first.
|
5
|
+
#
|
6
|
+
# Logic which affects only a single setting belongs in Instructions, while
|
7
|
+
# logic which affects multiple settings belongs in Parsers.
|
8
|
+
#
|
9
|
+
# Since instruction classes are so small, we define several per file in the
|
10
|
+
# instructions directory to save on boilerplate. All fall under the
|
11
|
+
# Instructions module namespace.
|
12
|
+
module Instructions
|
13
|
+
# Generic instruction, meant to be inherited. Children of this class must
|
14
|
+
# override the source method, and likely want to override valid?, coerce,
|
15
|
+
# and error_message as applicable.
|
16
|
+
class Instruction
|
17
|
+
# Memoized value of the given instruction. This is the public API.
|
18
|
+
def value
|
19
|
+
return @value if defined?(@value)
|
20
|
+
|
21
|
+
raise ArgumentError, error_message unless valid?
|
22
|
+
|
23
|
+
@value = coerced
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Source(s) of truth - where does this setting come from? Logic does not
|
29
|
+
# belong here. If information comes from muliple places, return an array
|
30
|
+
# or a hash.
|
31
|
+
def source
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
# Determine whether or not the input(s) are valid.
|
36
|
+
def valid?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Convert input(s) to output.
|
41
|
+
def coerce
|
42
|
+
source
|
43
|
+
end
|
44
|
+
|
45
|
+
# Message returned if validation fails. Override this with something more
|
46
|
+
# helpful.
|
47
|
+
def error_message
|
48
|
+
"JPT - #{setting_name} received an invalid argument: #{source}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def coerced
|
52
|
+
return @coerced if defined?(@coerced)
|
53
|
+
|
54
|
+
@coerced = coerce
|
55
|
+
end
|
56
|
+
|
57
|
+
def setting_name
|
58
|
+
Utils.snakeize(self.class.to_s.split('::').last)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Load Parents
|
65
|
+
Dir[File.dirname(__FILE__) + '/instructions/parents/*.rb']
|
66
|
+
.sort.each { |file| require file }
|
67
|
+
|
68
|
+
# Load children:
|
69
|
+
Dir[File.dirname(__FILE__) + '/instructions/children/*.rb']
|
70
|
+
.sort.each { |file| require file }
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# PictureTag configuration in _config.yml
|
4
|
+
class Pconfig < Instruction
|
5
|
+
def source
|
6
|
+
PictureTag.config['picture']
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# https://example.com/my-base-path/assets/generated-images/image.jpg
|
11
|
+
# ^^^^^^^^^^^^^
|
12
|
+
# | domain | baseurl | directory | filename
|
13
|
+
class Baseurl < Instruction
|
14
|
+
def source
|
15
|
+
{
|
16
|
+
ignore: PictureTag.pconfig['ignore_baseurl'],
|
17
|
+
key: PictureTag.pconfig['baseurl_key']
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def coerce
|
22
|
+
return '' if source[:ignore]
|
23
|
+
|
24
|
+
PictureTag.config[source[:key]] || ''
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Whether to use relative or absolute URLs for images.
|
29
|
+
class RelativeUrl < EnvInstruction
|
30
|
+
def source
|
31
|
+
PictureTag.pconfig['relative_url']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Image source directory
|
36
|
+
class SourceDir < Instruction
|
37
|
+
private
|
38
|
+
|
39
|
+
def source
|
40
|
+
[
|
41
|
+
PictureTag.site.source,
|
42
|
+
PictureTag.pconfig['source']
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
def coerce
|
47
|
+
File.join(*source.map(&:to_s))
|
48
|
+
end
|
49
|
+
|
50
|
+
def setting_name
|
51
|
+
'source directory'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Image output directory
|
56
|
+
class DestDir < Instruction
|
57
|
+
private
|
58
|
+
|
59
|
+
def source
|
60
|
+
[
|
61
|
+
PictureTag.site.config['destination'],
|
62
|
+
PictureTag.pconfig['output']
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
66
|
+
def coerce
|
67
|
+
File.join(*source.map(&:to_s))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Whether to continue if a source image is missing
|
72
|
+
class ContinueOnMissing < EnvInstruction
|
73
|
+
def source
|
74
|
+
PictureTag.pconfig['ignore_missing_images']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Whether to use a CDN
|
79
|
+
class Cdn < EnvInstruction
|
80
|
+
def source
|
81
|
+
{
|
82
|
+
url: PictureTag.pconfig['cdn_url'],
|
83
|
+
setting: PictureTag.pconfig['cdn_environments']
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def coerce
|
88
|
+
source[:url] && super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# CDN URL
|
93
|
+
class CdnUrl < Instruction
|
94
|
+
def source
|
95
|
+
PictureTag.pconfig['cdn_url']
|
96
|
+
end
|
97
|
+
|
98
|
+
def valid?
|
99
|
+
require 'uri'
|
100
|
+
uri = URI(source)
|
101
|
+
|
102
|
+
# If the URI library can't parse it, it's not valid.
|
103
|
+
uri.scheme && uri.host
|
104
|
+
end
|
105
|
+
|
106
|
+
def error_message
|
107
|
+
<<~HEREDOC
|
108
|
+
cdn_url must be a valid URI in the following format: https://example.com/
|
109
|
+
current setting: #{source}
|
110
|
+
HEREDOC
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Disable JPT?
|
115
|
+
class Disabled < EnvInstruction
|
116
|
+
def source
|
117
|
+
PictureTag.pconfig['disabled']
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Fast build?
|
122
|
+
class FastBuild < EnvInstruction
|
123
|
+
def source
|
124
|
+
PictureTag.pconfig['fast_build']
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# Jekyll site info
|
4
|
+
class Site < Instruction
|
5
|
+
def source
|
6
|
+
PictureTag.context.registers[:site]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Current page in jekyll site
|
11
|
+
class Page < Instruction
|
12
|
+
def source
|
13
|
+
PictureTag.context.registers[:page]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Digs into jekyll context, returns current environment
|
18
|
+
class JekyllEnv < Instruction
|
19
|
+
def source
|
20
|
+
PictureTag.context.environments.first['jekyll']['environment']
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# Builds instances of all source images.
|
4
|
+
class SourceImages < Instruction
|
5
|
+
def source
|
6
|
+
{
|
7
|
+
source_names: PictureTag.params.source_names,
|
8
|
+
media_presets: PictureTag.params.media_presets
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def coerce
|
13
|
+
sources = [PictureTag::SourceImage.new(source[:source_names].shift)]
|
14
|
+
|
15
|
+
while source[:source_names].any?
|
16
|
+
sources << PictureTag::SourceImage.new(
|
17
|
+
source[:source_names].shift, source[:media_presets].shift
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
sources
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Which crop to use for a given media query. Can be given either in params
|
26
|
+
# or preset.
|
27
|
+
class Crop < ConditionalInstruction
|
28
|
+
def source
|
29
|
+
super.merge(
|
30
|
+
{ params: PictureTag.params.crop }
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def coerce(media = nil)
|
35
|
+
raise ArgumentError unless valid?
|
36
|
+
|
37
|
+
source[:params][media] || value_hash[media]
|
38
|
+
end
|
39
|
+
|
40
|
+
def setting_basename
|
41
|
+
'crop'
|
42
|
+
end
|
43
|
+
|
44
|
+
def setting_prefix
|
45
|
+
'media'
|
46
|
+
end
|
47
|
+
|
48
|
+
def acceptable_types
|
49
|
+
super + [String]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Which vips interestingness setting to use for a given media query. Can be
|
54
|
+
# given either in params or preset.
|
55
|
+
class Keep < ConditionalInstruction
|
56
|
+
def source
|
57
|
+
super.merge(
|
58
|
+
{ params: PictureTag.params.keep }
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
def coerce(media = nil)
|
63
|
+
raise ArgumentError unless valid?
|
64
|
+
|
65
|
+
lookup[source[:params][media] || super(media)]
|
66
|
+
end
|
67
|
+
|
68
|
+
def lookup
|
69
|
+
{
|
70
|
+
'center' => :centre,
|
71
|
+
'centre' => :centre,
|
72
|
+
'attention' => :attention,
|
73
|
+
'entropy' => :entropy
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def setting_basename
|
78
|
+
'keep'
|
79
|
+
end
|
80
|
+
|
81
|
+
def setting_prefix
|
82
|
+
'media'
|
83
|
+
end
|
84
|
+
|
85
|
+
def acceptable_types
|
86
|
+
super + [String]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# PictureTag configuration
|
4
|
+
class Config < Instruction
|
5
|
+
def source
|
6
|
+
PictureTag::Parsers::Configuration.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
# Tag parameters
|
11
|
+
class Params < Instruction
|
12
|
+
def source
|
13
|
+
PictureTag::Parsers::TagParser.new PictureTag.raw_params
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Currently selected preset
|
18
|
+
class Preset < Instruction
|
19
|
+
def source
|
20
|
+
PictureTag::Parsers::Preset.new PictureTag.params.preset_name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Handles non-image arguments to liquid tag and preset.
|
25
|
+
class HtmlAttributes < Instruction
|
26
|
+
def source
|
27
|
+
PictureTag::Parsers::HTMLAttributeSet.new PictureTag.params.leftovers
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: rename to MediaQueries
|
32
|
+
# Returns user-defined media queries.
|
33
|
+
class MediaPresets < Instruction
|
34
|
+
def source
|
35
|
+
STOCK_MEDIA_QUERIES.merge(
|
36
|
+
PictureTag.site.data.dig('picture', 'media_queries') || {}
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Main job is to determine which backend should handle which image formats.
|
42
|
+
class Backend < Instruction
|
43
|
+
def source
|
44
|
+
PictureTag::Parsers::ImageBackend.new
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|