j1_paginator 2019.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +86 -0
- data/Gemfile +14 -0
- data/LICENSE +22 -0
- data/README-AUTOPAGES.md +141 -0
- data/README-GENERATOR.md +708 -0
- data/README.md +82 -0
- data/Rakefile +16 -0
- data/j1_paginator.gemspec +34 -0
- data/lib/j1_paginator.rb +34 -0
- data/lib/j1_paginator/autopages/autoPages.rb +77 -0
- data/lib/j1_paginator/autopages/defaults.rb +40 -0
- data/lib/j1_paginator/autopages/pages/baseAutoPage.rb +60 -0
- data/lib/j1_paginator/autopages/pages/categoryAutoPage.rb +32 -0
- data/lib/j1_paginator/autopages/pages/collectionAutoPage.rb +31 -0
- data/lib/j1_paginator/autopages/pages/tagAutoPage.rb +32 -0
- data/lib/j1_paginator/autopages/utils.rb +76 -0
- data/lib/j1_paginator/generator/compatibilityUtils.rb +121 -0
- data/lib/j1_paginator/generator/defaults.rb +27 -0
- data/lib/j1_paginator/generator/paginationGenerator.rb +146 -0
- data/lib/j1_paginator/generator/paginationIndexer.rb +116 -0
- data/lib/j1_paginator/generator/paginationModel.rb +377 -0
- data/lib/j1_paginator/generator/paginationPage.rb +66 -0
- data/lib/j1_paginator/generator/paginator.rb +107 -0
- data/lib/j1_paginator/generator/utils.rb +181 -0
- data/lib/j1_paginator/version.rb +10 -0
- data/spec/generator/defaults_spec.rb +34 -0
- data/spec/generator/paginationPage_spec.rb +12 -0
- data/spec/generator/paginator_spec.rb +197 -0
- data/spec/generator/utils_spec.rb +67 -0
- data/spec/spec_helper.rb +13 -0
- metadata +144 -0
@@ -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
|