jekyll_picture_tag 1.14.0 → 2.0.0pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +2 -0
  3. data/.github/workflows/code-checks.yml +2 -12
  4. data/.rubocop.yml +2 -0
  5. data/.ruby-version +1 -1
  6. data/docs/devs/contributing/code.md +11 -3
  7. data/docs/devs/contributing/testing.md +0 -11
  8. data/docs/devs/releases.md +20 -0
  9. data/docs/index.md +32 -17
  10. data/docs/logo.png +0 -0
  11. data/docs/logo.svg +880 -0
  12. data/docs/users/getting_started.md +55 -0
  13. data/docs/users/installation.md +17 -38
  14. data/docs/users/liquid_tag/argument_reference/crop.md +21 -36
  15. data/docs/users/liquid_tag/examples.md +13 -25
  16. data/docs/users/liquid_tag/index.md +1 -1
  17. data/docs/users/notes/{migration.md → migration_1.md} +1 -1
  18. data/docs/users/notes/migration_2.md +99 -0
  19. data/docs/users/presets/cropping.md +21 -22
  20. data/docs/users/presets/default.md +11 -3
  21. data/docs/users/presets/examples.md +77 -45
  22. data/docs/users/presets/fallback_image.md +1 -1
  23. data/docs/users/presets/html_attributes.md +1 -1
  24. data/docs/users/presets/image_formats.md +3 -3
  25. data/docs/users/presets/image_quality.md +70 -55
  26. data/docs/users/presets/index.md +78 -42
  27. data/docs/users/presets/link_source.md +1 -1
  28. data/docs/users/presets/media_queries.md +1 -1
  29. data/docs/users/presets/nomarkdown_override.md +1 -1
  30. data/docs/users/presets/pixel_ratio_srcsets.md +1 -1
  31. data/docs/users/presets/width_height_attributes.md +1 -1
  32. data/docs/users/presets/width_srcsets.md +61 -23
  33. data/docs/users/tutorial.md +97 -0
  34. data/jekyll_picture_tag.gemspec +33 -23
  35. data/lib/jekyll_picture_tag.rb +8 -6
  36. data/lib/jekyll_picture_tag/cache.rb +64 -3
  37. data/lib/jekyll_picture_tag/defaults/global.rb +18 -0
  38. data/lib/jekyll_picture_tag/defaults/presets.rb +57 -0
  39. data/lib/jekyll_picture_tag/images.rb +1 -0
  40. data/lib/jekyll_picture_tag/images/generated_image.rb +25 -63
  41. data/lib/jekyll_picture_tag/images/image_file.rb +90 -0
  42. data/lib/jekyll_picture_tag/images/img_uri.rb +3 -12
  43. data/lib/jekyll_picture_tag/images/source_image.rb +44 -9
  44. data/lib/jekyll_picture_tag/instructions.rb +70 -6
  45. data/lib/jekyll_picture_tag/instructions/children/config.rb +128 -0
  46. data/lib/jekyll_picture_tag/instructions/children/context.rb +24 -0
  47. data/lib/jekyll_picture_tag/instructions/children/params.rb +90 -0
  48. data/lib/jekyll_picture_tag/instructions/children/parsers.rb +41 -0
  49. data/lib/jekyll_picture_tag/instructions/children/preset.rb +182 -0
  50. data/lib/jekyll_picture_tag/instructions/parents/conditional_instruction.rb +69 -0
  51. data/lib/jekyll_picture_tag/instructions/parents/env_instruction.rb +29 -0
  52. data/lib/jekyll_picture_tag/output_formats/basic.rb +5 -17
  53. data/lib/jekyll_picture_tag/parsers.rb +5 -0
  54. data/lib/jekyll_picture_tag/{instructions → parsers}/arg_splitter.rb +1 -1
  55. data/lib/jekyll_picture_tag/parsers/configuration.rb +28 -0
  56. data/lib/jekyll_picture_tag/{instructions → parsers}/html_attributes.rb +1 -1
  57. data/lib/jekyll_picture_tag/parsers/preset.rb +43 -0
  58. data/lib/jekyll_picture_tag/{instructions → parsers}/tag_parser.rb +15 -12
  59. data/lib/jekyll_picture_tag/router.rb +35 -93
  60. data/lib/jekyll_picture_tag/srcsets/basic.rb +4 -10
  61. data/lib/jekyll_picture_tag/utils.rb +10 -20
  62. data/lib/jekyll_picture_tag/version.rb +1 -1
  63. data/readme.md +2 -0
  64. metadata +124 -106
  65. data/Dockerfile +0 -9
  66. data/docs/users/notes/input_checking.md +0 -6
  67. data/docs/users/presets/strip_metadata.md +0 -13
  68. data/install_imagemagick.sh +0 -23
  69. data/jekyll-picture-tag.gemspec +0 -52
  70. data/lib/jekyll-picture-tag.rb +0 -25
  71. data/lib/jekyll_picture_tag/cache/base.rb +0 -61
  72. data/lib/jekyll_picture_tag/cache/generated.rb +0 -20
  73. data/lib/jekyll_picture_tag/cache/source.rb +0 -19
  74. data/lib/jekyll_picture_tag/defaults/global.yml +0 -13
  75. data/lib/jekyll_picture_tag/defaults/presets.yml +0 -12
  76. data/lib/jekyll_picture_tag/instructions/configuration.rb +0 -121
  77. data/lib/jekyll_picture_tag/instructions/preset.rb +0 -122
  78. data/lib/jekyll_picture_tag/instructions/set.rb +0 -75
