jekyll_picture_tag 1.6.0 → 1.7.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 +2 -0
- data/.travis.yml +11 -0
- data/Gemfile +2 -2
- data/Rakefile +28 -0
- data/contributing.md +67 -0
- data/docs/examples/_config.yml +10 -0
- data/{examples → docs/examples}/_data/picture.yml +39 -19
- data/docs/examples/post.md +46 -0
- data/docs/global_configuration.md +115 -0
- data/docs/installation.md +30 -0
- data/docs/migration.md +178 -0
- data/docs/notes.md +85 -0
- data/docs/presets.md +407 -0
- data/docs/readme.md +23 -0
- data/docs/usage.md +131 -0
- data/jekyll-picture-tag.gemspec +3 -12
- data/jekyll_picture_tag.gemspec +8 -3
- data/lib/jekyll-picture-tag.rb +5 -3
- data/lib/jekyll_picture_tag.rb +45 -42
- data/lib/jekyll_picture_tag/defaults/global.yml +0 -3
- data/lib/jekyll_picture_tag/defaults/presets.yml +1 -0
- data/lib/jekyll_picture_tag/generated_image.rb +60 -39
- data/lib/jekyll_picture_tag/img_uri.rb +55 -0
- data/lib/jekyll_picture_tag/instructions.rb +1 -102
- data/lib/jekyll_picture_tag/instructions/configuration.rb +30 -74
- data/lib/jekyll_picture_tag/instructions/html_attributes.rb +18 -27
- data/lib/jekyll_picture_tag/instructions/preset.rb +14 -3
- data/lib/jekyll_picture_tag/instructions/set.rb +61 -0
- data/lib/jekyll_picture_tag/instructions/tag_parser.rb +80 -23
- data/lib/jekyll_picture_tag/output_formats.rb +1 -1
- data/lib/jekyll_picture_tag/output_formats/{basics.rb → basic.rb} +24 -19
- data/lib/jekyll_picture_tag/output_formats/data_attributes.rb +2 -2
- data/lib/jekyll_picture_tag/output_formats/direct_url.rb +1 -3
- data/lib/jekyll_picture_tag/output_formats/img.rb +4 -4
- data/lib/jekyll_picture_tag/output_formats/naked_srcset.rb +5 -4
- data/lib/jekyll_picture_tag/output_formats/picture.rb +6 -16
- data/lib/jekyll_picture_tag/output_formats/readme.md +8 -15
- data/lib/jekyll_picture_tag/router.rb +98 -0
- data/lib/jekyll_picture_tag/source_image.rb +15 -23
- data/lib/jekyll_picture_tag/srcsets.rb +1 -1
- data/lib/jekyll_picture_tag/srcsets/{basics.rb → basic.rb} +22 -13
- data/lib/jekyll_picture_tag/srcsets/pixel_ratio.rb +6 -11
- data/lib/jekyll_picture_tag/srcsets/width.rb +3 -11
- data/lib/jekyll_picture_tag/utils.rb +32 -49
- data/lib/jekyll_picture_tag/version.rb +1 -1
- data/readme.md +70 -70
- metadata +97 -16
- data/bin/console +0 -14
- data/bin/setup +0 -7
- data/examples/_config.yml +0 -4
- data/examples/post.md +0 -18
@@ -1,47 +1,104 @@
|
|
1
1
|
module PictureTag
|
2
2
|
module Instructions
|
3
|
-
# This
|
4
|
-
#
|
3
|
+
# This tag takes the arguments handed to the liquid tag, and extracts the
|
4
|
+
# preset name (if present), source image name(s), and associated media
|
5
|
+
# queries (if present). The leftovers (html attributes) are handed off to
|
6
|
+
# its respective class.
|
5
7
|
class TagParser
|
6
|
-
attr_reader :preset_name, :
|
8
|
+
attr_reader :preset_name, :source_names, :media_presets
|
7
9
|
def initialize(raw_params)
|
8
|
-
|
10
|
+
build_params PictureTag::Utils.liquid_lookup(raw_params)
|
9
11
|
|
10
12
|
@preset_name = grab_preset_name
|
11
13
|
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
15
|
-
|
14
|
+
# The first param specified will be our base image, so it has no
|
15
|
+
# associated media query.
|
16
|
+
@media_presets = []
|
17
|
+
@source_names = [] << strip_quotes(@params.shift)
|
16
18
|
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
@source_images = build_sources(source_image_names)
|
19
|
+
# Detect and store arguments of the format 'media_query: img.jpg' as
|
20
|
+
# keys and values.
|
21
|
+
add_media_source while @params.first =~ /[\w\-]+:$/
|
22
|
+
end
|
24
23
|
|
25
|
-
|
26
|
-
@
|
24
|
+
def leftovers
|
25
|
+
@params
|
27
26
|
end
|
28
27
|
|
29
28
|
private
|
30
29
|
|
30
|
+
def add_media_source
|
31
|
+
@media_presets << @params.shift.delete_suffix(':')
|
32
|
+
@source_names << strip_quotes(@params.shift)
|
33
|
+
end
|
34
|
+
|
31
35
|
# First param is the preset name, unless it's a filename.
|
32
36
|
def grab_preset_name
|
33
|
-
if @params.first
|
37
|
+
if @params.first.include? '.'
|
34
38
|
'default'
|
35
39
|
else
|
36
40
|
@params.shift
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
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
|
54
|
+
|
55
|
+
raw_params.each_char { |c| handle_char(c) }
|
56
|
+
|
57
|
+
add_word # We have to explicitly add the last one.
|
58
|
+
end
|
59
|
+
|
60
|
+
def handle_char(char)
|
61
|
+
# last character was a backslash:
|
62
|
+
if @escaped
|
63
|
+
close_escape char
|
64
|
+
|
65
|
+
# char is a backslash or a quote:
|
66
|
+
elsif char.match(/["\\]/)
|
67
|
+
handle_special char
|
68
|
+
|
69
|
+
# Character isn't whitespace, or it's inside double quotes:
|
70
|
+
elsif @in_quotes || char.match(/\S/)
|
71
|
+
@word << char
|
72
|
+
|
73
|
+
# Character is whitespace outside of double quotes:
|
74
|
+
else
|
75
|
+
add_word
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_special(char)
|
80
|
+
if char == '\\'
|
81
|
+
@escaped = true
|
82
|
+
elsif char == '"'
|
83
|
+
@in_quotes = !@in_quotes
|
84
|
+
@word << char
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def add_word
|
89
|
+
return if @word.empty?
|
90
|
+
|
91
|
+
@params << @word
|
92
|
+
@word = ''
|
93
|
+
end
|
94
|
+
|
95
|
+
def close_escape(char)
|
96
|
+
@word << char
|
97
|
+
@escaped = false
|
98
|
+
end
|
99
|
+
|
100
|
+
def strip_quotes(name)
|
101
|
+
name.delete_prefix('"').delete_suffix('"')
|
45
102
|
end
|
46
103
|
end
|
47
104
|
end
|
@@ -3,7 +3,7 @@ module PictureTag
|
|
3
3
|
# own option.
|
4
4
|
module OutputFormats
|
5
5
|
# Generic functions common to all output formats.
|
6
|
-
|
6
|
+
class Basic
|
7
7
|
include ObjectiveElements
|
8
8
|
|
9
9
|
# Used for both the fallback image, and for the complete markup.
|
@@ -16,7 +16,7 @@ module PictureTag
|
|
16
16
|
|
17
17
|
fallback = build_fallback_image
|
18
18
|
|
19
|
-
add_src(img, fallback.
|
19
|
+
add_src(img, fallback.uri)
|
20
20
|
|
21
21
|
add_alt(img, attributes['alt'])
|
22
22
|
|
@@ -40,26 +40,17 @@ module PictureTag
|
|
40
40
|
end
|
41
41
|
|
42
42
|
# Media is the media query associated with the desired source image.
|
43
|
-
def build_srcset(
|
43
|
+
def build_srcset(source_image, format)
|
44
44
|
if PictureTag.preset['pixel_ratios']
|
45
|
-
|
45
|
+
Srcsets::PixelRatio.new(source_image, format)
|
46
46
|
else
|
47
|
-
|
47
|
+
Srcsets::Width.new(source_image, format)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
def build_pixel_ratio_srcset(media, format)
|
52
|
-
Srcsets::PixelRatio.new(media: media, format: format)
|
53
|
-
end
|
54
|
-
|
55
|
-
def build_width_srcset(media, format)
|
56
|
-
Srcsets::Width.new(media: media, format: format)
|
57
|
-
end
|
58
|
-
|
59
51
|
# Extracting these functions to their own methods for easy overriding.
|
60
|
-
|
61
|
-
|
62
|
-
element.src = PictureTag.build_url name
|
52
|
+
def add_src(element, uri)
|
53
|
+
element.src = uri
|
63
54
|
end
|
64
55
|
|
65
56
|
def add_srcset(element, srcset)
|
@@ -81,9 +72,9 @@ module PictureTag
|
|
81
72
|
# File, not HTML
|
82
73
|
def build_fallback_image
|
83
74
|
GeneratedImage.new(
|
84
|
-
source_file: PictureTag.source_images
|
75
|
+
source_file: PictureTag.source_images.first,
|
85
76
|
format: PictureTag.fallback_format,
|
86
|
-
width:
|
77
|
+
width: checked_fallback_width
|
87
78
|
)
|
88
79
|
end
|
89
80
|
|
@@ -93,7 +84,7 @@ module PictureTag
|
|
93
84
|
# Kramdown is super picky about the {::nomarkdown} extension-- we have to
|
94
85
|
# strip line breaks or nothing works.
|
95
86
|
def nomarkdown_wrapper(content)
|
96
|
-
"{::nomarkdown}#{content.delete("\n")}{:/nomarkdown}"
|
87
|
+
"{::nomarkdown}#{content.delete("\n").gsub(/> </, '><')}{:/nomarkdown}"
|
97
88
|
end
|
98
89
|
|
99
90
|
def anchor_tag(content)
|
@@ -103,6 +94,20 @@ module PictureTag
|
|
103
94
|
|
104
95
|
content.add_parent anchor
|
105
96
|
end
|
97
|
+
|
98
|
+
def checked_fallback_width
|
99
|
+
source = PictureTag.source_images.first
|
100
|
+
target = PictureTag.fallback_width
|
101
|
+
|
102
|
+
if target > source.width
|
103
|
+
Utils.warning "#{source.shortname} is smaller than the " \
|
104
|
+
"requested fallback width of #{target}px. Using #{source.width}" \
|
105
|
+
' px instead.'
|
106
|
+
source.width
|
107
|
+
else
|
108
|
+
target
|
109
|
+
end
|
110
|
+
end
|
106
111
|
end
|
107
112
|
end
|
108
113
|
end
|
@@ -2,9 +2,7 @@ module PictureTag
|
|
2
2
|
module OutputFormats
|
3
3
|
# Represents a bare url you can use in another context, such as a direct
|
4
4
|
# link, but keep the resizing functionality
|
5
|
-
class DirectUrl
|
6
|
-
include PictureTag::OutputFormats::Basics
|
7
|
-
|
5
|
+
class DirectUrl < Basic
|
8
6
|
def to_s
|
9
7
|
build_base_img.src
|
10
8
|
end
|
@@ -2,11 +2,11 @@ module PictureTag
|
|
2
2
|
module OutputFormats
|
3
3
|
# Represents a bare <img> tag with a srcset attribute.
|
4
4
|
# Used when <picture> is unnecessary.
|
5
|
-
class Img
|
6
|
-
include PictureTag::OutputFormats::Basics
|
7
|
-
|
5
|
+
class Img < Basic
|
8
6
|
def srcset
|
9
|
-
build_srcset(
|
7
|
+
@srcset ||= build_srcset(
|
8
|
+
PictureTag.source_images.first, PictureTag.formats.first
|
9
|
+
)
|
10
10
|
end
|
11
11
|
|
12
12
|
def base_markup
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module PictureTag
|
2
2
|
module OutputFormats
|
3
3
|
# Returns only a srcset attribute, for more custom or complicated markup.
|
4
|
-
class NakedSrcset
|
5
|
-
include Basics
|
6
|
-
|
4
|
+
class NakedSrcset < Basic
|
7
5
|
def to_s
|
8
|
-
|
6
|
+
image = PictureTag.source_images.first
|
7
|
+
format = PictureTag.formats.first
|
8
|
+
|
9
|
+
build_srcset(image, format).to_s
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -2,25 +2,15 @@ module PictureTag
|
|
2
2
|
module OutputFormats
|
3
3
|
# Represents a <picture> tag, enclosing at least 2 <source> tags and an
|
4
4
|
# <img> tag.
|
5
|
-
class Picture
|
6
|
-
include Basics
|
7
|
-
|
5
|
+
class Picture < Basic
|
8
6
|
def srcsets
|
7
|
+
formats = PictureTag.formats
|
8
|
+
# Source images are provided in reverse order and must be flipped:
|
9
|
+
images = PictureTag.source_images.reverse
|
9
10
|
sets = []
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
# provided in the order they must be returned, source images are
|
14
|
-
# provided in the reverse (least to most preferable) and must be
|
15
|
-
# flipped. We'll use an intermediate value to accomplish this.
|
16
|
-
format_set = []
|
17
|
-
|
18
|
-
# Source images are defined in the tag params, and associated with
|
19
|
-
# media queries. The base (first provided) image has a key of nil.
|
20
|
-
PictureTag.source_images.each_key do |media|
|
21
|
-
format_set << build_srcset(media, format)
|
22
|
-
end
|
23
|
-
sets.concat format_set.reverse
|
12
|
+
formats.each do |format|
|
13
|
+
images.each { |image| sets << build_srcset(image, format) }
|
24
14
|
end
|
25
15
|
|
26
16
|
sets
|
@@ -1,16 +1,9 @@
|
|
1
1
|
# Writing Output Formats
|
2
2
|
|
3
|
-
|
4
3
|
## Naming and Instantiating
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
instantiated.
|
9
|
-
|
10
|
-
Example:
|
11
|
-
|
12
|
-
In `_data/picture.yml`: `markup: example_format` will cause the plugin to use
|
13
|
-
an instance of `ExampleFormat` (with no arguments.)
|
5
|
+
In the relevant `_data/picture.yml` preset, `markup: example_format` will cause
|
6
|
+
the plugin to use an instance of `ExampleFormat` (with no arguments.)
|
14
7
|
|
15
8
|
You'll need to add an appropriate `require_relative` statement to
|
16
9
|
`../output_formats.rb`
|
@@ -24,13 +17,13 @@ it this way because information flow was getting arduous; I was passing a lot
|
|
24
17
|
of information to classes which only needed it to pass on to classes they
|
25
18
|
instantiate.
|
26
19
|
|
27
|
-
`PictureTag.source_images` returns
|
28
|
-
liquid tag.
|
29
|
-
|
30
|
-
|
31
|
-
media queries named in `_data/picture.yml`.
|
20
|
+
`PictureTag.source_images` returns an array of the source images provided in
|
21
|
+
the liquid tag. The first one is the base image, the rest that follow are
|
22
|
+
associated with media queries. Check out `source_image.rb` to see what they
|
23
|
+
offer.
|
32
24
|
|
33
|
-
There's a lot of information available, dig around in `../
|
25
|
+
There's a lot of information available, dig around in `../router.rb`.
|
26
|
+
Output formats should only consume this information, never modify it.
|
34
27
|
|
35
28
|
## Producing output
|
36
29
|
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module PictureTag
|
2
|
+
# The rest of the application doesn't care where the instruction logic
|
3
|
+
# resides. The following module 'routes' method calls to the right place, so
|
4
|
+
# the rest of the application can just call 'PictureTag.(some method)'
|
5
|
+
|
6
|
+
# At first I thought I'd do some sweet dynamic metaprogramming here, but it
|
7
|
+
# ended up complicated and clever, rather than convenient and understandable.
|
8
|
+
# This way is not strictly DRY, but it's straightforward and readable. If it
|
9
|
+
# gets big, I'll refactor.
|
10
|
+
module Router
|
11
|
+
attr_accessor :instructions, :context
|
12
|
+
# Context forwarding
|
13
|
+
|
14
|
+
# Global site data
|
15
|
+
def site
|
16
|
+
@context.registers[:site]
|
17
|
+
end
|
18
|
+
|
19
|
+
# Page which tag is called from
|
20
|
+
def page
|
21
|
+
@context.registers[:page]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Instructions forwarding
|
25
|
+
|
26
|
+
def config
|
27
|
+
@instructions.config
|
28
|
+
end
|
29
|
+
|
30
|
+
def preset
|
31
|
+
@instructions.preset
|
32
|
+
end
|
33
|
+
|
34
|
+
def media_presets
|
35
|
+
@instructions.media_presets
|
36
|
+
end
|
37
|
+
|
38
|
+
def html_attributes
|
39
|
+
@instructions.html_attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
def output_class
|
43
|
+
@instructions.output_class
|
44
|
+
end
|
45
|
+
|
46
|
+
def source_images
|
47
|
+
@instructions.source_images
|
48
|
+
end
|
49
|
+
|
50
|
+
# Config Forwarding
|
51
|
+
|
52
|
+
def source_dir
|
53
|
+
config.source_dir
|
54
|
+
end
|
55
|
+
|
56
|
+
def dest_dir
|
57
|
+
config.dest_dir
|
58
|
+
end
|
59
|
+
|
60
|
+
def continue_on_missing?
|
61
|
+
config.continue_on_missing?
|
62
|
+
end
|
63
|
+
|
64
|
+
def cdn?
|
65
|
+
config.cdn?
|
66
|
+
end
|
67
|
+
|
68
|
+
def pconfig
|
69
|
+
config.pconfig
|
70
|
+
end
|
71
|
+
|
72
|
+
# Preset forwarding
|
73
|
+
|
74
|
+
def widths(media)
|
75
|
+
preset.widths(media)
|
76
|
+
end
|
77
|
+
|
78
|
+
def formats
|
79
|
+
preset.formats
|
80
|
+
end
|
81
|
+
|
82
|
+
def fallback_format
|
83
|
+
preset.fallback_format
|
84
|
+
end
|
85
|
+
|
86
|
+
def fallback_width
|
87
|
+
preset.fallback_width
|
88
|
+
end
|
89
|
+
|
90
|
+
def nomarkdown?
|
91
|
+
preset.nomarkdown?
|
92
|
+
end
|
93
|
+
|
94
|
+
def quality(format)
|
95
|
+
preset.quality(format)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|