jekyll_picture_tag 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +24 -0
- data/Rakefile +1 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/examples/_config.yml +4 -0
- data/examples/_data/picture.yml +85 -0
- data/examples/post.md +18 -0
- data/jekyll_picture_tag.gemspec +39 -0
- data/lib/jekyll_picture_tag.rb +67 -0
- data/lib/jekyll_picture_tag/defaults/global.yml +10 -0
- data/lib/jekyll_picture_tag/defaults/presets.yml +7 -0
- data/lib/jekyll_picture_tag/generated_image.rb +66 -0
- data/lib/jekyll_picture_tag/instructions.rb +105 -0
- data/lib/jekyll_picture_tag/instructions/configuration.rb +82 -0
- data/lib/jekyll_picture_tag/instructions/html_attributes.rb +59 -0
- data/lib/jekyll_picture_tag/instructions/preset.rb +61 -0
- data/lib/jekyll_picture_tag/instructions/tag_parser.rb +48 -0
- data/lib/jekyll_picture_tag/output_formats.rb +9 -0
- data/lib/jekyll_picture_tag/output_formats/auto.rb +15 -0
- data/lib/jekyll_picture_tag/output_formats/basics.rb +105 -0
- data/lib/jekyll_picture_tag/output_formats/data_attributes.rb +44 -0
- data/lib/jekyll_picture_tag/output_formats/data_auto.rb +14 -0
- data/lib/jekyll_picture_tag/output_formats/data_img.rb +8 -0
- data/lib/jekyll_picture_tag/output_formats/data_picture.rb +8 -0
- data/lib/jekyll_picture_tag/output_formats/direct_url.rb +13 -0
- data/lib/jekyll_picture_tag/output_formats/img.rb +24 -0
- data/lib/jekyll_picture_tag/output_formats/picture.rb +66 -0
- data/lib/jekyll_picture_tag/source_image.rb +62 -0
- data/lib/jekyll_picture_tag/srcsets.rb +3 -0
- data/lib/jekyll_picture_tag/srcsets/basics.rb +88 -0
- data/lib/jekyll_picture_tag/srcsets/pixel_ratio.rb +32 -0
- data/lib/jekyll_picture_tag/srcsets/width.rb +45 -0
- data/lib/jekyll_picture_tag/utils.rb +67 -0
- data/lib/jekyll_picture_tag/version.rb +3 -0
- data/migration.md +178 -0
- data/readme.md +787 -0
- metadata +191 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# Global config (big picture). loads jekyll data/config files, and the j-p-t
|
4
|
+
# defaults from included yml files.
|
5
|
+
class Configuration
|
6
|
+
def initialize
|
7
|
+
@content = build_config
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
@content[key]
|
12
|
+
end
|
13
|
+
|
14
|
+
# Site.source is the master jekyll source directory
|
15
|
+
# Source dir is the jekyll-picture-tag source directory.
|
16
|
+
def source_dir
|
17
|
+
File.join PictureTag.site.source, self['picture']['source']
|
18
|
+
end
|
19
|
+
|
20
|
+
# site.dest is the master jekyll destination directory
|
21
|
+
# source_dest is the jekyll-picture-tag destination directory. (generated
|
22
|
+
# file location setting.)
|
23
|
+
def dest_dir
|
24
|
+
File.join PictureTag.site.dest, self['picture']['output']
|
25
|
+
end
|
26
|
+
|
27
|
+
# Takes our config into account. Generated images, not source
|
28
|
+
def build_url(filename)
|
29
|
+
File.join url_prefix, self['picture']['output'], filename
|
30
|
+
end
|
31
|
+
|
32
|
+
# For linking source images
|
33
|
+
def build_source_url(filename)
|
34
|
+
File.join url_prefix, self['picture']['source'], filename
|
35
|
+
end
|
36
|
+
|
37
|
+
def nomarkdown?
|
38
|
+
Utils.markdown_page? && self['picture']['nomarkdown']
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def build_config
|
44
|
+
YAML.safe_load(
|
45
|
+
# Jekyll Picture Tag Default settings
|
46
|
+
File.read(
|
47
|
+
File.join(ROOT_PATH, 'jekyll_picture_tag/defaults/global.yml')
|
48
|
+
)
|
49
|
+
).merge(
|
50
|
+
# _config.yml defined settings
|
51
|
+
PictureTag.site.config
|
52
|
+
) do |_key, jpt_default, site_value|
|
53
|
+
setting_merge(jpt_default, site_value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def setting_merge(jpt_default, site_value)
|
58
|
+
if site_value.nil?
|
59
|
+
# Jekyll baseurl is nil if not configured, which breaks things.
|
60
|
+
# jpt_default is an empty string, which doesn't.
|
61
|
+
jpt_default
|
62
|
+
elsif site_value.is_a? Hash
|
63
|
+
# We'll merge hashes one level deep. If we need true deep merging,
|
64
|
+
# we'll import a gem or do something recursive.
|
65
|
+
jpt_default.merge site_value
|
66
|
+
else
|
67
|
+
site_value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# https://example.com/my-base-path/assets/generated-images/image.jpg
|
72
|
+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
73
|
+
# | site url | site path | j-p-t dest dir |
|
74
|
+
def url_prefix
|
75
|
+
File.join(
|
76
|
+
self['picture']['relative_url'] ? '' : PictureTag.config['url'],
|
77
|
+
PictureTag.config['baseurl'],
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# Handles HTML attributes, sourced from configuration and the liquid tag,
|
4
|
+
# sent to various elements.
|
5
|
+
# Stored as a hash, with string keys.
|
6
|
+
class HTMLAttributeSet
|
7
|
+
# Initialize with leftovers passed into the liquid tag
|
8
|
+
def initialize(params)
|
9
|
+
@content = load_preset
|
10
|
+
|
11
|
+
parse_params(params) if params
|
12
|
+
handle_url
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@content[key]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_preset
|
22
|
+
PictureTag.preset['attributes'] || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Syntax this function processes:
|
26
|
+
# class="old way" --picture class="new way" --alt Here's my alt text
|
27
|
+
def parse_params(params)
|
28
|
+
params_array = params.split(/\s+--/).map(&:strip)
|
29
|
+
|
30
|
+
# This allows the old tag syntax to work.
|
31
|
+
@content['implicit'] = params_array.shift unless params.strip =~ /^--/
|
32
|
+
|
33
|
+
# Split function above doesn't take the dashes off the first param.
|
34
|
+
params_array.first.delete_prefix! '--' if params_array.any?
|
35
|
+
|
36
|
+
params_array.each do |param|
|
37
|
+
# Splits on spaces, the first word will be our key.
|
38
|
+
a = param.split
|
39
|
+
|
40
|
+
# Supplied tag arguments will overwrite (not append) configured values
|
41
|
+
@content[a.shift] = a.join(' ')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
# Handles anchor tag destination. Can come from 2 places in 2 formats:
|
45
|
+
# Can come from defaults, preset, or tag
|
46
|
+
# Default is false. Preset can specify either true or false
|
47
|
+
# Tag params can be a URL
|
48
|
+
|
49
|
+
# picture test.jpg --url http://example.com
|
50
|
+
def handle_url
|
51
|
+
return unless PictureTag.preset['link_source'] && !self['link']
|
52
|
+
|
53
|
+
@content['link'] = PictureTag.build_source_url(
|
54
|
+
Utils.biggest_source.shortname
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# Handles the specific tag image set to construct.
|
4
|
+
class Preset
|
5
|
+
attr_reader :name
|
6
|
+
def initialize
|
7
|
+
@name = PictureTag.preset_name
|
8
|
+
@content = build_preset
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](key)
|
12
|
+
@content[key]
|
13
|
+
end
|
14
|
+
|
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
|
+
def fallback_format
|
23
|
+
PictureTag::Utils.process_format(@content['fallback_format'], nil)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fallback_width
|
27
|
+
@content['fallback_width']
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def build_preset
|
33
|
+
# The _data/picture.yml file is optional.
|
34
|
+
picture_data_file = grab_data_file
|
35
|
+
|
36
|
+
default_preset.merge picture_data_file
|
37
|
+
end
|
38
|
+
|
39
|
+
def default_preset
|
40
|
+
YAML.safe_load File.read(
|
41
|
+
File.join(ROOT_PATH, 'jekyll_picture_tag/defaults/presets.yml')
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def grab_data_file
|
46
|
+
PictureTag.site
|
47
|
+
.data
|
48
|
+
.dig('picture', 'markup_presets', @name) || no_preset
|
49
|
+
end
|
50
|
+
|
51
|
+
def no_preset
|
52
|
+
Utils.warning(
|
53
|
+
" Preset \"#{@name}\" not found in _data/picture.yml under "\
|
54
|
+
'markup_presets key. Using default values.'
|
55
|
+
)
|
56
|
+
|
57
|
+
{}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module Instructions
|
3
|
+
# This class takes the string given to the jekyll tag, and extracts useful
|
4
|
+
# information from it.
|
5
|
+
class TagParser
|
6
|
+
attr_reader :preset_name, :source_images, :html_attributes_raw
|
7
|
+
def initialize(raw_params)
|
8
|
+
@params = PictureTag::Utils.liquid_lookup(raw_params).split
|
9
|
+
|
10
|
+
@preset_name = grab_preset_name
|
11
|
+
|
12
|
+
# source_image keys are media queries, values are source images. The
|
13
|
+
# first param specified will be our base image, so it has no associated
|
14
|
+
# media query. Yes, nil can be a hash key.
|
15
|
+
source_image_names = { nil => @params.shift }
|
16
|
+
|
17
|
+
# Store source keys which fit a pattern in a hash.
|
18
|
+
while @params.first =~ /[\w\-]+:/
|
19
|
+
media_query = @params.shift[0..-2]
|
20
|
+
source_image_names[media_query] = @params.shift
|
21
|
+
end
|
22
|
+
|
23
|
+
@source_images = build_sources(source_image_names)
|
24
|
+
|
25
|
+
# Anything left will be html attributes
|
26
|
+
@html_attributes_raw = @params.join(' ')
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# First param is the preset name, unless it's a filename.
|
32
|
+
def grab_preset_name
|
33
|
+
if @params.first =~ %r{[\w./]+\.\w+}
|
34
|
+
'default'
|
35
|
+
else
|
36
|
+
@params.shift
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Takes filenames relative to JPT source directory. SourceImage instances
|
41
|
+
# persist within a tag, allowing us to only perform some expensive File
|
42
|
+
# operations once.
|
43
|
+
def build_sources(names)
|
44
|
+
names.transform_values { |n| PictureTag::SourceImage.new n }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative 'output_formats/basics'
|
2
|
+
require_relative 'output_formats/data_attributes'
|
3
|
+
require_relative 'output_formats/auto'
|
4
|
+
require_relative 'output_formats/img'
|
5
|
+
require_relative 'output_formats/picture'
|
6
|
+
require_relative 'output_formats/data_auto'
|
7
|
+
require_relative 'output_formats/data_picture'
|
8
|
+
require_relative 'output_formats/data_img'
|
9
|
+
require_relative 'output_formats/direct_url'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module OutputFormats
|
3
|
+
# This is an odd class, which never returns itself. It selects the most
|
4
|
+
# appropriate format, and returns an instance of that class.
|
5
|
+
class Auto
|
6
|
+
def self.new
|
7
|
+
if PictureTag::Utils.count_srcsets > 1
|
8
|
+
Picture.new
|
9
|
+
else
|
10
|
+
Img.new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module PictureTag
|
2
|
+
# Contains all possible HTML output format options. Auto is considered its
|
3
|
+
# own option.
|
4
|
+
module OutputFormats
|
5
|
+
# Generic functions common to all output formats.
|
6
|
+
module Basics
|
7
|
+
include ObjectiveElements
|
8
|
+
|
9
|
+
# Used for both the fallback image, and for the complete markup.
|
10
|
+
def build_base_img
|
11
|
+
img = SingleTag.new 'img'
|
12
|
+
attributes = PictureTag.html_attributes
|
13
|
+
|
14
|
+
img.attributes << attributes['img']
|
15
|
+
img.attributes << attributes['implicit']
|
16
|
+
|
17
|
+
fallback = build_fallback_image
|
18
|
+
|
19
|
+
add_src(img, fallback.name)
|
20
|
+
|
21
|
+
add_alt(img, attributes['alt'])
|
22
|
+
|
23
|
+
img
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
wrap(base_markup).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Handles various wrappers around basic markup
|
33
|
+
def wrap(markup)
|
34
|
+
markup = anchor_tag(markup) if PictureTag.html_attributes['link']
|
35
|
+
markup = nomarkdown_wrapper(markup.to_s) if PictureTag.nomarkdown?
|
36
|
+
|
37
|
+
markup
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_srcset(media, format)
|
41
|
+
if PictureTag.preset['pixel_ratios']
|
42
|
+
build_pixel_ratio_srcset(media, format)
|
43
|
+
else
|
44
|
+
build_width_srcset(media, format)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_pixel_ratio_srcset(media, format)
|
49
|
+
Srcsets::PixelRatio.new(media: media, format: format)
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_width_srcset(media, format)
|
53
|
+
Srcsets::Width.new(media: media, format: format)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Extracting these functions to their own methods for easy overriding.
|
57
|
+
# They are destructive.
|
58
|
+
def add_src(element, name)
|
59
|
+
element.src = PictureTag.build_url name
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_srcset(element, srcset)
|
63
|
+
element.srcset = srcset.to_s
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_sizes(element, srcset)
|
67
|
+
element.sizes = srcset.sizes if srcset.sizes
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_alt(element, alt)
|
71
|
+
element.alt = alt if alt
|
72
|
+
end
|
73
|
+
|
74
|
+
def add_media(element, srcset)
|
75
|
+
element.media = srcset.media_attribute if srcset.media
|
76
|
+
end
|
77
|
+
|
78
|
+
# File, not HTML
|
79
|
+
def build_fallback_image
|
80
|
+
GeneratedImage.new(
|
81
|
+
source_file: PictureTag.source_images[nil],
|
82
|
+
format: PictureTag.fallback_format,
|
83
|
+
width: PictureTag.fallback_width
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Stops kramdown from molesting our output
|
88
|
+
# Must be given a string.
|
89
|
+
#
|
90
|
+
# Kramdown is super picky about the {::nomarkdown} extension-- we have to
|
91
|
+
# strip line breaks or nothing works.
|
92
|
+
def nomarkdown_wrapper(content)
|
93
|
+
"{::nomarkdown}#{content.delete("\n")}{:/nomarkdown}"
|
94
|
+
end
|
95
|
+
|
96
|
+
def anchor_tag(content)
|
97
|
+
anchor = DoubleTag.new 'a'
|
98
|
+
anchor.attributes << PictureTag.html_attributes['a']
|
99
|
+
anchor.href = PictureTag.html_attributes['link']
|
100
|
+
|
101
|
+
content.add_parent anchor
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module OutputFormats
|
3
|
+
# This is not an output format, it's a module for use in others. It allows
|
4
|
+
# us to create JavaScript Library friendly markup, for things like LazyLoad
|
5
|
+
module DataAttributes
|
6
|
+
def base_markup
|
7
|
+
build_noscript(super)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def add_src(element, name)
|
13
|
+
element.attributes << { 'data-src' => PictureTag.build_url(name) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_srcset(element, srcset)
|
17
|
+
element.attributes << { 'data-srcset' => srcset.to_s }
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_sizes(element, srcset)
|
21
|
+
element.attributes << { 'data-sizes' => srcset.sizes } if srcset.sizes
|
22
|
+
end
|
23
|
+
|
24
|
+
def build_noscript(base_content)
|
25
|
+
return base_content unless PictureTag.preset['noscript']
|
26
|
+
|
27
|
+
noscript = DoubleTag.new(
|
28
|
+
'noscript',
|
29
|
+
content: Img.new.build_base_img,
|
30
|
+
|
31
|
+
# Markdown fix requires removal of line breaks:
|
32
|
+
oneline: PictureTag.nomarkdown?
|
33
|
+
).to_s
|
34
|
+
|
35
|
+
ShelfTag.new(
|
36
|
+
content: [base_content, noscript],
|
37
|
+
|
38
|
+
# Markdown fix requires removal of line breaks:
|
39
|
+
oneline: PictureTag.nomarkdown?
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module PictureTag
|
2
|
+
module OutputFormats
|
3
|
+
# Similar to Auto, but sets data-src (and so on) instead of src
|
4
|
+
class DataAuto < Auto
|
5
|
+
def self.new
|
6
|
+
if PictureTag::Utils.count_srcsets > 1
|
7
|
+
DataPicture.new
|
8
|
+
else
|
9
|
+
DataImg.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|