@@ -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,41 @@
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
+ end
41
+ end
@@ -0,0 +1,182 @@
1
+ require 'mime-types'
2
+
3
+ module PictureTag
4
+ module Instructions
5
+ # Returns an instance of the correct markup format's corresponding class
6
+ class OutputClass < Instruction
7
+ private
8
+
9
+ def source
10
+ PictureTag.preset['markup']
11
+ end
12
+
13
+ def coerce
14
+ Object.const_get(class_name)
15
+ end
16
+
17
+ def class_name
18
+ 'PictureTag::OutputFormats::' + Utils.titleize(source)
19
+ end
20
+
21
+ def valid?
22
+ source.is_a?(String) &&
23
+ !source.include?(' ') &&
24
+ Object.const_defined?(class_name)
25
+ end
26
+ end
27
+
28
+ # Image formats
29
+ class Formats < Instruction
30
+ private
31
+
32
+ def source
33
+ PictureTag.preset['formats']
34
+ end
35
+
36
+ def coerce
37
+ [source].flatten
38
+ end
39
+
40
+ def valid?
41
+ coerced.all? do |format|
42
+ types = MIME::Types.type_for(format)
43
+ format == 'original' ||
44
+ types.length == 1 && types.first.media_type == 'image'
45
+ end
46
+ end
47
+ end
48
+
49
+ # Fallback image format
50
+ class FallbackFormat < Instruction
51
+ private
52
+
53
+ def source
54
+ PictureTag.preset['fallback_format']
55
+ end
56
+
57
+ def valid?
58
+ types = MIME::Types.type_for(coerced)
59
+ coerced == 'original' ||
60
+ types.length == 1 && types.first.media_type == 'image'
61
+ end
62
+ end
63
+
64
+ # Fallback Image width
65
+ class FallbackWidth < Instruction
66
+ private
67
+
68
+ def source
69
+ PictureTag.preset['fallback_width']
70
+ end
71
+
72
+ def valid?
73
+ source.is_a? Integer
74
+ end
75
+
76
+ def error_message
77
+ <<~HEREDOC
78
+ fallback_width for preset '#{PictureTag.preset.name}' is invalid. It
79
+ should be a positive integer. You can use underscores as separators:
80
+ 1200
81
+ 1_200
82
+ HEREDOC
83
+ end
84
+ end
85
+
86
+ # Whether to add a {::nomarkdown} wrapper
87
+ class Nomarkdown < Instruction
88
+ private
89
+
90
+ def source
91
+ {
92
+ config: PictureTag.pconfig['nomarkdown'],
93
+ md_page: Utils.markdown_page?,
94
+ preset: PictureTag.preset['nomarkdown']
95
+ }
96
+ end
97
+
98
+ def valid?
99
+ source.fetch_values(:preset, :config).all? do |setting|
100
+ [true, false, nil].include? setting
101
+ end
102
+ end
103
+
104
+ def coerce
105
+ return source[:preset] unless source[:preset].nil?
106
+
107
+ source[:md_page] && source[:config]
108
+ end
109
+ end
110
+
111
+ # Returns widths for a given media query.
112
+ class Widths < ConditionalInstruction
113
+ private
114
+
115
+ def setting_name
116
+ 'widths'
117
+ end
118
+
119
+ def setting_prefix
120
+ 'media'
121
+ end
122
+
123
+ def acceptable_types
124
+ super + [Array]
125
+ end
126
+
127
+ def valid_hash?
128
+ hash = source[:hash]
129
+ return true if hash.nil?
130
+
131
+ hash.is_a?(Hash) && valid_hash_keys?(hash) && valid_hash_values?(hash)
132
+ end
133
+
134
+ def valid_hash_keys?(hash)
135
+ hash.keys.all? { |k| k.is_a? String }
136
+ end
137
+
138
+ def valid_hash_values?(hash)
139
+ hash.values.all? do |val|
140
+ val.is_a?(Array) && val.all? { |subval| subval.is_a? Integer }
141
+ end
142
+ end
143
+ end
144
+
145
+ # Returns quality for a given width.
146
+ class Quality < ConditionalInstruction
147
+ private
148
+
149
+ def setting_name
150
+ 'quality'
151
+ end
152
+
153
+ def setting_prefix
154
+ 'format'
155
+ end
156
+
157
+ def acceptable_types
158
+ super + [Integer, Hash]
159
+ end
160
+
161
+ def coerce(format = nil, width = nil)
162
+ setting = super(format)
163
+
164
+ return setting unless setting.is_a? Hash
165
+
166
+ parse_quality_hash(setting, width)
167
+ end
168
+
169
+ # Works out linearly interpolated quality settings.
170
+ def parse_quality_hash(points, width)
171
+ # The points can be given in any order.
172
+ low, high = *points.keys.map(&:to_i).sort
173
+
174
+ case width
175
+ when 0..low then points[low]
176
+ when low..high then Utils.interpolate(points.keys, points.values, width)
177
+ when high..999_999 then points[high]
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end