sutty-liquid 0.8.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9eecb00f3cfa8234ca077bddd1201be34e5490f8ebd915fd7b8988b0bc0e8347
4
- data.tar.gz: 133733b0369fa579d0fcfd1b64ab89a8ab46b2ebbb70e1341f916c02cb66cbf7
3
+ metadata.gz: f89b4f4a0b3dc57f4720491bd1245b63e48a018eb059f1e1c065178e8d0ab185
4
+ data.tar.gz: 797a226386270f608022b0c9ff89402a17b0806fba9e409e0fb99eff2bdad475
5
5
  SHA512:
6
- metadata.gz: 4e5c7ba1221f73a4ae42b59d4ad0f3d021a630617649d8412deab39ec06d3929286c85758a53ebc8edc44daa8d1b04def206b126ec848c9524b51907836bcd92
7
- data.tar.gz: b371f3d5db4c81ac5b208fbd2491c15a25fc86315faa4ef6525e9dfcb522244350a0d649fbd0522cdf42ae424624d40b85edcb85a57b307ecda69a013d96bfe9
6
+ metadata.gz: 924ba6d9c6d01f1f3b313782911de7ce751e7dab63fbfd99cb18ccbec9f34ee2fe30ff9a65a5cc7e5b203ac3a90a2b094d89606a0247905322d9d611dd175eda
7
+ data.tar.gz: d2b3c2c91a51311313cff91b82ca752d680eb54b12145187bbeaa0771e0473fe561e3995c583bcbdb78e5297a67b42fae58bdeb427ef30dfaa8e5dc8c8aa6c5b
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module Arrays
6
+ # Returns one or several random items from an Array.
7
+ #
8
+ # @example
9
+ # {{ site.posts | sample }}
10
+ # {{ site.posts | sample: 3 }}
11
+ # @param input [Array]
12
+ # @param amount [Integer]
13
+ # @return [Any]
14
+ def sample(input, amount = 1)
15
+ input = [] unless array? input
16
+
17
+ input.sample(amount)
18
+ end
19
+
20
+ # Join arrays or append item to array
21
+ #
22
+ # @example
23
+ # {% assign things = site.posts | where: "layout", "thing" %}
24
+ # {% assign being = site.posts | find: "layout", "being" %}
25
+ # {% assign dread = things | concat: being %}
26
+ #
27
+ # @param input [Array]
28
+ # @param concatenable [Array,any]
29
+ def join(input, concatenable)
30
+ input = [] unless array? input
31
+
32
+ if concatenable.is_a? Array
33
+ input + concatenable
34
+ else
35
+ input.dup << concatenable
36
+ end
37
+ end
38
+
39
+ # Finds the next posts in a collection, starting from current
40
+ # post and restarts from the beginning to always return posts.
41
+ #
42
+ # @example
43
+ # {{ page | infinite_next: site.posts }}
44
+ # @param input [Jekyll::Document,Drop] The current post (page)
45
+ # @param posts [Array] An array of posts (ie. results from where filter)
46
+ # @param amount [Integer] Amount of posts next to current, max is posts size
47
+ # @return [Array<Jekyll::Document,Drop>] Array of posts
48
+ def infinite_next(input, posts, amount = 1)
49
+ posts = [] unless array? posts
50
+
51
+ liquid_input = input.to_liquid
52
+ liquid_posts = posts.map(&:to_liquid)
53
+ index = find_in_stack(liquid_input, liquid_posts)
54
+ liquid_posts.rotate(index).slice(1, amount) if index
55
+ end
56
+
57
+ # Finds the previous posts in a collection, starting from current
58
+ # post and restarts from the beginning to always return posts.
59
+ #
60
+ # @example
61
+ # {{ page | infinite_next: site.posts }}
62
+ # @param input [Jekyll::Document,Drop] The current post (page)
63
+ # @param posts [Array<Jekyll::Document,Drop>] An array of posts (ie. results from where filter)
64
+ # @param amount [Integer] Amount of posts previous to current, max is posts size
65
+ # @return [Array<Jekyll::Document,Drop>] Array of posts
66
+ def infinite_prev(input, posts, amount = 1)
67
+ posts = [] unless array? posts
68
+
69
+ liquid_input = input.to_liquid
70
+ liquid_posts = posts.map(&:to_liquid)
71
+ index = find_in_stack(liquid_input, liquid_posts)
72
+ liquid_posts.rotate(index).reverse.slice(0, amount).reverse if index
73
+ end
74
+
75
+ # Return next post from an array or nothing if post is last.
76
+ #
77
+ # @example
78
+ # {{ page | next: site.posts }}
79
+ # @param input [Jekyll::Document,Drop]
80
+ # @param posts [Array<Jekyll::Document,Drop>]
81
+ # @return [Drop,nil]
82
+ def next(input, posts)
83
+ posts = [] unless array? posts
84
+
85
+ liquid_input = input.to_liquid
86
+ liquid_posts = posts.map(&:to_liquid)
87
+ index = find_in_stack(liquid_input, liquid_posts)
88
+ liquid_posts[index + 1] if index
89
+ end
90
+
91
+ # Return previous post from an array, or nothing if post is the
92
+ # first item.
93
+ #
94
+ # @example
95
+ # {{ page | prev: site.posts }}
96
+ # {{ page | previous: site.posts }}
97
+ # @param input [Jekyll::Document,Drop]
98
+ # @param array [Array]
99
+ # @return [Drop,nil]
100
+ def prev(input, posts)
101
+ posts = [] unless array? posts
102
+
103
+ liquid_input = input.to_liquid
104
+ liquid_posts = posts.map(&:to_liquid)
105
+ index = find_in_stack(liquid_input, liquid_posts)
106
+ liquid_posts[index - 1] if index && index > 0
107
+ end
108
+
109
+ alias_method :previous, :prev
110
+
111
+ private
112
+
113
+ # Detects if param is an array or raise an error if strict filters
114
+ # is enabled
115
+ #
116
+ # @param posts [Array]
117
+ # @return [Boolean]
118
+ def array?(posts)
119
+ posts.is_a?(Array).tap do |a|
120
+ next if a
121
+ next unless @context.strict_filters
122
+
123
+ raise Liquid::ArgumentError.new('needs an array argument')
124
+ end
125
+ end
126
+
127
+ # Find post in posts, raise an error if strict_filters is enabled
128
+ #
129
+ # @param input [Drop]
130
+ # @param posts [Array] array of drops
131
+ # @return [Integer]
132
+ def find_in_stack(input, posts)
133
+ posts.index(input).tap do |n|
134
+ next if n
135
+ next unless @context.strict_filters
136
+
137
+ title = input['title'] || input['id'] || 'no title'
138
+ titles = posts.map do |p|
139
+ p['title'] || p['id'] || 'no title'
140
+ end
141
+
142
+ raise Liquid::ArgumentError.new("can't find #{title} in #{titles.join(', ')}")
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ Liquid::Template.register_filter(Jekyll::Filters::Arrays)
@@ -123,7 +123,7 @@ module Jekyll
123
123
  input.blank?
