jekyll_picture_tag 1.9.0 → 1.12.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 +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
|