jekyll-paginate-content 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ module Jekyll
2
+ module Paginate::Content
3
+
4
+ class Document < Jekyll::Document
5
+ attr_accessor :pager
6
+
7
+ def initialize(orig_doc, site, collection)
8
+ super(orig_doc.path, { :site => site,
9
+ :collection => site.collections[collection]})
10
+ self.merge_data!(orig_doc.data)
11
+ end
12
+
13
+ def data
14
+ @data ||= {}
15
+ end
16
+
17
+ end
18
+
19
+ class Page < Jekyll::Page
20
+ def initialize(orig_page, site, dirname, filename)
21
+ @site = site
22
+ @base = site.source
23
+ @dir = dirname
24
+ @name = filename
25
+
26
+ self.process(filename)
27
+ self.data ||= {}
28
+ self.data.merge!(orig_page.data)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,162 @@
1
+ module Jekyll
2
+ module Paginate::Content
3
+ class Generator < Jekyll::Generator
4
+ safe true
5
+
6
+ def generate(site)
7
+ start_time = Time.now
8
+
9
+ sconfig = site.config['paginate_content'] || {}
10
+
11
+ return unless sconfig["enabled"].nil? || sconfig["enabled"]
12
+
13
+ @debug = sconfig["debug"]
14
+
15
+ sconfig['collection'] = sconfig['collection'].split(/,\s*/) if sconfig['collection'].is_a?(String)
16
+
17
+ collections = [ sconfig['collection'], sconfig["collections"] ].flatten.compact.uniq;
18
+ collections = [ "posts", "pages" ] if collections.empty?
19
+
20
+ # Use this hash syntax to facilite merging _config.yml overrides
21
+ properties = {
22
+ 'all' => {
23
+ 'autogen' => 'jekyll-paginate-content',
24
+ 'hidden' => true,
25
+ 'tag' => nil,
26
+ 'tags' => nil,
27
+ 'category' => nil,
28
+ 'categories'=> nil
29
+ },
30
+
31
+ 'first' => {
32
+ 'hidden' => false,
33
+ 'tag' => '$',
34
+ 'tags' => '$',
35
+ 'category' => '$',
36
+ 'categories'=> '$'
37
+ },
38
+
39
+ 'part' => {},
40
+
41
+ 'last' => {},
42
+
43
+ 'single' => {}
44
+ }
45
+
46
+ base_url = (sconfig['prepend_baseurl'].nil? || sconfig['prepend_baseurl']) ? site.config['baseurl'] : ''
47
+
48
+ @config = {
49
+ :collections => collections,
50
+ :title => sconfig['title'],
51
+ :permalink => sconfig['permalink'] || '/:num/',
52
+ :trail => sconfig['trail'] || {},
53
+ :auto => sconfig['auto'],
54
+ :base_url => base_url,
55
+
56
+ :separator => sconfig['separator'] || '<!--page-->',
57
+ :header => sconfig['header'] || '<!--page_header-->',
58
+ :footer => sconfig['footer'] || '<!--page_footer-->',
59
+
60
+ :single_page => sconfig['single_page'] || '/view-all/',
61
+ :seo_canonical => sconfig['seo_canonical'].nil? || sconfig['seo_canonical'],
62
+ :toc_exclude => sconfig['toc_exclude'],
63
+
64
+ :properties => properties,
65
+ :user_props => sconfig['properties'] || {}
66
+ }
67
+
68
+ # Run through each specified collection
69
+
70
+ total_skipped = 0
71
+ total_single = 0
72
+
73
+ collections.each do |collection|
74
+ if collection == "pages"
75
+ items = site.pages
76
+ else
77
+ next if !site.collections.has_key?(collection)
78
+ items = site.collections[collection].docs
79
+ end
80
+
81
+ new_items = []
82
+ old_items = []
83
+
84
+ total_parts = 0
85
+ total_copies = 0
86
+
87
+ if @config[:auto]
88
+ if m = /^h(\d)/i.match(@config[:separator])
89
+ process = items.select { |item| /(\n|)(<h#{m[1]}|-{4,}|={4,})/.match?(item.content) }
90
+ else
91
+ process = items.select { |item| item.content.include?(@config[:separator]) }
92
+ end
93
+ else
94
+ process = items.select { |item| item.data['paginate'] }
95
+ end
96
+
97
+ process.each do |item|
98
+ paginator = Paginator.new(site, collection, item, @config)
99
+ if paginator.skipped
100
+ debug "[#{collection}] \"#{item.data['title']}\" skipped"
101
+ total_skipped += 1
102
+
103
+ elsif paginator.items.empty?
104
+ total_single += 1
105
+ debug "[#{collection}] \"#{item.data['title']}\" is a single page"
106
+ end
107
+
108
+ next if paginator.items.empty?
109
+
110
+ debug "[#{collection}] \"#{item.data['title']}\", #{paginator.items.length-1}+1 pages"
111
+ total_parts += paginator.items.length-1;
112
+ total_copies += 1
113
+ new_items << paginator.items
114
+ old_items << item
115
+ end
116
+
117
+ if !new_items.empty?
118
+ # Remove the old items at the original URLs
119
+ old_items.each do |item|
120
+ items.delete(item)
121
+ end
122
+
123
+ # Add the new items in
124
+ new_items.flatten!.each do |new_item|
125
+ items << new_item
126
+ end
127
+
128
+ info "[#{collection}] Generated #{total_parts}+#{total_copies} pages"
129
+ end
130
+ end
131
+
132
+ if total_skipped > 0
133
+ s = (total_skipped == 1 ? '' : 's')
134
+ info "Skipped #{total_skipped} unchanged item#{s}"
135
+ end
136
+
137
+ if total_single > 0
138
+ s = (total_single == 1 ? '' : 's')
139
+ info "#{total_single} page#{s} could not be split"
140
+ end
141
+
142
+ runtime = "%.6f" % (Time.now - start_time).to_f
143
+ debug "Runtime: #{runtime}s"
144
+ end
145
+
146
+ private
147
+ def info(msg)
148
+ Jekyll.logger.info "PaginateContent:", msg
149
+ end
150
+
151
+ def warn(msg)
152
+ Jekyll.logger.warn "PaginateContent:", msg
153
+ end
154
+
155
+ def debug(msg)
156
+ Jekyll.logger.warn "PaginateContent:", msg if @debug
157
+ end
158
+ end
159
+
160
+
161
+ end
162
+ end
@@ -0,0 +1,72 @@
1
+ module Jekyll
2
+ module Paginate::Content
3
+
4
+ class Pager
5
+ attr_accessor :activated, :first_page, :first_page_path,
6
+ :first_path, :has_next, :has_prev, :has_previous,
7
+ :is_first, :is_last, :last_page, :last_page_path,
8
+ :last_path, :next_is_last, :next_page, :next_page_path,
9
+ :next_path, :next_section, :page, :page_num, :page_path,
10
+ :page_trail, :pages, :paginated, :previous_is_first,
11
+ :prev_is_first, :previous_page, :prev_page, :previous_page_path,
12
+ :previous_path, :prev_path, :prev_section, :previous_section,
13
+ :section, :seo, :single_page, :toc, :total_pages, :view_all
14
+
15
+ def initialize(data)
16
+ data.each do |k,v|
17
+ instance_variable_set("@#{k}", v) if self.respond_to? k
18
+ end
19
+ end
20
+
21
+ def to_liquid
22
+ {
23
+ # Based on sverrir's jpv2
24
+ 'first_page' => first_page,
25
+ 'first_page_path' => first_page_path,
26
+ 'last_page' => last_page,
27
+ 'last_page_path' => last_page_path,
28
+ 'next_page' => next_page,
29
+ 'next_page_path' => next_page_path,
30
+ 'page' => page_num,
31
+ 'page_path' => page_path,
32
+ 'page_trail' => page_trail,
33
+ 'previous_page' => previous_page,
34
+ 'previous_page_path' => previous_page_path,
35
+ 'total_pages' => total_pages, # parts of the original page
36
+
37
+ # New stuff
38
+ 'has_next' => has_next,
39
+ 'has_previous' => has_previous,
40
+ 'is_first' => is_first,
41
+ 'is_last' => is_last,
42
+ 'next_is_last' => next_is_last,
43
+ 'previous_is_first' => previous_is_first,
44
+ 'paginated' => paginated,
45
+ 'seo' => seo,
46
+ 'single_page' => single_page,
47
+ 'section' => section,
48
+ 'toc' => toc,
49
+ 'next_section' => next_section,
50
+ 'previous_section' => previous_section,
51
+
52
+ # Aliases
53
+ 'activated' => paginated,
54
+ 'first_path' => first_page_path,
55
+ 'next_path' => next_page_path,
56
+ 'has_prev' => has_previous,
57
+ 'previous_path' => previous_page_path,
58
+ 'prev_path' => previous_page_path,
59
+ 'last_path' => last_page_path,
60
+ 'prev_page' => previous_page,
61
+ 'prev_is_first' => previous_is_first,
62
+ 'prev_section' => previous_section,
63
+ 'page_num' => page_num,
64
+ 'pages' => total_pages,
65
+ 'view_all' => single_page
66
+ }
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+
@@ -0,0 +1,575 @@
1
+ module Jekyll
2
+ module Paginate::Content
3
+
4
+ class Paginator
5
+ attr_accessor :skipped
6
+
7
+ def initialize(site, collection, item, config)
8
+ @site = site
9
+ @collection = collection
10
+ @items = []
11
+ @skipped = false
12
+
13
+ source_prefix = item.is_a?(Jekyll::Page) ? site.source : ''
14
+ source = File.join(source_prefix, item.path)
15
+ html = item.destination('')
16
+
17
+ final_config = {}.merge(config)
18
+ if item.data.has_key?('paginate_content')
19
+ item.data['paginate_content'].each do |k,v|
20
+ s = k.downcase.strip.to_sym
21
+ final_config[s] = v
22
+ end
23
+ end
24
+ @config = final_config
25
+
26
+ if @config[:force] || (!File.exist?(html) || (File.mtime(html) < File.mtime(source)))
27
+ self.split(item)
28
+ else
29
+ @skipped = true
30
+ end
31
+ end
32
+
33
+ def items
34
+ @items
35
+ end
36
+
37
+ def split(item)
38
+ sep = @config[:separator].downcase.strip
39
+ # Update the header IDs the original document
40
+ content = item.content
41
+
42
+ # Escape special characters inside code blocks
43
+ content.scan(/(```|~~~+)(.*?)\1/m).each do |e|
44
+ escaped = e[1].gsub(/([#<\-=])/, '~|\1|')
45
+ content.gsub!(e[1], escaped)
46
+ end
47
+
48
+ # Generate TOC
49
+ toc = ""
50
+
51
+ seen_anchors = {}
52
+ list_chars = ['-','*','+']
53
+
54
+ if m = /^h([1-6])$/.match(sep)
55
+ base_level = m[1].to_i - 1
56
+ else
57
+ base_level = 5
58
+ end
59
+
60
+ lowest_level = 5
61
+
62
+ # TODO: Optimize this regex
63
+ content.scan(/(^|\r?\n)((#+)\s*([^\r\n#]+)#*\r?\n|([^\r\n]+)\r?\n(=+|\-{4,})\s*\r?\n|<h([1-6])[^>]*>([^\r\n<]+)(\s*<\/h\7>))/mi).each do |m|
64
+ header = m[3] || m[4] || m[7]
65
+
66
+ next if @config[:toc_exclude] && @config[:toc_exclude].include?(header)
67
+
68
+ markup = m[1].strip
69
+
70
+ # Level is 0-based for convenience
71
+ if m[3]
72
+ level = m[2].length - 1
73
+ elsif m[4]
74
+ level = m[5][0] == '=' ? 0 : 1
75
+ elsif m[7]
76
+ level = m[6].to_i - 1
77
+ end
78
+
79
+ lowest_level = [level, lowest_level].min
80
+
81
+ orig_anchor = anchor = header.downcase.gsub(/[[:punct:]]/, '').gsub(/\s+/, '-')
82
+
83
+ ctr = 1
84
+ while seen_anchors[anchor]
85
+ anchor = "#{orig_anchor}-#{ctr}"
86
+ ctr += 1
87
+ end
88
+ seen_anchors[anchor] = 1
89
+
90
+ # Escape the header so we don't match again
91
+ # for the same header text in a different location
92
+ escaped = Regexp.escape(markup)
93
+ markup = "$$_#{markup}_$$"
94
+
95
+ content.sub!(/#{escaped}\s*(?=#|\r?\n)/, "#{markup}#{$/}{: id=\"#{anchor}\"}#{$/}")
96
+
97
+ # Markdown indent
98
+ char = list_chars[level % 3]
99
+ indent = ' ' * level
100
+ toc << "#{indent}#{char} [#{header}](##{anchor})#{$/}"
101
+ end
102
+
103
+ if lowest_level > 0
104
+ excess = ' ' * lowest_level
105
+ toc.gsub!(/^#{excess}/, '')
106
+ end
107
+
108
+ # Restore original header text
109
+ content.gsub!(/\$\$_(.*?)_\$\$/m, '\1')
110
+
111
+ @toc = toc.empty? ? nil : toc
112
+
113
+ # Handle splitting by headers, h1-h6
114
+ if m = /^h([1-6])$/.match(sep)
115
+ # Split on <h2> etc.
116
+
117
+ level = m[1].to_i
118
+
119
+ init_pages = []
120
+
121
+ # atx syntax: Prefixed by one or more '#'
122
+ atx = "#" * level
123
+ atx_parts = content.split(/(?=^#{atx} )/)
124
+
125
+ # HTML symtax <h1> to <h6>
126
+ htx_parts = []
127
+ atx_parts.each do |section|
128
+ htx_parts << section.split(/(?=<#{sep}[^>]*>)/i)
129
+ end
130
+ htx_parts.flatten!
131
+
132
+ if level <= 2
133
+ # Setext syntax: underlined by '=' (h1) or '-' (h2)
134
+ # For now require four '-' to avoid confusion with <hr>
135
+ # or demo YAML front-matter
136
+ stx = level == 1 ? "=" : '-' * 4
137
+ htx_parts.each do |section|
138
+ init_pages << section.split(/(?=^.+\n#{stx}+$)/)
139
+ end
140
+
141
+ else
142
+ init_pages = htx_parts
143
+ end
144
+
145
+ init_pages.flatten!
146
+ else
147
+ init_pages = content.split(sep)
148
+ end
149
+
150
+ return if init_pages.length == 1
151
+
152
+ # Unescape special characters inside code blocks, for main content
153
+ # Main content was modified by adding header IDs
154
+ content.gsub!(/~\|(.)\|/, '\1')
155
+
156
+ # Make page length the minimum, if specified
157
+ if @config[:minimum]
158
+ pages = []
159
+ init_pages.each do |page_content|
160
+ i = pages.empty? ? 0 : pages.length - 1
161
+ if !pages[i] || pages[i].length < @config[:minimum]
162
+ pages[i] ||= ""
163
+ pages[i] << page_content
164
+ else
165
+ pages << page_content
166
+ i += 1
167
+ end
168
+ end
169
+
170
+ else
171
+ pages = init_pages
172
+ end
173
+
174
+ page_header = pages[0].split(@config[:header])
175
+ pages[0] = page_header[1] || page_header[0]
176
+ header = page_header[1] ? page_header[0] : ''
177
+
178
+ page_footer = pages[-1].split(@config[:footer])
179
+ pages[-1] = page_footer[0]
180
+ footer = page_footer[1] || ''
181
+
182
+ new_items = []
183
+ page_data = {}
184
+
185
+ dirname = ""
186
+ filename = ""
187
+
188
+ # For SEO; 'canonical' is a personal override ;-)
189
+ site_url = (@site.config['canonical'] || @site.config['url']) + @site.config['baseurl']
190
+ site_url.gsub!(/\/$/, '')
191
+
192
+ # For the permalink
193
+ base = item.url
194
+
195
+ user_props = @config[:user_props]
196
+
197
+ first_page_path = ''
198
+ total_pages = 0
199
+ single_page = ''
200
+ id = ("%10.9f" % Time.now.to_f).to_s
201
+
202
+ num = 1
203
+ max = pages.length
204
+
205
+ # Find the anchors/targets
206
+ a_locations = {}
207
+ i = 1
208
+ pages.each do |page|
209
+ # TODO: Optimize this regex
210
+ page.scan(/<a\s+name=['"](\S+)['"]>[^<]*<\/a>|<[^>]*id=['"](\S+)['"][^>]*>|{:.*id=['"](\S+)['"][^}]*}/i).each do |a|
211
+ anchor = a[0] || a[1] || a[2]
212
+ a_locations[anchor] = i
213
+ end
214
+ i += 1
215
+ end
216
+
217
+ ######################################## Main processing
218
+
219
+ pages.each do |page|
220
+ # Unescape special characters inside code blocks, for pages
221
+ page.gsub!(/~\|(.)\|/, '\1')
222
+
223
+ plink_all = nil
224
+ plink_next = nil
225
+ plink_prev = nil
226
+
227
+ paginator = {}
228
+
229
+ first = num == 1
230
+ last = num == max
231
+
232
+ if m = base.match(/(.*\/[^\.]*)(\.[^\.]+)$/)
233
+ # /.../filename.ext
234
+ plink = _permalink(m[1], num, max)
235
+ plink_all = m[1] + @config[:single_page]
236
+ plink_prev = _permalink(m[1], num-1, max) if !first
237
+ plink_next = _permalink(m[1],num+1, max) if !last
238
+ else
239
+ # /.../folder/
240
+ plink = _permalink(base, num, max)
241
+ plink_all = base + @config[:single_page]
242
+ plink_prev = _permalink(base, num-1, max) if !first
243
+ plink_next = _permalink(base, num+1, max) if !last
244
+ end
245
+
246
+ plink_all.gsub!(/\/\//,'/')
247
+
248
+ # TODO: Put these in classes
249
+
250
+ if @collection == "pages"
251
+ if first
252
+ # Keep the info of the original page to avoid warnings
253
+ # while creating the new virtual pages
254
+ dirname = File.dirname(plink)
255
+ filename = item.name
256
+ page_data = item.data
257
+ end
258
+
259
+ paginator.merge!(page_data)
260
+ new_part = Page.new(item, @site, dirname, filename)
261
+ else
262
+ new_part = Document.new(item, @site, @collection)
263
+ end
264
+
265
+ # Find the section names from the first h1 etc.
266
+ # TODO: Simplify/merge regex
267
+ candidates = {}
268
+ if m = /(.*\r?\n|)#+\s+(.*)\s*#*/.match(page)
269
+ candidates[m[2]] = m[1].length
270
+ end
271
+
272
+ if m = /(.*\r?\n|)([^\r\n]+)\r?\n(=+|\-{4,})\s*\r?\n/.match(page)
273
+ candidates[m[2]] = m[1].length
274
+ end
275
+
276
+ if m = /<h([1-6])[^>]*>\s*([^\r\n<]+)(\s*<\/h\1)/mi.match(page)
277
+ candidates[m[2]] = m[1].length
278
+ end
279
+
280
+ if candidates.empty?
281
+ section = "Untitled"
282
+ else
283
+ section = candidates.sort_by { |k,v| v }.first.flatten[0]
284
+ end
285
+
286
+ paginator['section'] = section
287
+
288
+ paginator['paginated'] = true
289
+ paginator['page_num'] = num
290
+ paginator['page_path'] = @config[:base_url] + _permalink(base, num, max)
291
+
292
+ paginator['first_page'] = 1
293
+ paginator['first_page_path'] = @config[:base_url] + base
294
+
295
+ paginator['last_page'] = pages.length
296
+ paginator['last_page_path'] = @config[:base_url] + _permalink(base, max, max)
297
+
298
+ paginator['total_pages'] = max
299
+
300
+ paginator['single_page'] = @config[:base_url] + plink_all
301
+
302
+ if first
303
+ paginator['is_first'] = true
304
+ first_page_path = @config[:base_url] + base
305
+ total_pages = max
306
+ single_page = plink_all
307
+ else
308
+ paginator['previous_page'] = num - 1
309
+ paginator['previous_page_path'] = @config[:base_url] + plink_prev
310
+ end
311
+
312
+ if last
313
+ paginator['is_last'] = true
314
+ else
315
+ paginator['next_page'] = num + 1
316
+ paginator['next_page_path'] = @config[:base_url] + plink_next
317
+ end
318
+
319
+ paginator['previous_is_first'] = (num == 2)
320
+ paginator['next_is_last'] = (num == max - 1)
321
+
322
+ paginator['has_previous'] = (num >= 2)
323
+ paginator['has_next'] = (num < max)
324
+
325
+ seo = {}
326
+ seo['canonical'] = _seo('canonical', site_url + plink_all) if @config[:seo_canonical];
327
+ seo['prev'] = _seo('prev', site_url + plink_prev) if plink_prev
328
+ seo['next'] = _seo('next', site_url + plink_next) if plink_next
329
+ seo['links'] = seo.map {|k,v| v }.join($/)
330
+
331
+ paginator['seo'] = seo
332
+
333
+ # Set the paginator
334
+ new_part.pager = Pager.new(paginator)
335
+
336
+ # Set up the frontmatter properties
337
+ _set_properties(item, new_part, 'all', user_props)
338
+ _set_properties(item, new_part, 'first', user_props) if first
339
+ _set_properties(item, new_part, 'last', user_props) if last
340
+ _set_properties(item, new_part, 'part', user_props) if !first && !last
341
+
342
+ # Don't allow these to be overriden,
343
+ # i.e. set/reset layout, date, title, permalink
344
+
345
+ new_part.data['layout'] = item.data['layout']
346
+ new_part.data['date'] = item.data['date']
347
+ new_part.data['permalink'] = plink
348
+
349
+ # title is set together with trail below as it may rely on section name
350
+
351
+ new_part.data['pagination_info'] =
352
+ {
353
+ 'curr_page' => num,
354
+ 'total_pages' => max,
355
+ 'type' => first ? 'first' : ( last ? 'last' : 'part'),
356
+ 'id' => id
357
+ }
358
+
359
+ new_part.content = header + page + footer
360
+
361
+ new_items << new_part
362
+
363
+ num += 1
364
+ end
365
+
366
+ t_config = @config[:trail]
367
+ t_config[:title] = @config[:title]
368
+
369
+ # Replace #target with page_path#target
370
+ i = 0
371
+ new_items.each do |item|
372
+ content = item.content
373
+
374
+ _adjust_links(new_items, item.content, a_locations, i+1)
375
+
376
+ # Adjust the TOC relative to this page
377
+ if @toc
378
+ toc = @toc.dup
379
+ _adjust_links(new_items, toc, a_locations, i+1)
380
+ else
381
+ toc = nil
382
+ end
383
+
384
+ item.pager.toc = { 'simple' => toc }
385
+
386
+ item.pager.page_trail = _page_trail(@config[:base_url] + base, new_items, i+1,
387
+ new_items.length, t_config)
388
+
389
+ # Previous/next section name assignments
390
+ item.pager.previous_section = new_items[i-1].pager.section if i > 0
391
+ item.pager.next_section = new_items[i+1].pager.section if i < new_items.length - 1
392
+
393
+ i += 1
394
+ end
395
+
396
+ # This is in another loop to avoid messing with the titles
397
+ # during page trail generation
398
+ i = 1
399
+ new_items.each do |item|
400
+ item.data['title'] =
401
+ _title(@config[:title], new_items, i, new_items.length,
402
+ @config[:retitle_first])
403
+ i += 1
404
+ end
405
+
406
+ # Setup single-page view
407
+
408
+ if !@config[:single_page].empty?
409
+ if @collection == "pages"
410
+ single = Page.new(item, @site, dirname, item.name)
411
+ else
412
+ single = Document.new(item, @site, @collection)
413
+ end
414
+
415
+ _set_properties(item, single, 'all', user_props)
416
+ _set_properties(item, single, 'single', user_props)
417
+
418
+ single.data['pagination_info'] = {
419
+ 'type' => 'single',
420
+ 'id' => id
421
+ }
422
+
423
+ # Restore original properties for these
424
+ single.data['permalink'] = single_page
425
+ single.data['layout'] = item.data['layout']
426
+ single.data['date'] = item.data['date']
427
+ single.data['title'] = item.data['title']
428
+
429
+ # Just some limited data for the single page
430
+ seo = @config[:seo_canonical] ?
431
+ _seo('canonical', site_url + single_page) : ""
432
+
433
+ single_paginator = {
434
+ 'first_page_path' => first_page_path,
435
+ 'total_pages' => total_pages,
436
+ 'toc' => {
437
+ 'simple' => @toc
438
+ },
439
+ 'seo' => {
440
+ 'links' => seo,
441
+ 'canonical' => seo
442
+ }
443
+ }
444
+
445
+ single.pager = Pager.new(single_paginator)
446
+ single.content = item.content
447
+
448
+ new_items << single
449
+ end
450
+
451
+ @items = new_items
452
+ end
453
+
454
+ private
455
+ def _page_trail(base, items, page, max, config)
456
+ page_trail = []
457
+
458
+ before = config["before"] || 0
459
+ after = config["after"] || 0
460
+
461
+ (before <= 0 || before >= max) ? 0 : before
462
+ (after <= 0 || after >= max) ? 0 : after
463
+
464
+ if before.zero? && after.zero?
465
+ start_page = 1
466
+ end_page = max
467
+ else
468
+ start_page = page - before
469
+ start_page = 1 if start_page <= 0
470
+
471
+ end_page = start_page + before + after
472
+ if end_page > max
473
+ end_page = max
474
+ start_page = max - before - after
475
+ start_page = 1 if start_page <= 0
476
+ end
477
+ end
478
+
479
+ i = start_page
480
+ while i <= end_page do
481
+ title = _title(config[:title], items, i, max)
482
+ page_trail <<
483
+ {
484
+ 'num' => i,
485
+ 'path' => _permalink(base, i, max),
486
+ 'title' => title
487
+ }
488
+ i += 1
489
+ end
490
+
491
+ page_trail
492
+ end
493
+
494
+ def _seo(type, url)
495
+ " <link rel=\"#{type}\" href=\"#{url}\" />"
496
+ end
497
+
498
+ def _permalink(base, page, max)
499
+ return base if page == 1
500
+
501
+ (base + @config[:permalink]).
502
+ gsub(/:num/, page.to_s).
503
+ gsub(/:max/, max.to_s).
504
+ gsub(/\/\//, '/')
505
+ end
506
+
507
+ def _title(format, items, page, max, retitle_first = false)
508
+ orig = items[page-1].data['title']
509
+ return orig if !format || (page == 1 && !retitle_first)
510
+
511
+ section = items[page-1].pager.section
512
+
513
+ format.gsub(/:title/, orig || '').
514
+ gsub(/:section/, section).
515
+ gsub(/:num/, page.to_s).
516
+ gsub(/:max/, max.to_s)
517
+ end
518
+
519
+ def _set_properties(original, item, stage, user_props = nil)
520
+ stage_props = {}
521
+ stage_props.merge!(@config[:properties][stage])
522
+
523
+ if user_props && user_props.has_key?(stage)
524
+ stage_props.merge!(user_props[stage])
525
+ end
526
+
527
+ return if stage_props.empty?
528
+
529
+ # Handle special values
530
+ stage_props.delete_if do |k,v|
531
+ if k == "pagination_info"
532
+ false
533
+ elsif v == "/"
534
+ true
535
+ else
536
+ if v.is_a?(String) && m = /\$\.?(.*)$/.match(v)
537
+ stage_props[k] = m[1].empty? ?
538
+ original.data[k] : original.data[m[1]]
539
+ end
540
+ false
541
+ end
542
+ end
543
+
544
+ if item.respond_to?('merge_data')
545
+ item.merge_data!(stage_props)
546
+ else
547
+ item.data.merge!(stage_props)
548
+ end
549
+
550
+ end
551
+
552
+ def _adjust_links(new_items, content, a_locations, num)
553
+ # TODO: Try to merge these
554
+
555
+ # [Something](#target)
556
+ content.scan(/\[[^\]]+\]\(#(.*)\)/i).flatten.each do |a|
557
+ if (page_num = a_locations[a]) && (page_num != num)
558
+ content.gsub!(/(\[[^\]]+\]\()##{a}(\))/i,
559
+ '\1' + @site.config['baseurl'] + new_items[page_num-1].data['permalink']+'#'+a+'\2')
560
+ end
561
+ end
562
+
563
+ # [Something]: #target
564
+ content.scan(/\[[^\]]+\]:\s*#(\S+)/i).flatten.each do |a|
565
+ if (page_num = a_locations[a]) && (page_num != num)
566
+ content.gsub!(/(\[[^\]]+\]:\s*)##{a}/i,
567
+ '\1' + @site.config['baseurl'] + new_items[page_num-1].data['permalink']+'#'+a)
568
+ end
569
+ end
570
+
571
+ end
572
+ end
573
+
574
+ end
575
+ end