jekyll_picture_tag 1.9.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.travis.yml +4 -7
- data/Dockerfile +9 -0
- data/docs/Gemfile +4 -2
- data/docs/Gemfile.lock +186 -89
- data/docs/_config.yml +6 -10
- data/docs/devs/contributing/code.md +44 -0
- data/docs/devs/contributing/docs.md +13 -0
- data/docs/devs/contributing/index.md +15 -0
- data/docs/devs/contributing/setup.md +13 -0
- data/docs/devs/contributing/testing.md +41 -0
- data/docs/devs/index.md +7 -0
- data/docs/{releases.md → devs/releases.md} +35 -13
- data/docs/index.md +58 -36
- data/docs/users/configuration/cdn.md +35 -0
- data/docs/users/configuration/directories.md +34 -0
- data/docs/users/configuration/disable.md +24 -0
- data/docs/users/configuration/fast_build.md +28 -0
- data/docs/users/configuration/ignore_missing.md +23 -0
- data/docs/users/configuration/index.md +29 -0
- data/docs/users/configuration/kramdown_fix.md +20 -0
- data/docs/users/configuration/relative_urls.md +15 -0
- data/docs/users/configuration/suppress_warnings.md +16 -0
- data/docs/users/index.md +7 -0
- data/docs/users/installation.md +52 -0
- data/docs/users/liquid_tag/argument_reference/alternate_images.md +18 -0
- data/docs/users/liquid_tag/argument_reference/attributes.md +42 -0
- data/docs/users/liquid_tag/argument_reference/base_image.md +12 -0
- data/docs/users/liquid_tag/argument_reference/crop.md +48 -0
- data/docs/users/liquid_tag/argument_reference/link.md +16 -0
- data/docs/users/liquid_tag/argument_reference/preset.md +17 -0
- data/docs/users/liquid_tag/argument_reference/readme.md +9 -0
- data/docs/users/liquid_tag/examples.md +81 -0
- data/docs/users/liquid_tag/index.md +31 -0
- data/docs/users/notes/git_lfs.md +7 -0
- data/docs/users/notes/github_pages.md +5 -0
- data/docs/users/notes/html_attributes.md +5 -0
- data/docs/users/notes/index.md +6 -0
- data/docs/users/notes/input_checking.md +6 -0
- data/docs/users/notes/kramdown_bug.md +41 -0
- data/docs/users/notes/managing_images.md +21 -0
- data/docs/{migration.md → users/notes/migration.md} +0 -0
- data/docs/users/presets/cropping.md +61 -0
- data/docs/users/presets/default.md +23 -0
- data/docs/users/presets/examples.md +79 -0
- data/docs/users/presets/fallback_image.md +28 -0
- data/docs/users/presets/html_attributes.md +26 -0
- data/docs/users/presets/image_formats.md +21 -0
- data/docs/users/presets/image_quality.md +43 -0
- data/docs/users/presets/index.md +101 -0
- data/docs/users/presets/link_source.md +16 -0
- data/docs/users/presets/markup_formats/fragments.md +48 -0
- data/docs/users/presets/markup_formats/javascript_friendly.md +57 -0
- data/docs/users/presets/markup_formats/readme.md +43 -0
- data/docs/users/presets/markup_formats/standard_html.md +25 -0
- data/docs/users/presets/media_queries.md +36 -0
- data/docs/users/presets/nomarkdown_override.md +17 -0
- data/docs/users/presets/pixel_ratio_srcsets.md +32 -0
- data/docs/users/presets/width_height_attributes.md +34 -0
- data/docs/users/presets/width_srcsets.md +85 -0
- data/jekyll_picture_tag.gemspec +1 -1
- data/lib/jekyll_picture_tag.rb +1 -0
- data/lib/jekyll_picture_tag/cache.rb +3 -0
- data/lib/jekyll_picture_tag/cache/base.rb +59 -0
- data/lib/jekyll_picture_tag/cache/generated.rb +20 -0
- data/lib/jekyll_picture_tag/cache/source.rb +19 -0
- data/lib/jekyll_picture_tag/defaults/presets.yml +2 -0
- data/lib/jekyll_picture_tag/generated_image.rb +52 -41
- data/lib/jekyll_picture_tag/img_uri.rb +1 -0
- data/lib/jekyll_picture_tag/instructions.rb +1 -0
- data/lib/jekyll_picture_tag/instructions/arg_splitter.rb +69 -0
- data/lib/jekyll_picture_tag/instructions/configuration.rb +1 -1
- data/lib/jekyll_picture_tag/instructions/preset.rb +40 -15
- data/lib/jekyll_picture_tag/instructions/set.rb +23 -9
- data/lib/jekyll_picture_tag/instructions/tag_parser.rb +59 -69
- data/lib/jekyll_picture_tag/output_formats/basic.rb +34 -15
- data/lib/jekyll_picture_tag/output_formats/img.rb +11 -0
- data/lib/jekyll_picture_tag/output_formats/picture.rb +22 -0
- data/lib/jekyll_picture_tag/router.rb +9 -0
- data/lib/jekyll_picture_tag/source_image.rb +60 -44
- data/lib/jekyll_picture_tag/srcsets/basic.rb +29 -7
- data/lib/jekyll_picture_tag/utils.rb +18 -0
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +40 -16
- metadata +67 -20
- data/docs/_layouts/directory.html +0 -32
- data/docs/assets/style.css +0 -31
- data/docs/contributing.md +0 -75
- data/docs/example_presets.md +0 -116
- data/docs/global_configuration.md +0 -173
- data/docs/installation.md +0 -30
- data/docs/notes.md +0 -91
- data/docs/output.md +0 -63
- data/docs/presets.md +0 -309
- data/docs/usage.md +0 -113
@@ -0,0 +1,20 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Cache
|
3
|
+
# Caches generated image details, so we can skip expensive operations whenever
|
4
|
+
# possible.
|
5
|
+
# Stored width and height are values for the source image, after cropping.
|
6
|
+
class Generated
|
7
|
+
include Base
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def cache_dir
|
12
|
+
'generated'
|
13
|
+
end
|
14
|
+
|
15
|
+
def template
|
16
|
+
{ width: nil, height: nil }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Cache
|
3
|
+
# Caches source image details, so we can skip expensive operations whenever
|
4
|
+
# possible.
|
5
|
+
class Source
|
6
|
+
include Base
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def template
|
11
|
+
{ digest: nil, width: nil, height: nil }
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_dir
|
15
|
+
'source'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -4,12 +4,15 @@ module PictureTag
|
|
4
4
|
# Represents a generated image file.
|
5
5
|
class GeneratedImage
|
6
6
|
attr_reader :width, :format
|
7
|
+
|
7
8
|
include MiniMagick
|
8
9
|
|
9
|
-
def initialize(source_file:, width:, format:)
|
10
|
+
def initialize(source_file:, width:, format:, crop: nil, gravity: '')
|
10
11
|
@source = source_file
|
11
12
|
@width = width
|
12
13
|
@format = process_format format
|
14
|
+
@crop = crop
|
15
|
+
@gravity = gravity
|
13
16
|
end
|
14
17
|
|
15
18
|
def exists?
|
@@ -29,7 +32,7 @@ module PictureTag
|
|
29
32
|
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-123abc.jpg
|
30
33
|
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
31
34
|
def name
|
32
|
-
|
35
|
+
@name ||= "#{@source.base_name}-#{@width}-#{id}.#{@format}"
|
33
36
|
end
|
34
37
|
|
35
38
|
# https://example.com/assets/images/myimage-100-123abc.jpg
|
@@ -38,54 +41,64 @@ module PictureTag
|
|
38
41
|
ImgURI.new(name).to_s
|
39
42
|
end
|
40
43
|
|
41
|
-
|
44
|
+
# Post crop
|
45
|
+
def source_width
|
46
|
+
update_cache unless cache[:width]
|
42
47
|
|
43
|
-
|
44
|
-
# ^^^^^^^^^^^^^^^^^^^^^^^
|
45
|
-
def name_left
|
46
|
-
"#{@source.base_name}-#{@width}-"
|
48
|
+
cache[:width]
|
47
49
|
end
|
48
50
|
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
guess_digest if !@source.digest_guess && PictureTag.fast_build?
|
51
|
+
# Post crop
|
52
|
+
def source_height
|
53
|
+
update_cache unless cache[:height]
|
53
54
|
|
54
|
-
|
55
|
+
cache[:height]
|
55
56
|
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
private
|
59
|
+
|
60
|
+
# We exclude width and format from the cache name, since it isn't specific to them.
|
61
|
+
def cache
|
62
|
+
@cache ||= Cache::Generated.new("#{@source.base_name}-#{id}")
|
61
63
|
end
|
62
64
|
|
63
|
-
|
64
|
-
|
65
|
-
# source file, and if it exists we assume it's the right one.
|
66
|
-
def guess_digest
|
67
|
-
matches = dest_glob
|
68
|
-
return unless matches.length == 1
|
65
|
+
def update_cache
|
66
|
+
return if @source.missing
|
69
67
|
|
70
|
-
#
|
71
|
-
|
72
|
-
start = finish - 6
|
68
|
+
# Ensure it's generated:
|
69
|
+
image
|
73
70
|
|
74
|
-
|
75
|
-
|
71
|
+
cache[:width] = @source_dimensions[:width]
|
72
|
+
cache[:height] = @source_dimensions[:height]
|
73
|
+
|
74
|
+
cache.write
|
76
75
|
end
|
77
76
|
|
78
|
-
#
|
79
|
-
|
80
|
-
|
77
|
+
# Hash all inputs and truncate, so we know when they change without getting too long.
|
78
|
+
# /home/dave/my_blog/_site/generated/somefolder/myimage-100-1234abcde.jpg
|
79
|
+
# ^^^^^^^^^
|
80
|
+
def id
|
81
|
+
@id ||= Digest::MD5.hexdigest([@source.digest, @crop, @gravity, quality].join)[0..8]
|
81
82
|
end
|
82
83
|
|
84
|
+
# Post crop, before resizing and reformatting
|
83
85
|
def image
|
84
|
-
@image ||=
|
86
|
+
@image ||= open_image
|
85
87
|
end
|
86
88
|
|
87
|
-
def
|
88
|
-
|
89
|
+
def open_image
|
90
|
+
image_base = Image.open(@source.name)
|
91
|
+
image_base.combine_options do |i|
|
92
|
+
i.auto_orient
|
93
|
+
if @crop
|
94
|
+
i.gravity @gravity
|
95
|
+
i.crop @crop
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
@source_dimensions = { width: image_base.width, height: image_base.height }
|
100
|
+
|
101
|
+
image_base
|
89
102
|
end
|
90
103
|
|
91
104
|
def generate_image
|
@@ -94,30 +107,28 @@ module PictureTag
|
|
94
107
|
write_image
|
95
108
|
end
|
96
109
|
|
110
|
+
def quality
|
111
|
+
PictureTag.quality(@format)
|
112
|
+
end
|
113
|
+
|
97
114
|
def process_image
|
98
115
|
image.combine_options do |i|
|
99
116
|
i.resize "#{@width}x"
|
100
|
-
i.auto_orient
|
101
117
|
i.strip
|
102
118
|
end
|
103
119
|
|
104
120
|
image.format @format
|
105
|
-
image.quality
|
121
|
+
image.quality quality
|
106
122
|
end
|
107
123
|
|
108
124
|
def write_image
|
109
|
-
|
125
|
+
FileUtils.mkdir_p(File.dirname(absolute_filename))
|
110
126
|
|
111
127
|
image.write absolute_filename
|
112
128
|
|
113
129
|
FileUtils.chmod(0o644, absolute_filename)
|
114
130
|
end
|
115
131
|
|
116
|
-
# Make sure destination directory exists
|
117
|
-
def check_dest_dir
|
118
|
-
FileUtils.mkdir_p(dest_dir) unless Dir.exist?(dest_dir)
|
119
|
-
end
|
120
|
-
|
121
132
|
def process_format(format)
|
122
133
|
if format.casecmp('original').zero?
|
123
134
|
@source.ext
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# This class takes in the arguments passed to the liquid tag, and splits it
|
4
|
+
# up into 'words' (correctly handling quotes and backslash escapes.)
|
5
|
+
#
|
6
|
+
# To handle quotes and backslash escaping, we have to parse the string by
|
7
|
+
# characters to break it up correctly. I'm sure there's a library to do
|
8
|
+
# this, but it's not that much code honestly. If this starts getting big,
|
9
|
+
# we'll pull in a new dependency.
|
10
|
+
#
|
11
|
+
class ArgSplitter
|
12
|
+
attr_reader :words
|
13
|
+
|
14
|
+
def initialize(raw_params)
|
15
|
+
@words = []
|
16
|
+
@word = ''
|
17
|
+
@in_quotes = false
|
18
|
+
@escaped = false
|
19
|
+
|
20
|
+
raw_params.each_char { |c| handle_char(c) }
|
21
|
+
|
22
|
+
add_word # We have to explicitly add the last one.
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def handle_char(char)
|
28
|
+
# last character was a backslash:
|
29
|
+
if @escaped
|
30
|
+
close_escape char
|
31
|
+
|
32
|
+
# char is a backslash or a quote:
|
33
|
+
elsif char.match?(/["\\]/)
|
34
|
+
handle_special char
|
35
|
+
|
36
|
+
# Character isn't whitespace, or it's inside double quotes:
|
37
|
+
elsif @in_quotes || char.match(/\S/)
|
38
|
+
@word << char
|
39
|
+
|
40
|
+
# Character is whitespace outside of double quotes:
|
41
|
+
else
|
42
|
+
add_word
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_word
|
47
|
+
return if @word.empty?
|
48
|
+
|
49
|
+
@words << @word
|
50
|
+
@word = ''
|
51
|
+
end
|
52
|
+
|
53
|
+
def handle_special(char)
|
54
|
+
case char
|
55
|
+
when '\\'
|
56
|
+
@escaped = true
|
57
|
+
when '"'
|
58
|
+
@in_quotes = !@in_quotes
|
59
|
+
@word << char
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def close_escape(char)
|
64
|
+
@word << char
|
65
|
+
@escaped = false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -30,7 +30,7 @@ module PictureTag
|
|
30
30
|
# source_dest is the jekyll-picture-tag destination directory. (generated
|
31
31
|
# file location setting.)
|
32
32
|
def dest_dir
|
33
|
-
File.join PictureTag.site.
|
33
|
+
File.join PictureTag.site.config['destination'], pconfig['output']
|
34
34
|
end
|
35
35
|
|
36
36
|
def nomarkdown?
|
@@ -3,6 +3,7 @@ module PictureTag
|
|
3
3
|
# Handles the specific tag image set to construct.
|
4
4
|
class Preset
|
5
5
|
attr_reader :name
|
6
|
+
|
6
7
|
def initialize(name)
|
7
8
|
@name = name
|
8
9
|
@content = build_preset
|
@@ -12,13 +13,6 @@ module PictureTag
|
|
12
13
|
@content[key]
|
13
14
|
end
|
14
15
|
|
15
|
-
# Returns the set of widths to use for a given media query.
|
16
|
-
def widths(media)
|
17
|
-
width_hash = self['media_widths'] || {}
|
18
|
-
width_hash.default = self['widths']
|
19
|
-
width_hash[media]
|
20
|
-
end
|
21
|
-
|
22
16
|
def formats
|
23
17
|
@content['formats']
|
24
18
|
end
|
@@ -41,15 +35,38 @@ module PictureTag
|
|
41
35
|
end
|
42
36
|
end
|
43
37
|
|
38
|
+
# Image widths to generate for a given media query.
|
39
|
+
def widths(media = nil)
|
40
|
+
setting_lookup('widths', 'media', media)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Image quality setting, possibly dependent on format.
|
44
44
|
def quality(format = nil)
|
45
|
-
|
46
|
-
|
45
|
+
setting_lookup('quality', 'format', format)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Gravity setting (for imagemagick cropping)
|
49
|
+
def gravity(media = nil)
|
50
|
+
setting_lookup('gravity', 'media', media)
|
51
|
+
end
|
47
52
|
|
48
|
-
|
53
|
+
# Crop value
|
54
|
+
def crop(media = nil)
|
55
|
+
setting_lookup('crop', 'media', media)
|
49
56
|
end
|
50
57
|
|
51
58
|
private
|
52
59
|
|
60
|
+
# Return arbitrary setting values, taking their defaults into account.
|
61
|
+
# Ex: quality can be set for all image formats, or individually per
|
62
|
+
# format. Per-format settings should override the general setting.
|
63
|
+
def setting_lookup(setting, prefix, lookup)
|
64
|
+
media_values = @content[prefix + '_' + setting] || {}
|
65
|
+
media_values.default = @content[setting]
|
66
|
+
|
67
|
+
media_values[lookup]
|
68
|
+
end
|
69
|
+
|
53
70
|
def build_preset
|
54
71
|
# The _data/picture.yml file is optional.
|
55
72
|
picture_data_file = grab_data_file
|
@@ -64,16 +81,24 @@ module PictureTag
|
|
64
81
|
end
|
65
82
|
|
66
83
|
def grab_data_file
|
84
|
+
search_data('presets') || search_data('markup_presets') || no_preset
|
85
|
+
end
|
86
|
+
|
87
|
+
def search_data(key)
|
67
88
|
PictureTag.site
|
68
89
|
.data
|
69
|
-
.dig('picture',
|
90
|
+
.dig('picture', key, @name)
|
70
91
|
end
|
71
92
|
|
72
93
|
def no_preset
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
94
|
+
unless @name == 'default'
|
95
|
+
Utils.warning(
|
96
|
+
<<~HEREDOC
|
97
|
+
Preset "#{@name}" not found in {PictureTag.config['data_dir']}/picture.yml
|
98
|
+
under markup_presets key. Using default values."
|
99
|
+
HEREDOC
|
100
|
+
)
|
101
|
+
end
|
77
102
|
|
78
103
|
{}
|
79
104
|
end
|
@@ -27,13 +27,35 @@ module PictureTag
|
|
27
27
|
# These are our Media Query presets. It's really just a hash, and there
|
28
28
|
# are no default values, so extracting this to its own class is overkill.
|
29
29
|
def media_presets
|
30
|
-
|
30
|
+
search_data('media_queries') || search_data('media_presets') || {}
|
31
31
|
end
|
32
32
|
|
33
33
|
def source_images
|
34
34
|
@source_images ||= build_source_images
|
35
35
|
end
|
36
36
|
|
37
|
+
def crop(media = nil)
|
38
|
+
params.geometries[media] || preset.crop(media)
|
39
|
+
end
|
40
|
+
|
41
|
+
def gravity(media = nil)
|
42
|
+
params.gravities[media] || preset.gravity(media)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a class constant for the selected output format, which is used
|
46
|
+
# to dynamically instantiate it.
|
47
|
+
def output_class
|
48
|
+
Object.const_get(
|
49
|
+
'PictureTag::OutputFormats::' + Utils.titleize(preset['markup'])
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def search_data(key)
|
56
|
+
PictureTag.site.data.dig('picture', key)
|
57
|
+
end
|
58
|
+
|
37
59
|
def build_source_images
|
38
60
|
source_names = params.source_names
|
39
61
|
media_presets = params.media_presets
|
@@ -48,14 +70,6 @@ module PictureTag
|
|
48
70
|
|
49
71
|
sources
|
50
72
|
end
|
51
|
-
|
52
|
-
# Returns a class constant for the selected output format, which is used
|
53
|
-
# to dynamically instantiate it.
|
54
|
-
def output_class
|
55
|
-
Object.const_get(
|
56
|
-
'PictureTag::OutputFormats::' + Utils.titleize(preset['markup'])
|
57
|
-
)
|
58
|
-
end
|
59
73
|
end
|
60
74
|
end
|
61
75
|
end
|
@@ -1,105 +1,95 @@
|
|
1
1
|
module PictureTag
|
2
2
|
module Instructions
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# Tag Parsing Responsibilities:
|
4
|
+
#
|
5
|
+
# {% picture mypreset a.jpg 3:2 mobile: b.jpg --alt "Alt" --link "/" %}
|
6
|
+
# | Jekyll | TagParser | HTMLAttributes |
|
7
|
+
#
|
8
|
+
# This class takes the arguments handed to the liquid tag (given as a simple
|
9
|
+
# string), hands them to ArgSplitter (which breaks them up into an array of
|
10
|
+
# words), extracts the preset name (if present), source image name(s),
|
11
|
+
# associated media queries (if present), and image-related arguments such as
|
12
|
+
# crop and gravity. HTML attributes are handed off to its respective class
|
13
|
+
# (as 'leftovers')
|
14
|
+
#
|
15
|
+
# Media presets and source names are stored as arrays in their correct
|
16
|
+
# orders. Gravities and geometries are stored in a hash, keyed by their
|
17
|
+
# relevant media presets. Note that the base image will have a media preset
|
18
|
+
# of nil, which is a perfectly fine hash key.
|
19
|
+
#
|
7
20
|
class TagParser
|
8
|
-
attr_reader :preset_name, :source_names, :media_presets
|
9
|
-
|
10
|
-
build_params PictureTag::Utils.liquid_lookup(raw_params)
|
21
|
+
attr_reader :preset_name, :source_names, :media_presets, :gravities,
|
22
|
+
:geometries, :leftovers
|
11
23
|
|
12
|
-
|
24
|
+
def initialize(raw_params)
|
25
|
+
@raw_params = raw_params
|
26
|
+
@params = split_params
|
13
27
|
|
14
|
-
# The first param specified will be our base image, so it has no
|
15
|
-
# associated media query.
|
16
28
|
@media_presets = []
|
17
|
-
@source_names = []
|
29
|
+
@source_names = []
|
30
|
+
@geometries = {}
|
31
|
+
@gravities = {}
|
18
32
|
|
19
|
-
|
20
|
-
# keys and values.
|
21
|
-
add_media_source while @params.first =~ /[\w\-]+:$/
|
22
|
-
end
|
23
|
-
|
24
|
-
def leftovers
|
25
|
-
@params
|
33
|
+
parse_params
|
26
34
|
end
|
27
35
|
|
28
36
|
private
|
29
37
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
38
|
+
def split_params
|
39
|
+
ArgSplitter
|
40
|
+
.new(Utils.liquid_lookup(@raw_params))
|
41
|
+
.words
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
'default'
|
39
|
-
else
|
40
|
-
@params.shift
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Originally separating arguments was just handled by splitting the raw
|
45
|
-
# params on spaces. To handle quotes and backslash escaping, we have to
|
46
|
-
# parse the string by characters to break it up correctly. I'm sure
|
47
|
-
# there's a library to do this, but it's not that much code honestly. If
|
48
|
-
# this starts getting big, we'll pull in a new dependency.
|
49
|
-
def build_params(raw_params)
|
50
|
-
@params = []
|
51
|
-
@word = ''
|
52
|
-
@in_quotes = false
|
53
|
-
@escaped = false
|
44
|
+
def parse_params
|
45
|
+
@preset_name = determine_preset_name
|
46
|
+
@source_names << strip_quotes(@params.shift)
|
54
47
|
|
55
|
-
|
48
|
+
parse_param(@params.first) until stop_here? @params.first
|
56
49
|
|
57
|
-
|
50
|
+
@leftovers = @params
|
58
51
|
end
|
59
52
|
|
60
|
-
def
|
61
|
-
|
62
|
-
|
63
|
-
close_escape char
|
53
|
+
def parse_param(param)
|
54
|
+
if param.match?(/[\w\-]+:$/)
|
55
|
+
add_media_source
|
64
56
|
|
65
|
-
|
66
|
-
|
67
|
-
handle_special char
|
57
|
+
elsif Utils::GRAVITIES.include?(param.downcase)
|
58
|
+
@gravities[@media_presets.last] = @params.shift
|
68
59
|
|
69
|
-
|
70
|
-
|
71
|
-
@word << char
|
60
|
+
elsif param.match?(Utils::GEOMETRY_REGEX)
|
61
|
+
@geometries[@media_presets.last] = @params.shift
|
72
62
|
|
73
|
-
# Character is whitespace outside of double quotes:
|
74
63
|
else
|
75
|
-
|
64
|
+
raise_error(param)
|
76
65
|
end
|
77
66
|
end
|
78
67
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@word << char
|
85
|
-
end
|
68
|
+
# HTML attributes are handled by its own class; once we encounter them
|
69
|
+
# we are finished here.
|
70
|
+
def stop_here?(param)
|
71
|
+
# No param Explicit HTML attribute Implicit HTML attribute
|
72
|
+
param.nil? || param.match?(/^--\S*/) || param.match?(/^\w*="/)
|
86
73
|
end
|
87
74
|
|
88
|
-
def
|
89
|
-
|
90
|
-
|
91
|
-
@params << @word
|
92
|
-
@word = ''
|
75
|
+
def add_media_source
|
76
|
+
@media_presets << @params.shift.delete_suffix(':')
|
77
|
+
@source_names << strip_quotes(@params.shift)
|
93
78
|
end
|
94
79
|
|
95
|
-
|
96
|
-
|
97
|
-
@
|
80
|
+
# The first param is the preset name, unless it's a filename.
|
81
|
+
def determine_preset_name
|
82
|
+
@params.first.include?('.') ? 'default' : @params.shift
|
98
83
|
end
|
99
84
|
|
100
85
|
def strip_quotes(name)
|
101
86
|
name.delete_prefix('"').delete_suffix('"')
|
102
87
|
end
|
88
|
+
|
89
|
+
def raise_error(param)
|
90
|
+
raise ArgumentError, "Could not parse '#{param}' in the following "\
|
91
|
+
"tag: \n {% picture #{@raw_params} %}"
|
92
|
+
end
|
103
93
|
end
|
104
94
|
end
|
105
95
|
end
|