j1_paginator 2019.1.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.
@@ -0,0 +1,377 @@
1
+ module Jekyll
2
+ module J1Paginator::Generator
3
+
4
+ #
5
+ # The main model for the pagination, handles the orchestration of the pagination and calling all the necessary bits and bobs needed :)
6
+ #
7
+ class PaginationModel
8
+
9
+ @debug = false # is debug output enabled?
10
+ @logging_lambda = nil # The lambda to use for logging
11
+ @page_add_lambda = nil # The lambda used to create pages and add them to the site
12
+ @page_remove_lambda = nil # Lambda to remove a page from the site.pages collection
13
+ @collection_by_name_lambda = nil # Lambda to get all documents/posts in a particular collection (by name)
14
+
15
+ # ctor
16
+ def initialize(logging_lambda, page_add_lambda, page_remove_lambda, collection_by_name_lambda)
17
+ @logging_lambda = logging_lambda
18
+ @page_add_lambda = page_add_lambda
19
+ @page_remove_lambda = page_remove_lambda
20
+ @collection_by_name_lambda = collection_by_name_lambda
21
+ end
22
+
23
+
24
+ def run(default_config, site_pages, site_title)
25
+ # By default if pagination is enabled we attempt to find all index.html pages in the site
26
+ templates = self.discover_paginate_templates(site_pages)
27
+ if templates.size <= 0
28
+ @logging_lambda.call "Is enabled, but I couldn't find any pagination page. Skipping pagination. "+
29
+ "Pages must have 'pagination: enabled: true' in their front-matter for pagination to work.", "warn"
30
+ return
31
+ end
32
+
33
+ # Now for each template page generate the paginator for it
34
+ templates.each do |template|
35
+ # All pages that should be paginated need to include the pagination config element
36
+ if template.data['pagination'].is_a?(Hash)
37
+ template_config = Jekyll::Utils.deep_merge_hashes(default_config, template.data['pagination'] || {})
38
+
39
+ # Handling deprecation of configuration values
40
+ self._fix_deprecated_config_features(template_config)
41
+
42
+ @debug = template_config['debug'] # Is debugging enabled on the page level
43
+
44
+ self._debug_print_config_info(template_config, template.path)
45
+
46
+ # Only paginate the template if it is explicitly enabled
47
+ # This makes the logic simpler by avoiding the need to determine which index pages
48
+ # were generated automatically and which weren't
49
+ if template_config['enabled'].to_s == 'true'
50
+ if !@debug
51
+ @logging_lambda.call "found page: "+template.path, 'debug'
52
+ end
53
+
54
+ # Request all documents in all collections that the user has requested
55
+ all_posts = self.get_docs_in_collections(template_config['collection'])
56
+
57
+ # Create the necessary indexes for the posts
58
+ all_categories = PaginationIndexer.index_posts_by(all_posts, 'categories')
59
+ all_categories['posts'] = all_posts; # Populate a category for all posts (this is here for backward compatibility, do not use this as it will be decommissioned 2018-01-01)
60
+ # (this is a default and must not be used in the category system)
61
+ all_tags = PaginationIndexer.index_posts_by(all_posts, 'tags')
62
+ all_locales = PaginationIndexer.index_posts_by(all_posts, 'locale')
63
+
64
+ # TODO: NOTE!!! This whole request for posts and indexing results could be cached to improve performance, leaving like this for now during testing
65
+
66
+ # Now construct the pagination data for this template page
67
+ self.paginate(template, template_config, site_title, all_posts, all_tags, all_categories, all_locales)
68
+ end
69
+ end
70
+ end #for
71
+
72
+ # Return the total number of templates found
73
+ return templates.size
74
+ end # function run
75
+
76
+ #
77
+ # This function is here to retain the old compatability logic with the jekyll-paginate gem
78
+ # no changes should be made to this function and it should be retired and deleted after 2018-01-01
79
+ # (REMOVE AFTER 2018-01-01)
80
+ #
81
+ def run_compatability(legacy_config, site_pages, site_title, all_posts)
82
+
83
+ # Decomissioning error
84
+ if( date = Date.strptime("20180101","%Y%m%d") <= Date.today )
85
+ raise ArgumentError.new("Legacy jekyll-paginate configuration compatibility mode has expired. Please upgrade to j1_paginator configuration.")
86
+ end
87
+
88
+ # Two month warning or general notification
89
+ if( date = Date.strptime("20171101","%Y%m%d") <= Date.today )
90
+ @logging_lambda.call "Legacy pagination logic will stop working on Jan 1st 2018, update your configs before that time.", "warn"
91
+ else
92
+ @logging_lambda.call "Detected legacy jekyll-paginate logic running. "+
93
+ "Please update your configs to use the j1_paginator logic. This compatibility function "+
94
+ "will stop working after Jan 1st 2018 and your site build will throw an error.", "warn"
95
+ end
96
+
97
+ if template = CompatibilityUtils.template_page(site_pages, legacy_config['legacy_source'], legacy_config['permalink'])
98
+ CompatibilityUtils.paginate(legacy_config, all_posts, template, @page_add_lambda)
99
+ else
100
+ @logging_lambda.call "Legacy pagination is enabled, but I couldn't find " +
101
+ "an index.html page to use as the pagination page. Skipping pagination.", "warn"
102
+ end
103
+ end # function run_compatability (REMOVE AFTER 2018-01-01)
104
+
105
+ # Returns the combination of all documents in the collections that are specified
106
+ # raw_collection_names can either be a list of collections separated by a ',' or ' ' or a single string
107
+ def get_docs_in_collections(raw_collection_names)
108
+ if raw_collection_names.is_a?(String)
109
+ collection_names = raw_collection_names.split(/;|,|\s/)
110
+ else
111
+ collection_names = raw_collection_names
112
+ end
113
+
114
+ docs = []
115
+ # Now for each of the collections get the docs
116
+ collection_names.each do |coll_name|
117
+ # Request all the documents for the collection in question, and join it with the total collection
118
+ docs += @collection_by_name_lambda.call(coll_name.downcase.strip)
119
+ end
120
+
121
+ # Hidden documents should not not be processed anywhere.
122
+ docs = docs.reject { |doc| doc['hidden'] }
123
+
124
+ return docs
125
+ end
126
+
127
+ def _fix_deprecated_config_features(config)
128
+ keys_to_delete = []
129
+
130
+ # As of v1.5.1 the title_suffix is deprecated and 'title' should be used
131
+ # but only if title has not been defined already!
132
+ if( !config['title_suffix'].nil? )
133
+ if( config['title'].nil? )
134
+ config['title'] = ":title" + config['title_suffix'].to_s # Migrate the old key to title
135
+ end
136
+ keys_to_delete << "title_suffix" # Always remove the depricated key if found
137
+ end
138
+
139
+ # Delete the depricated keys
140
+ config.delete_if{ |k,| keys_to_delete.include? k }
141
+ end
142
+
143
+ LOG_KEY = 'Pagination: '.rjust(20).freeze
144
+ DIVIDER = ('-' * 80).freeze
145
+ NOT_SET = '[Not set]'.freeze
146
+
147
+ # Debug print the config
148
+ def _debug_log(topic, message = nil)
149
+ return unless @debug
150
+
151
+ message = message.to_s
152
+ topic = "#{topic.ljust(24)}: " unless message.empty?
153
+ puts LOG_KEY + topic + message
154
+ end
155
+
156
+ # Debug print the config
157
+ def _debug_print_config_info(config, page_path)
158
+ return unless @debug
159
+
160
+ puts ''
161
+ puts LOG_KEY + "Page: #{page_path}"
162
+ puts LOG_KEY + DIVIDER
163
+ _debug_log ' Active configuration'
164
+ _debug_log ' Enabled', config['enabled']
165
+ _debug_log ' Items per page', config['per_page']
166
+ _debug_log ' Permalink', config['permalink']
167
+ _debug_log ' Title', config['title']
168
+ _debug_log ' Limit', config['limit']
169
+ _debug_log ' Sort by', config['sort_field']
170
+ _debug_log ' Sort reverse', config['sort_reverse']
171
+ _debug_log ' Active Filters'
172
+ _debug_log ' Collection', config['collection']
173
+ _debug_log ' Offset', config['offset']
174
+ _debug_log ' Category', (config['category'].nil? || config['category'] == 'posts' ? NOT_SET : config['category'])
175
+ _debug_log ' Tag', config['tag'] || NOT_SET
176
+ _debug_log ' Locale', config['locale'] || NOT_SET
177
+
178
+ return unless config['legacy']
179
+
180
+ _debug_log ' Legacy Paginate Code Enabled'
181
+ _debug_log ' Legacy Paginate', config['per_page']
182
+ _debug_log ' Legacy Source', config['legacy_source']
183
+ _debug_log ' Legacy Path', config['paginate_path']
184
+ end
185
+
186
+ # Debug print the config
187
+ def _debug_print_filtering_info(filter_name, before_count, after_count)
188
+ return unless @debug
189
+
190
+ filter_name = filter_name.to_s.ljust(9)
191
+ before_count = before_count.to_s.rjust(3)
192
+ _debug_log " Filtering by #{filter_name}", "#{before_count} => #{after_count}"
193
+ end
194
+
195
+ #
196
+ # Rolls through all the pages passed in and finds all pages that have pagination enabled on them.
197
+ # These pages will be used as templates
198
+ #
199
+ # site_pages - All pages in the site
200
+ #
201
+ def discover_paginate_templates(site_pages)
202
+ candidates = []
203
+ site_pages.select do |page|
204
+ # If the page has the enabled config set, supports any type of file name html or md
205
+ if page.data['pagination'].is_a?(Hash) && page.data['pagination']['enabled']
206
+ candidates << page
207
+ end
208
+ end
209
+ return candidates
210
+ end # function discover_paginate_templates
211
+
212
+ # Paginates the blog's posts. Renders the index.html file into paginated
213
+ # directories, e.g.: page2/index.html, page3/index.html, etc and adds more
214
+ # site-wide data.
215
+ #
216
+ # site - The Site.
217
+ # template - The index.html Page that requires pagination.
218
+ # config - The configuration settings that should be used
219
+ #
220
+ def paginate(template, config, site_title, all_posts, all_tags, all_categories, all_locales)
221
+ # By default paginate on all posts in the site
222
+ using_posts = all_posts
223
+
224
+ # Now start filtering out any posts that the user doesn't want included in the pagination
225
+ before = using_posts.size
226
+ using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'category', using_posts, all_categories)
227
+ self._debug_print_filtering_info('Category', before, using_posts.size)
228
+ before = using_posts.size
229
+ using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'tag', using_posts, all_tags)
230
+ self._debug_print_filtering_info('Tag', before, using_posts.size)
231
+ before = using_posts.size
232
+ using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'locale', using_posts, all_locales)
233
+ self._debug_print_filtering_info('Locale', before, using_posts.size)
234
+
235
+ # Apply sorting to the posts if configured, any field for the post is available for sorting
236
+ if config['sort_field']
237
+ sort_field = config['sort_field'].to_s
238
+
239
+ # There is an issue in Jekyll related to lazy initialized member variables that causes iterators to
240
+ # break when accessing an uninitialized value during iteration. This happens for document.rb when the <=> compaison function
241
+ # is called (as this function calls the 'date' field which for drafts are not initialized.)
242
+ # So to unblock this common issue for the date field I simply iterate once over every document and initialize the .date field explicitly
243
+ if @debug
244
+ Jekyll.logger.info "Pagination:", "Rolling through the date fields for all documents"
245
+ end
246
+ using_posts.each do |u_post|
247
+ if u_post.respond_to?('date')
248
+ tmp_date = u_post.date
249
+ if( !tmp_date || tmp_date.nil? )
250
+ if @debug
251
+ Jekyll.logger.info "Pagination:", "Explicitly assigning date for doc: #{u_post.data['title']} | #{u_post.path}"
252
+ end
253
+ u_post.date = File.mtime(u_post.path)
254
+ end
255
+ end
256
+ end
257
+
258
+ using_posts.sort!{ |a,b| Utils.sort_values(Utils.sort_get_post_data(a.data, sort_field), Utils.sort_get_post_data(b.data, sort_field)) }
259
+
260
+ # Remove the first x entries
261
+ offset_post_count = [0, config['offset'].to_i].max
262
+ using_posts.pop(offset_post_count)
263
+
264
+ if config['sort_reverse']
265
+ using_posts.reverse!
266
+ end
267
+ end
268
+
269
+ # Calculate the max number of pagination-pages based on the configured per page value
270
+ total_pages = Utils.calculate_number_of_pages(using_posts, config['per_page'])
271
+
272
+ # If a upper limit is set on the number of total pagination pages then impose that now
273
+ if config['limit'] && config['limit'].to_i > 0 && config['limit'].to_i < total_pages
274
+ total_pages = config['limit'].to_i
275
+ end
276
+
277
+ #### BEFORE STARTING REMOVE THE TEMPLATE PAGE FROM THE SITE LIST!
278
+ @page_remove_lambda.call( template )
279
+
280
+ # list of all newly created pages
281
+ newpages = []
282
+
283
+ # Consider the default index page name and extension
284
+ indexPageName = config['indexpage'].nil? ? '' : config['indexpage'].split('.')[0]
285
+ indexPageExt = config['extension'].nil? ? '' : Utils.ensure_leading_dot(config['extension'])
286
+ indexPageWithExt = indexPageName + indexPageExt
287
+
288
+ # In case there are no (visible) posts, generate the index file anyway
289
+ total_pages = 1 if total_pages.zero?
290
+
291
+ # Now for each pagination page create it and configure the ranges for the collection
292
+ # This .pager member is a built in thing in Jekyll and defines the paginator implementation
293
+ # Simpy override to use mine
294
+ (1..total_pages).each do |cur_page_nr|
295
+
296
+ # 1. Create the in-memory page
297
+ # External Proc call to create the actual page for us (this is passed in when the pagination is run)
298
+ newpage = PaginationPage.new( template, cur_page_nr, total_pages, indexPageWithExt )
299
+
300
+ # 2. Create the url for the in-memory page (calc permalink etc), construct the title, set all page.data values needed
301
+ first_index_page_url = Utils.validate_url(template)
302
+ paginated_page_url = File.join(first_index_page_url, config['permalink'])
303
+
304
+ # 3. Create the pager logic for this page, pass in the prev and next page numbers, assign pager to in-memory page
305
+ newpage.pager = Paginator.new( config['per_page'], first_index_page_url, paginated_page_url, using_posts, cur_page_nr, total_pages, indexPageName, indexPageExt)
306
+
307
+ # Create the url for the new page, make sure we prepend any permalinks that are defined in the template page before
308
+ pager_path = newpage.pager.page_path
309
+ if pager_path.end_with? '/'
310
+ newpage.url = File.join(pager_path, indexPageWithExt)
311
+ elsif pager_path.end_with? indexPageExt
312
+ # Support for direct .html files
313
+ newpage.url = pager_path
314
+ else
315
+ # Support for extensionless permalinks
316
+ newpage.url = pager_path + indexPageExt
317
+ end
318
+
319
+ if( template.data['permalink'] )
320
+ newpage.data['permalink'] = pager_path
321
+ end
322
+
323
+ # Transfer the title across to the new page
324
+ tmp_title = template.data['title'] || site_title
325
+ if cur_page_nr > 1 && config.has_key?('title')
326
+ # If the user specified a title suffix to be added then let's add that to all the pages except the first
327
+ newpage.data['title'] = "#{Utils.format_page_title(config['title'], tmp_title, cur_page_nr, total_pages)}"
328
+ else
329
+ newpage.data['title'] = tmp_title
330
+ end
331
+
332
+ # Signals that this page is automatically generated by the pagination logic
333
+ # (we don't do this for the first page as it is there to mask the one we removed)
334
+ if cur_page_nr > 1
335
+ newpage.data['autogen'] = "j1_paginator"
336
+ end
337
+
338
+ # Add the page to the site
339
+ @page_add_lambda.call( newpage )
340
+
341
+ # Store the page in an internal list for later referencing if we need to generate a pagination number path later on
342
+ newpages << newpage
343
+ end #each.do total_pages
344
+
345
+ # Now generate the pagination number path, e.g. so that the users can have a prev 1 2 3 4 5 next structure on their page
346
+ # simplest is to include all of the links to the pages preceeding the current one
347
+ # (e.g for page 1 you get the list 2, 3, 4.... and for page 2 you get the list 3,4,5...)
348
+ if config['trail'] && newpages.size > 1
349
+ trail_before = [config['trail']['before'].to_i, 0].max
350
+ trail_after = [config['trail']['after'].to_i, 0].max
351
+ trail_length = trail_before + trail_after + 1
352
+
353
+ if( trail_before > 0 || trail_after > 0 )
354
+ newpages.select do | npage |
355
+ idx_start = [ npage.pager.page - trail_before - 1, 0].max # Selecting the beginning of the trail
356
+ idx_end = [idx_start + trail_length, newpages.size].min # Selecting the end of the trail
357
+
358
+ # Always attempt to maintain the max total of <trail_length> pages in the trail (it will look better if the trail doesn't shrink)
359
+ if( idx_end - idx_start < trail_length )
360
+ # Attempt to pad the beginning if we have enough pages
361
+ idx_start = [idx_start - ( trail_length - (idx_end - idx_start) ), 0].max # Never go beyond the zero index
362
+ end
363
+
364
+ # Convert the newpages array into a two dimensional array that has [index, page_url] as items
365
+ #puts( "Trail created for page #{npage.pager.page} (idx_start:#{idx_start} idx_end:#{idx_end})")
366
+ npage.pager.page_trail = newpages[idx_start...idx_end].each_with_index.map {|ipage,idx| PageTrail.new(idx_start+idx+1, ipage.pager.page_path, ipage.data['title'])}
367
+ #puts( npage.pager.page_trail )
368
+ end #newpages.select
369
+ end #if trail_before / trail_after
370
+ end # if config['trail']
371
+
372
+ end # function paginate
373
+
374
+ end # class PaginationV2
375
+
376
+ end # module J1Paginator
377
+ end # module Jekyll
@@ -0,0 +1,66 @@
1
+ module Jekyll
2
+ module J1Paginator::Generator
3
+
4
+ #
5
+ # This page handles the creation of the fake pagination pages based on the original page configuration
6
+ # The code does the same things as the default Jekyll/page.rb code but just forces the code to look
7
+ # into the template instead of the (currently non-existing) pagination page.
8
+ #
9
+ # This page exists purely in memory and is not read from disk
10
+ #
11
+ class PaginationPage < Page
12
+ attr_reader :relative_path
13
+
14
+ def initialize(page_to_copy, cur_page_nr, total_pages, index_pageandext)
15
+ @site = page_to_copy.site
16
+ @base = ''
17
+ @url = ''
18
+ @relative_path = page_to_copy.relative_path
19
+
20
+ if cur_page_nr == 1
21
+ @dir = File.dirname(page_to_copy.dir)
22
+ @name = page_to_copy.name
23
+ else
24
+ @name = index_pageandext.nil? ? 'index.html' : index_pageandext
25
+ end
26
+
27
+ self.process(@name) # Creates the basename and ext member values
28
+
29
+ # Copy page data over site defaults
30
+ defaults = @site.frontmatter_defaults.all(page_to_copy.relative_path, type)
31
+ self.data = Jekyll::Utils.deep_merge_hashes(defaults, page_to_copy.data)
32
+
33
+ if defaults.has_key?('permalink')
34
+ self.data['permalink'] = Jekyll::URL.new(:template => defaults['permalink'], :placeholders => self.url_placeholders).to_s
35
+ @use_permalink_for_url = true
36
+ end
37
+
38
+ if !page_to_copy.data['autopage']
39
+ self.content = page_to_copy.content
40
+ else
41
+ # If the page is an auto page then migrate the necessary autopage info across into the
42
+ # new pagination page (so that users can get the correct keys etc)
43
+ if( page_to_copy.data['autopage'].has_key?('display_name') )
44
+ self.data['autopages'] = Jekyll::Utils.deep_merge_hashes( page_to_copy.data['autopage'], {} )
45
+ end
46
+ end
47
+
48
+ # Store the current page and total page numbers in the pagination_info construct
49
+ self.data['pagination_info'] = {"curr_page" => cur_page_nr, 'total_pages' => total_pages }
50
+
51
+ # Perform some validation that is also performed in Jekyll::Page
52
+ validate_data! page_to_copy.path
53
+ validate_permalink! page_to_copy.path
54
+
55
+ # Trigger a page event
56
+ #Jekyll::Hooks.trigger :pages, :post_init, self
57
+ end
58
+
59
+ def url=(url_value)
60
+ @url = @use_permalink_for_url ? self.data['permalink'] : url_value
61
+ end
62
+ alias_method :set_url, :url=
63
+ end # class PaginationPage
64
+
65
+ end # module J1Paginator
66
+ end # module Jekyll
@@ -0,0 +1,107 @@
1
+ module Jekyll
2
+ module J1Paginator::Generator
3
+
4
+ #
5
+ # Handles the preparation of all the posts based on the current page index
6
+ #
7
+ class Paginator
8
+ attr_reader :page, :per_page, :posts, :total_posts, :total_pages,
9
+ :previous_page, :previous_page_path, :next_page, :next_page_path, :page_path, :page_trail,
10
+ :first_page, :first_page_path, :last_page, :last_page_path
11
+
12
+ def page_trail=(page_array)
13
+ @page_trail = page_array
14
+ end
15
+
16
+ # Initialize a new Paginator.
17
+ #
18
+ def initialize(config_per_page, first_index_page_url, paginated_page_url, posts, cur_page_nr, num_pages, default_indexpage, default_ext)
19
+ @page = cur_page_nr
20
+ @per_page = config_per_page.to_i
21
+ @total_pages = num_pages
22
+
23
+ if @page > @total_pages
24
+ raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
25
+ end
26
+
27
+ init = (@page - 1) * @per_page
28
+ offset = (init + @per_page - 1) >= posts.size ? posts.size : (init + @per_page - 1)
29
+
30
+ # Ensure that the current page has correct extensions if needed
31
+ this_page_url = Utils.ensure_full_path(@page == 1 ? first_index_page_url : paginated_page_url,
32
+ !default_indexpage || default_indexpage.length == 0 ? 'index' : default_indexpage,
33
+ !default_ext || default_ext.length == 0 ? '.html' : default_ext)
34
+
35
+ # To support customizable pagination pages we attempt to explicitly append the page name to
36
+ # the url incase the user is using extensionless permalinks.
37
+ if default_indexpage && default_indexpage.length > 0
38
+ # Adjust first page url
39
+ first_index_page_url = Utils.ensure_full_path(first_index_page_url, default_indexpage, default_ext)
40
+ # Adjust the paginated pages as well
41
+ paginated_page_url = Utils.ensure_full_path(paginated_page_url, default_indexpage, default_ext)
42
+ end
43
+
44
+ @total_posts = posts.size
45
+ @posts = posts[init..offset]
46
+ @page_path = Utils.format_page_number(this_page_url, cur_page_nr, @total_pages)
47
+
48
+ @previous_page = @page != 1 ? @page - 1 : nil
49
+ @previous_page_path = @page == 1 ? nil :
50
+ @page == 2 ? Utils.format_page_number(first_index_page_url, 1, @total_pages) :
51
+ Utils.format_page_number(paginated_page_url, @previous_page, @total_pages)
52
+ @next_page = @page != @total_pages ? @page + 1 : nil
53
+ @next_page_path = @page != @total_pages ? Utils.format_page_number(paginated_page_url, @next_page, @total_pages) : nil
54
+
55
+ @first_page = 1
56
+ @first_page_path = Utils.format_page_number(first_index_page_url, 1, @total_pages)
57
+ @last_page = @total_pages
58
+ @last_page_path = Utils.format_page_number(paginated_page_url, @total_pages, @total_pages)
59
+ end
60
+
61
+ # Convert this Paginator's data to a Hash suitable for use by Liquid.
62
+ #
63
+ # Returns the Hash representation of this Paginator.
64
+ def to_liquid
65
+ {
66
+ 'per_page' => per_page,
67
+ 'posts' => posts,
68
+ 'total_posts' => total_posts,
69
+ 'total_pages' => total_pages,
70
+ 'page' => page,
71
+ 'page_path' => page_path,
72
+ 'previous_page' => previous_page,
73
+ 'previous_page_path' => previous_page_path,
74
+ 'next_page' => next_page,
75
+ 'next_page_path' => next_page_path,
76
+ 'first_page' => first_page,
77
+ 'first_page_path' => first_page_path,
78
+ 'last_page' => last_page,
79
+ 'last_page_path' => last_page_path,
80
+ 'page_trail' => page_trail
81
+ }
82
+ end
83
+
84
+ end # class Paginator
85
+
86
+ # Small utility class that handles individual pagination trails
87
+ # and makes them easier to work with in Liquid
88
+ class PageTrail
89
+ attr_reader :num, :path, :title
90
+
91
+ def initialize( num, path, title )
92
+ @num = num
93
+ @path = path
94
+ @title = title
95
+ end #func initialize
96
+
97
+ def to_liquid
98
+ {
99
+ 'num' => num,
100
+ 'path' => path,
101
+ 'title' => title
102
+ }
103
+ end
104
+ end #class PageTrail
105
+
106
+ end # module J1Paginator
107
+ end # module Jekyll