124
124
  when Array then input.compact.empty?
125
125
  when Hash then input.compact.empty?
126
- else input.respond_to?(:empty?) ? input.empty? : !!input
126
+ else input.respond_to?(:empty?) ? input.empty? : !!!input
127
127
  end
128
128
  end
129
129
 
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jekyll
4
+ module Filters
5
+ module Content
6
+ HEADINGS = 'h1,h2,h3,h4,h5,h6'
7
+
8
+ # Add id attribute to all headings with an optional prefix. It
9
+ # also creates a table of contents with unique IDs.
10
+ #
11
+ # It doesn't change pre-existing IDs.
12
+ #
13
+ # @example Render content with unique IDs
14
+ # {{ content | canonicalize_headings }}
15
+ # @param input [String] HTML string
16
+ # @param prefix [nil,String] Prefix IDs with this
17
+ # @return [String]
18
+ def canonicalize_headings(input, prefix = nil)
19
+ @@canonicalize_headings ||= {}
20
+ @@canonicalize_headings[input.hash] ||=
21
+ begin
22
+ require 'nokogiri'
23
+
24
+ toc = toc(input)
25
+
26
+ html_fragment(input).tap do |html|
27
+ html.css(HEADINGS).each do |h|
28
+ id = h['id']
29
+
30
+ unless id
31
+ slug = ::Jekyll::Utils.slugify(h.text, mode: 'pretty')
32
+ id = unique_id(slug, prefix, toc)
33
+ h['id'] = id
34
+ end
35
+
36
+ toc[id] = {
37
+ 'level' => h.name[1].to_i,
38
+ 'title' => h.text,
39
+ 'id' => id
40
+ }
41
+ end
42
+ end.to_s
43
+ end
44
+ end
45
+
46
+ # Extracts a table of contents from HTML up to a certain headings
47
+ # level.
48
+ #
49
+ # It returns an array of hashes with title, level and id.
50
+ #
51
+ # @example Create a table of contents
52
+ # {% assign toc = content | table_of_contents: 2 %}
53
+ #
54
+ # {% unless toc == empty %}
55
+ # {% for item in toc %}
56
+ # {% assign heading = "h" | append: item.level %}
57
+ # <{{ heading }}>
58
+ # <a href="#{{ item.id }}">
59
+ # {{ item.title }}
60
+ # </a>
61
+ # </{{ heading }}>
62
+ # {% endfor %}
63
+ # {% endunless %}
64
+ # @param input [String]
65
+ # @param max_level [Integer] All headings up to this level
66
+ # @param prefix [String,nil] Prefix
67
+ # @return [Hash]
68
+ def table_of_contents(input, max_level = 2, prefix = nil)
69
+ canonicalize_headings(input, prefix)
70
+
71
+ toc(input).select do |id, item|
72
+ item['level'] <= max_level
73
+ end.values
74
+ end
75
+
76
+ private
77
+
78
+ # Parses and caches an HTML5 fragment
79
+ #
80
+ # @param input [String]
81
+ # @return [Nokogiri::HTML5::Fragment]
82
+ def html_fragment(input)
83
+ @@html_fragment ||= {}
84
+ @@html_fragment[input.hash] ||= Nokogiri::HTML5.fragment(input.to_s)
85
+ end
86
+
87
+ # @param input [String]
88
+ # @return [Hash]
89
+ def toc(input)
90
+ @@toc ||= {}
91
+ @@toc[input.hash] ||= {}
92
+ end
93
+
94
+ # Apply a prefix to an ID unless it's already applied
95
+ #
96
+ # @param id [String]
97
+ # @param prefix [nil,String]
98
+ # @return [String]
99
+ def prefix_id(id, prefix)
100
+ return id if prefix.nil?
101
+ return id if id.start_with? "#{prefix}-"
102
+
103
+ "#{prefix}-#{id}"
104
+ end
105
+
106
+ # Obtains a unique ID for TOC
107
+ #
108
+ # @param id [String]
109
+ # @param prefix [String,nil]
110
+ # @param toc [Hash]
111
+ # @return [String]
112
+ def unique_id(id, prefix, toc)
113
+ prefixed_id = prefix_id id, prefix
114
+
115
+ return prefixed_id unless toc.key? prefixed_id
116
+
117
+ require 'securerandom'
118
+
119
+ loop do
120
+ unique = "#{prefixed_id}-#{SecureRandom.hex(3)}"
121
+
122
+ break unique unless toc.key? unique
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ Liquid::Template.register_filter(Jekyll::Filters::Content)
@@ -61,6 +61,20 @@ module Jekyll
61
61
  .gsub(star_delimiter, '*')
62
62
  .gsub(escaped_delimiter, '\*')
63
63
  end
64
+
65
+ def unescape(input)
66
+ CGI.unescapeHTML input.to_s
67
+ end
68
+
69
+ # Escapes a string by percent encoding all reserved characters
70
+ #
71
+ # @param :input [Any]
72
+ # @return [String]
73
+ def component_escape(input)
74
+ require 'addressable'
75
+
76
+ Addressable::URI.encode_component input.to_s, Addressable::URI::CharacterClasses::UNRESERVED
77
+ end
64
78
  end
65
79
  end
66
80
  end
data/lib/sutty-liquid.rb CHANGED
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'jekyll/filters/arrays'
3
4
  require_relative 'jekyll/filters/assertion'
4
5
  require_relative 'jekyll/filters/compact'
6
+ require_relative 'jekyll/filters/content'
5
7
  require_relative 'jekyll/filters/date_local'
6
- require_relative 'jekyll/filters/infinite'
7
8
  require_relative 'jekyll/filters/file'
8
9
  require_relative 'jekyll/filters/json'
9
10
  require_relative 'jekyll/filters/menu'
10
11
  require_relative 'jekyll/filters/number'
11
- require_relative 'jekyll/filters/sample'
12
12
  require_relative 'jekyll/filters/strings'
13
13
  require_relative 'jekyll/filters/social_network'
14
14
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sutty-liquid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.10.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - f
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
11
+ date: 2022-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.13'
83
+ - !ruby/object:Gem::Dependency
84
+ name: nokogiri
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
83
111
  description: An assorment of Liquid filters and tags for Jekyll used in Sutty
84
112
  email:
85
113
  - f@sutty.nl
@@ -92,16 +120,16 @@ files:
92
120
  - LICENSE.txt
93
121
  - README.md
94
122
  - lib/jekyll/drops/url_drop_decorator.rb
123
+ - lib/jekyll/filters/arrays.rb
95
124
  - lib/jekyll/filters/assertion.rb
96
125
  - lib/jekyll/filters/compact.rb
126
+ - lib/jekyll/filters/content.rb
97
127
  - lib/jekyll/filters/date_local.rb
98
128
  - lib/jekyll/filters/file.rb
99
- - lib/jekyll/filters/infinite.rb
100
129
  - lib/jekyll/filters/json.rb
101
130
  - lib/jekyll/filters/menu.rb
102
131
  - lib/jekyll/filters/number.rb
103
132
  - lib/jekyll/filters/pry.rb
104
- - lib/jekyll/filters/sample.rb
105
133
  - lib/jekyll/filters/social_network.rb
106
134
  - lib/jekyll/filters/strings.rb
107
135
  - lib/jekyll/tags/pry.rb
@@ -137,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
137
165
  - !ruby/object:Gem::Version
138
166
  version: '0'
139
167
  requirements: []
140
- rubygems_version: 3.3.5
168
+ rubygems_version: 3.3.15
141
169
  signing_key:
142
170
  specification_version: 4
143
171
  summary: Liquid filters
@@ -1,20 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- module Filters
5
- module Infinite
6
- #
7
- def infinite_next(input, posts, amount)
8
- liquid = posts.map(&:to_liquid)
9
- liquid.rotate(liquid.index(input)).slice(1, amount)
10
- end
11
-
12
- def infinite_prev(input, posts, amount)
13
- liquid = posts.map(&:to_liquid)
14
- liquid.rotate(liquid.index(input)).reverse.slice(0, amount).reverse
15
- end
16
- end
17
- end
18
- end
19
-
20
- Liquid::Template.register_filter(Jekyll::Filters::Infinite)
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Jekyll
4
- module Filters
5
- module Sample
6
- # Returns a random item from an Array.
7
- #
8
- # Example usage:
9
- #
10
- # {{ site.posts | sample }}
11
- # {{ site.posts | sample: 3 }}
12
- #
13
- # @input [Array]
14
- # @return [Any]
15
- def sample(input, amount = 1)
16
- return unless input.respond_to? :sample
17
-
18
- input.sample(amount)
19
- end
20
- end
21
- end
22
- end
23
-
24
- Liquid::Template.register_filter(Jekyll::Filters::Sample)