jekyll-paginate-v2-1.9.4/0000755000175000017500000000000013514472274014415 5ustar danieldanieljekyll-paginate-v2-1.9.4/spec/0000755000175000017500000000000013514472274015347 5ustar danieldanieljekyll-paginate-v2-1.9.4/spec/spec_helper.rb0000644000175000017500000000125613514472274020171 0ustar danieldanielrequire 'minitest/spec' require 'minitest/autorun' require 'jekyll' require_relative '../lib/jekyll-paginate-v2/version' require_relative '../lib/jekyll-paginate-v2/generator/defaults' require_relative '../lib/jekyll-paginate-v2/generator/utils' require_relative '../lib/jekyll-paginate-v2/generator/compatibilityUtils' require_relative '../lib/jekyll-paginate-v2/generator/paginationIndexer' require_relative '../lib/jekyll-paginate-v2/generator/paginator' require_relative '../lib/jekyll-paginate-v2/generator/paginationPage' require_relative '../lib/jekyll-paginate-v2/generator/paginationModel' require_relative '../lib/jekyll-paginate-v2/generator/paginationGenerator'jekyll-paginate-v2-1.9.4/spec/generator/0000755000175000017500000000000013514472274017335 5ustar danieldanieljekyll-paginate-v2-1.9.4/spec/generator/utils_spec.rb0000644000175000017500000000601713514472274022040 0ustar danieldanielrequire_relative '../spec_helper.rb' module Jekyll::PaginateV2::Generator describe Utils do it "should always replace num format with the specified number" do Utils.format_page_number( ":num", 7).must_equal "7" Utils.format_page_number( ":num", 13).must_equal "13" Utils.format_page_number( ":num", -2).must_equal "-2" Utils.format_page_number( ":num", 0).must_equal "0" Utils.format_page_number( ":num", 1000).must_equal "1000" end it "should always replace num format with the specified number and keep rest of formatting" do Utils.format_page_number( "/page:num/", 7).must_equal "/page7/" Utils.format_page_number( "/page:num/", 50).must_equal "/page50/" Utils.format_page_number( "/page:num/", -5).must_equal "/page-5/" Utils.format_page_number( "/car/:num/", 1).must_equal "/car/1/" Utils.format_page_number( "/car/:num", 1).must_equal "/car/1" Utils.format_page_number( "car/:num", 1).must_equal "car/1" Utils.format_page_number( "/car//:num", 1).must_equal "/car//1" end it "make sure there is a leading slash in path" do Utils.ensure_leading_slash("path/to/file/wow").must_equal "/path/to/file/wow" Utils.ensure_leading_slash("/no/place/wow/").must_equal "/no/place/wow/" Utils.ensure_leading_slash("/no").must_equal "/no" Utils.ensure_leading_slash("no").must_equal "/no" end it "make sure there is never a leading slash in path" do Utils.remove_leading_slash("path/to/file/wow").must_equal "path/to/file/wow" Utils.remove_leading_slash("/no/place/wow/").must_equal "no/place/wow/" Utils.remove_leading_slash("/no").must_equal "no" Utils.remove_leading_slash("no").must_equal "no" end it "sort must sort strings lowercase" do Utils.sort_values( "AARON", "Aaron").must_equal 0 Utils.sort_values( "AARON", "aaron").must_equal 0 Utils.sort_values( "aaron", "AARON").must_equal 0 end it "when sorting by nested post data the values must be resolved fully" do data = {'book'=>{ 'name' => { 'first'=> 'John', 'last'=> 'Smith'}, 'rank'=>20}} Utils.sort_get_post_data(data, "book:rank").must_equal 20 Utils.sort_get_post_data(data, "book:name:first").must_equal "John" Utils.sort_get_post_data(data, "book:name:last").must_equal "Smith" Utils.sort_get_post_data(data, "book:name").must_be_nil Utils.sort_get_post_data(data, "name").must_be_nil Utils.sort_get_post_data(data, "book").must_be_nil end it "should always replace max format with the specified number if specified" do Utils.format_page_number( ":num-:max", 7, 16).must_equal "7-16" Utils.format_page_number( ":num-:max", 13, 20).must_equal "13-20" Utils.format_page_number( ":num-:max", -2, -4).must_equal "-2--4" Utils.format_page_number( ":num_of_:max", 0, 10).must_equal "0_of_10" Utils.format_page_number( ":num/:max", 1000, 2000).must_equal "1000/2000" end end end jekyll-paginate-v2-1.9.4/spec/generator/paginator_spec.rb0000644000175000017500000001337713514472274022673 0ustar danieldanielrequire_relative '../spec_helper.rb' module Jekyll::PaginateV2::Generator describe Paginator do it "must include the necessary paginator attributes" do # config_per_page, first_index_page_url, paginated_page_url, posts, cur_page_nr, num_pages pager = Paginator.new(10, "index.html", "/page:num/", [], 1, 10, 'index', '.html') # None of these accessors should throw errors, just run through them to test val = pager.page val = pager.per_page val = pager.posts val = pager.total_posts val = pager.total_pages val = pager.previous_page val = pager.previous_page_path val = pager.next_page val = pager.next_page_path end it "must throw an error if the current page number is greater than the total pages" do err = -> { pager = Paginator.new(10, "index.html", "/page:num/", [], 10, 8, 'index', '.html') }.must_raise RuntimeError # No error should be raised below pager = Paginator.new(10, "index.html", "/page:num/", [], 8, 10, 'index', '.html') end it "must trim the list of posts correctly based on the cur_page_nr and per_page" do # Create a dummy list of posts that is easy to track posts = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35'] # Initialize a pager with # 5 posts per page # at page 2 out of 5 pages pager = Paginator.new(5, "index.html", "/page:num/", posts, 2, 5, '', '') pager.page.must_equal 2 pager.per_page.must_equal 5 pager.total_pages.must_equal 5 pager.total_posts.must_equal 35 pager.posts.size.must_equal 5 pager.posts[0].must_equal '6' pager.posts[4].must_equal '10' pager.previous_page.must_equal 1 pager.previous_page_path.must_equal 'index.html' pager.next_page.must_equal 3 pager.next_page_path.must_equal '/page3/' end it "must not create a previous page if we're at first page" do # Create a dummy list of posts that is easy to track posts = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35'] # Initialize a pager with # 5 posts per page # at page 2 out of 5 pages pager = Paginator.new(5, "index.html", "/page:num/", posts, 1, 5, '', '') pager.page.must_equal 1 pager.per_page.must_equal 5 pager.total_pages.must_equal 5 pager.total_posts.must_equal 35 pager.posts.size.must_equal 5 pager.posts[0].must_equal '1' pager.posts[4].must_equal '5' pager.previous_page.must_be_nil pager.previous_page_path.must_be_nil pager.next_page.must_equal 2 pager.next_page_path.must_equal '/page2/' end it "must not create a next page if we're at the final page" do # Create a dummy list of posts that is easy to track posts = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35'] # Initialize a pager with # 5 posts per page # at page 2 out of 5 pages pager = Paginator.new(5, "index.html", "/page:num/", posts, 5, 5, '', '') pager.page.must_equal 5 pager.per_page.must_equal 5 pager.total_pages.must_equal 5 pager.total_posts.must_equal 35 pager.posts.size.must_equal 5 pager.posts[0].must_equal '21' pager.posts[4].must_equal '25' pager.previous_page.must_equal 4 pager.previous_page_path.must_equal '/page4/' pager.next_page.must_be_nil pager.next_page_path.must_be_nil end it "must create the explicit index page and index extension when specified" do # Create a dummy list of posts that is easy to track posts = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35'] # Initialize a pager with # 5 posts per page # at page 2 out of 5 pages pager = Paginator.new(5, "index.html", "/page:num/", posts, 2, 5, 'index', '.html') pager.page.must_equal 2 pager.per_page.must_equal 5 pager.total_pages.must_equal 5 pager.total_posts.must_equal 35 pager.posts.size.must_equal 5 pager.posts[0].must_equal '6' pager.posts[4].must_equal '10' pager.previous_page.must_equal 1 pager.previous_page_path.must_equal 'index.html' pager.next_page.must_equal 3 pager.next_page_path.must_equal '/page3/index.html' end it "must create the explicit index page and index extension when specified" do # Create a dummy list of posts that is easy to track posts = ['1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31','32','33','34','35'] # Initialize a pager with # 5 posts per page # at page 2 out of 5 pages pager = Paginator.new(5, "/", "/", posts, 2, 5, 'feed:num', '.json') pager.page.must_equal 2 pager.per_page.must_equal 5 pager.total_pages.must_equal 5 pager.total_posts.must_equal 35 pager.posts.size.must_equal 5 pager.posts[0].must_equal '6' pager.posts[4].must_equal '10' pager.previous_page.must_equal 1 pager.previous_page_path.must_equal '/feed1.json' pager.next_page.must_equal 3 pager.next_page_path.must_equal '/feed3.json' end end end jekyll-paginate-v2-1.9.4/spec/generator/paginationPage_spec.rb0000644000175000017500000000056713514472274023632 0ustar danieldanielrequire_relative '../spec_helper.rb' module Jekyll::PaginateV2::Generator describe "tesing pagination page implementation" do it "sould always read the template file into itself" do # DUE TO THE JEKYLL:PAGE CLASS ACCESSING FILE IO DIRECTLY # I AM UNABLE TO MOCK OUT THE FILE OPERATIONS TO CREATE UNIT TESTS FOR THIS CLASS :/ end end endjekyll-paginate-v2-1.9.4/spec/generator/defaults_spec.rb0000644000175000017500000000217313514472274022506 0ustar danieldanielrequire_relative '../spec_helper.rb' module Jekyll::PaginateV2::Generator describe "checking default config" do it "should always contain the following keys" do DEFAULT.must_include 'enabled' DEFAULT.must_include 'collection' DEFAULT.must_include 'per_page' DEFAULT.must_include 'permalink' DEFAULT.must_include 'title' DEFAULT.must_include 'page_num' DEFAULT.must_include 'sort_reverse' DEFAULT.must_include 'sort_field' DEFAULT.must_include 'limit' DEFAULT.must_include 'debug' DEFAULT.size.must_be :>=, 10 end it "should always contain the following key defaults" do DEFAULT['enabled'].must_equal false DEFAULT['collection'].must_equal 'posts' DEFAULT['per_page'].must_equal 10 DEFAULT['permalink'].must_equal '/page:num/' DEFAULT['title'].must_equal ':title - page :num' DEFAULT['page_num'].must_equal 1 DEFAULT['sort_reverse'].must_equal false DEFAULT['sort_field'].must_equal 'date' DEFAULT['limit'].must_equal 0 DEFAULT['debug'].must_equal false end end endjekyll-paginate-v2-1.9.4/lib/0000755000175000017500000000000013514472274015163 5ustar danieldanieljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/0000755000175000017500000000000013514472274020570 5ustar danieldanieljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/version.rb0000644000175000017500000000055513514472274022607 0ustar danieldanielmodule Jekyll module PaginateV2 VERSION = "1.9.4" # When modifying remember to issue a new tag command in git before committing, then push the new tag # git tag -a v1.9.4 -m "Gem v1.9.4" # git push origin --tags # Yanking a published Gem # gem yank jekyll-paginate-v2 -v VERSION end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/0000755000175000017500000000000013514472274022556 5ustar danieldanieljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/utils.rb0000644000175000017500000001141513514472274024245 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # Static utility functions that are used in the code and # don't belong in once place in particular # class Utils # Static: Calculate the number of pages. # # all_posts - The Array of all Posts. # per_page - The Integer of entries per page. # # Returns the Integer number of pages. def self.calculate_number_of_pages(all_posts, per_page) (all_posts.size.to_f / per_page.to_i).ceil end # Static: returns a fully formatted string with the current (:num) page number and maximum (:max) page count replaced if configured # def self.format_page_number(toFormat, cur_page_nr, total_page_count=nil) s = toFormat.sub(':num', cur_page_nr.to_s) if !total_page_count.nil? s = s.sub(':max', total_page_count.to_s) end return s end #function format_page_number # Static: returns a fully formatted string with the :title variable and the current (:num) page number and maximum (:max) page count replaced # def self.format_page_title(toFormat, title, cur_page_nr=nil, total_page_count=nil) return format_page_number(toFormat.sub(':title', title.to_s), cur_page_nr, total_page_count) end #function format_page_title # Static: Return a String version of the input which has a leading dot. # If the input already has a dot in position zero, it will be # returned unchanged. # # path - a String path # # Returns the path with a leading slash def self.ensure_leading_dot(path) path[0..0] == "." ? path : ".#{path}" end # Static: Return a String version of the input which has a leading slash. # If the input already has a forward slash in position zero, it will be # returned unchanged. # # path - a String path # # Returns the path with a leading slash def self.ensure_leading_slash(path) path[0..0] == "/" ? path : "/#{path}" end # Static: Return a String version of the input without a leading slash. # # path - a String path # # Returns the input without the leading slash def self.remove_leading_slash(path) path[0..0] == "/" ? path[1..-1] : path end # Static: Return a String version of the input which has a trailing slash. # If the input already has a forward slash at the end, it will be # returned unchanged. # # path - a String path # # Returns the path with a trailing slash def self.ensure_trailing_slash(path) path[-1] == "/" ? path : "#{path}/" end # # Sorting routine used for ordering posts by custom fields. # Handles Strings separately as we want a case-insenstive sorting # def self.sort_values(a, b) if a.nil? && !b.nil? return -1 elsif !a.nil? && b.nil? return 1 end if a.is_a?(String) return a.downcase <=> b.downcase end if a.respond_to?('to_datetime') && b.respond_to?('to_datetime') return a.to_datetime <=> b.to_datetime end # By default use the built in sorting for the data type return a <=> b end # Retrieves the given sort field from the given post # the sort_field variable can be a hierarchical value on the form "parent_field:child_field" repeated as many times as needed # only the leaf child_field will be retrieved def self.sort_get_post_data(post_data, sort_field) # Begin by splitting up the sort_field by (;,:.) sort_split = sort_field.split(":") sort_value = post_data sort_split.each do |r_key| key = r_key.downcase.strip # Remove any erronious whitespace and convert to lower case if !sort_value.has_key?(key) return nil end # Work my way through the hash sort_value = sort_value[key] end # If the sort value is a hash then return nil else return the value if( sort_value.is_a?(Hash) ) return nil else return sort_value end end # Ensures that the passed in url has a index and extension applied def self.ensure_full_path(url, default_index, default_ext) if( url.end_with?('/')) return url + default_index + default_ext elsif !url.include?('.') return url + default_index end # Default return url end end end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/paginator.rb0000644000175000017500000001053713514472274025075 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # Handles the preparation of all the posts based on the current page index # class Paginator attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :previous_page_path, :next_page, :next_page_path, :page_path, :page_trail, :first_page, :first_page_path, :last_page, :last_page_path def page_trail @page_trail end def page_trail=(page_array) @page_trail = page_array end # Initialize a new Paginator. # def initialize(config_per_page, first_index_page_url, paginated_page_url, posts, cur_page_nr, num_pages, default_indexpage, default_ext) @page = cur_page_nr @per_page = config_per_page.to_i @total_pages = num_pages if @page > @total_pages raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}" end init = (@page - 1) * @per_page offset = (init + @per_page - 1) >= posts.size ? posts.size : (init + @per_page - 1) # Ensure that the current page has correct extensions if needed this_page_url = Utils.ensure_full_path(@page == 1 ? first_index_page_url : paginated_page_url, !default_indexpage || default_indexpage.length == 0 ? 'index' : default_indexpage, !default_ext || default_ext.length == 0 ? '.html' : default_ext) # To support customizable pagination pages we attempt to explicitly append the page name to # the url incase the user is using extensionless permalinks. if default_indexpage && default_indexpage.length > 0 # Adjust first page url first_index_page_url = Utils.ensure_full_path(first_index_page_url, default_indexpage, default_ext) # Adjust the paginated pages as well paginated_page_url = Utils.ensure_full_path(paginated_page_url, default_indexpage, default_ext) end @total_posts = posts.size @posts = posts[init..offset] @page_path = Utils.format_page_number(this_page_url, cur_page_nr, @total_pages) @previous_page = @page != 1 ? @page - 1 : nil @previous_page_path = @page == 1 ? nil : @page == 2 ? Utils.format_page_number(first_index_page_url, 1, @total_pages) : Utils.format_page_number(paginated_page_url, @previous_page, @total_pages) @next_page = @page != @total_pages ? @page + 1 : nil @next_page_path = @page != @total_pages ? Utils.format_page_number(paginated_page_url, @next_page, @total_pages) : nil @first_page = 1 @first_page_path = Utils.format_page_number(first_index_page_url, 1, @total_pages) @last_page = @total_pages @last_page_path = Utils.format_page_number(paginated_page_url, @total_pages, @total_pages) end # Convert this Paginator's data to a Hash suitable for use by Liquid. # # Returns the Hash representation of this Paginator. def to_liquid { 'per_page' => per_page, 'posts' => posts, 'total_posts' => total_posts, 'total_pages' => total_pages, 'page' => page, 'page_path' => page_path, 'previous_page' => previous_page, 'previous_page_path' => previous_page_path, 'next_page' => next_page, 'next_page_path' => next_page_path, 'first_page' => first_page, 'first_page_path' => first_page_path, 'last_page' => last_page, 'last_page_path' => last_page_path, 'page_trail' => page_trail } end end # class Paginator # Small utility class that handles individual pagination trails # and makes them easier to work with in Liquid class PageTrail attr_reader :num, :path, :title def initialize( num, path, title ) @num = num @path = path @title = title end #func initialize def to_liquid { 'num' => num, 'path' => path, 'title' => title } end end #class PageTrail end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/paginationPage.rb0000644000175000017500000000400513514472274026030 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # This page handles the creation of the fake pagination pages based on the original page configuration # The code does the same things as the default Jekyll/page.rb code but just forces the code to look # into the template instead of the (currently non-existing) pagination page. # # This page exists purely in memory and is not read from disk # class PaginationPage < Page def initialize(page_to_copy, cur_page_nr, total_pages, index_pageandext) @site = page_to_copy.site @base = '' @url = '' @name = index_pageandext.nil? ? 'index.html' : index_pageandext self.process(@name) # Creates the basename and ext member values # Only need to copy the data part of the page as it already contains the layout information self.data = Jekyll::Utils.deep_merge_hashes( page_to_copy.data, {} ) if !page_to_copy.data['autopage'] self.content = page_to_copy.content else # If the page is an auto page then migrate the necessary autopage info across into the # new pagination page (so that users can get the correct keys etc) if( page_to_copy.data['autopage'].has_key?('display_name') ) self.data['autopages'] = Jekyll::Utils.deep_merge_hashes( page_to_copy.data['autopage'], {} ) end end # Store the current page and total page numbers in the pagination_info construct self.data['pagination_info'] = {"curr_page" => cur_page_nr, 'total_pages' => total_pages } # Perform some validation that is also performed in Jekyll::Page validate_data! page_to_copy.path validate_permalink! page_to_copy.path # Trigger a page event #Jekyll::Hooks.trigger :pages, :post_init, self end def set_url(url_value) @url = url_value end end # class PaginationPage end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/paginationModel.rb0000644000175000017500000004516013514472274026223 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # The main model for the pagination, handles the orchestration of the pagination and calling all the necessary bits and bobs needed :) # class PaginationModel @debug = false # is debug output enabled? @logging_lambda = nil # The lambda to use for logging @page_add_lambda = nil # The lambda used to create pages and add them to the site @page_remove_lambda = nil # Lambda to remove a page from the site.pages collection @collection_by_name_lambda = nil # Lambda to get all documents/posts in a particular collection (by name) # ctor def initialize(logging_lambda, page_add_lambda, page_remove_lambda, collection_by_name_lambda) @logging_lambda = logging_lambda @page_add_lambda = page_add_lambda @page_remove_lambda = page_remove_lambda @collection_by_name_lambda = collection_by_name_lambda end def run(default_config, site_pages, site_title) # By default if pagination is enabled we attempt to find all index.html pages in the site templates = self.discover_paginate_templates(site_pages) if( templates.size.to_i <= 0 ) @logging_lambda.call "Is enabled, but I couldn't find any pagination page. Skipping pagination. "+ "Pages must have 'pagination: enabled: true' in their front-matter for pagination to work.", "warn" return end # Now for each template page generate the paginator for it templates.each do |template| # All pages that should be paginated need to include the pagination config element if template.data['pagination'].is_a?(Hash) template_config = Jekyll::Utils.deep_merge_hashes(default_config, template.data['pagination'] || {}) # Handling deprecation of configuration values self._fix_deprecated_config_features(template_config) @debug = template_config['debug'] # Is debugging enabled on the page level self._debug_print_config_info(template_config, template.path) # Only paginate the template if it is explicitly enabled # requiring this makes the logic simpler as I don't need to determine which index pages # were generated automatically and which weren't if( template_config['enabled'] ) if !@debug @logging_lambda.call "found page: "+template.path, 'debug' end # Request all documents in all collections that the user has requested all_posts = self.get_docs_in_collections(template_config['collection']) # Create the necessary indexes for the posts all_categories = PaginationIndexer.index_posts_by(all_posts, 'categories') 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) # (this is a default and must not be used in the category system) all_tags = PaginationIndexer.index_posts_by(all_posts, 'tags') all_locales = PaginationIndexer.index_posts_by(all_posts, 'locale') # TODO: NOTE!!! This whole request for posts and indexing results could be cached to improve performance, leaving like this for now during testing # Now construct the pagination data for this template page self.paginate(template, template_config, site_title, all_posts, all_tags, all_categories, all_locales) end end end #for # Return the total number of templates found return templates.size.to_i end # function run # # This function is here to retain the old compatability logic with the jekyll-paginate gem # no changes should be made to this function and it should be retired and deleted after 2018-01-01 # (REMOVE AFTER 2018-01-01) # def run_compatability(legacy_config, site_pages, site_title, all_posts) # Decomissioning error if( date = Date.strptime("20180101","%Y%m%d") <= Date.today ) raise ArgumentError.new("Legacy jekyll-paginate configuration compatibility mode has expired. Please upgrade to jekyll-paginate-v2 configuration.") end # Two month warning or general notification if( date = Date.strptime("20171101","%Y%m%d") <= Date.today ) @logging_lambda.call "Legacy pagination logic will stop working on Jan 1st 2018, update your configs before that time.", "warn" else @logging_lambda.call "Detected legacy jekyll-paginate logic running. "+ "Please update your configs to use the jekyll-paginate-v2 logic. This compatibility function "+ "will stop working after Jan 1st 2018 and your site build will throw an error.", "warn" end if template = CompatibilityUtils.template_page(site_pages, legacy_config['legacy_source'], legacy_config['permalink']) CompatibilityUtils.paginate(legacy_config, all_posts, template, @page_add_lambda) else @logging_lambda.call "Legacy pagination is enabled, but I couldn't find " + "an index.html page to use as the pagination page. Skipping pagination.", "warn" end end # function run_compatability (REMOVE AFTER 2018-01-01) # Returns the combination of all documents in the collections that are specified # raw_collection_names can either be a list of collections separated by a ',' or ' ' or a single string def get_docs_in_collections(raw_collection_names) if raw_collection_names.is_a?(String) collection_names = raw_collection_names.split(/;|,|\s/) else collection_names = raw_collection_names end docs = [] # Now for each of the collections get the docs collection_names.each do |coll_name| # Request all the documents for the collection in question, and join it with the total collection docs += @collection_by_name_lambda.call(coll_name.downcase.strip) end # Hidden documents should not not be processed anywhere. docs = docs.reject { |doc| doc['hidden'] } return docs end def _fix_deprecated_config_features(config) keys_to_delete = [] # As of v1.5.1 the title_suffix is deprecated and 'title' should be used # but only if title has not been defined already! if( !config['title_suffix'].nil? ) if( config['title'].nil? ) config['title'] = ":title" + config['title_suffix'].to_s # Migrate the old key to title end keys_to_delete << "title_suffix" # Always remove the depricated key if found end # Delete the depricated keys config.delete_if{ |k,| keys_to_delete.include? k } end def _debug_print_config_info(config, page_path) r = 20 f = "Pagination: ".rjust(20) # Debug print the config if @debug puts f + "----------------------------" puts f + "Page: "+page_path.to_s puts f + " Active configuration" puts f + " Enabled: ".ljust(r) + config['enabled'].to_s puts f + " Items per page: ".ljust(r) + config['per_page'].to_s puts f + " Permalink: ".ljust(r) + config['permalink'].to_s puts f + " Title: ".ljust(r) + config['title'].to_s puts f + " Limit: ".ljust(r) + config['limit'].to_s puts f + " Sort by: ".ljust(r) + config['sort_field'].to_s puts f + " Sort reverse: ".ljust(r) + config['sort_reverse'].to_s puts f + " Active Filters" puts f + " Collection: ".ljust(r) + config['collection'].to_s puts f + " Category: ".ljust(r) + (config['category'].nil? || config['category'] == "posts" ? "[Not set]" : config['category'].to_s) puts f + " Tag: ".ljust(r) + (config['tag'].nil? ? "[Not set]" : config['tag'].to_s) puts f + " Locale: ".ljust(r) + (config['locale'].nil? ? "[Not set]" : config['locale'].to_s) if config['legacy'] puts f + " Legacy Paginate Code Enabled" puts f + " Legacy Paginate: ".ljust(r) + config['per_page'].to_s puts f + " Legacy Source: ".ljust(r) + config['legacy_source'].to_s puts f + " Legacy Path: ".ljust(r) + config['paginate_path'].to_s end end end def _debug_print_filtering_info(filter_name, before_count, after_count) # Debug print the config if @debug puts "Pagination: ".rjust(20) + " Filtering by: "+filter_name.to_s.ljust(9) + " " + before_count.to_s.rjust(3) + " => " + after_count.to_s end end # # Rolls through all the pages passed in and finds all pages that have pagination enabled on them. # These pages will be used as templates # # site_pages - All pages in the site # def discover_paginate_templates(site_pages) candidates = [] site_pages.select do |page| # If the page has the enabled config set, supports any type of file name html or md if page.data['pagination'].is_a?(Hash) && page.data['pagination']['enabled'] candidates << page end end return candidates end # function discover_paginate_templates # Paginates the blog's posts. Renders the index.html file into paginated # directories, e.g.: page2/index.html, page3/index.html, etc and adds more # site-wide data. # # site - The Site. # template - The index.html Page that requires pagination. # config - The configuration settings that should be used # def paginate(template, config, site_title, all_posts, all_tags, all_categories, all_locales) # By default paginate on all posts in the site using_posts = all_posts # Now start filtering out any posts that the user doesn't want included in the pagination before = using_posts.size.to_i using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'category', using_posts, all_categories) self._debug_print_filtering_info('Category', before, using_posts.size.to_i) before = using_posts.size.to_i using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'tag', using_posts, all_tags) self._debug_print_filtering_info('Tag', before, using_posts.size.to_i) before = using_posts.size.to_i using_posts = PaginationIndexer.read_config_value_and_filter_posts(config, 'locale', using_posts, all_locales) self._debug_print_filtering_info('Locale', before, using_posts.size.to_i) # Apply sorting to the posts if configured, any field for the post is available for sorting if config['sort_field'] sort_field = config['sort_field'].to_s # There is an issue in Jekyll related to lazy initialized member variables that causes iterators to # break when accessing an uninitialized value during iteration. This happens for document.rb when the <=> compaison function # is called (as this function calls the 'date' field which for drafts are not initialized.) # So to unblock this common issue for the date field I simply iterate once over every document and initialize the .date field explicitly if @debug puts "Pagination: ".rjust(20) + "Rolling through the date fields for all documents" end using_posts.each do |u_post| if u_post.respond_to?('date') tmp_date = u_post.date if( !tmp_date || tmp_date.nil? ) if @debug puts "Pagination: ".rjust(20) + "Explicitly assigning date for doc: #{u_post.data['title']} | #{u_post.path}" end u_post.date = File.mtime(u_post.path) end end end 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)) } if config['sort_reverse'] using_posts.reverse! end end # Calculate the max number of pagination-pages based on the configured per page value total_pages = Utils.calculate_number_of_pages(using_posts, config['per_page']) # If a upper limit is set on the number of total pagination pages then impose that now if config['limit'] && config['limit'].to_i > 0 && config['limit'].to_i < total_pages total_pages = config['limit'].to_i end #### BEFORE STARTING REMOVE THE TEMPLATE PAGE FROM THE SITE LIST! @page_remove_lambda.call( template ) # list of all newly created pages newpages = [] # Consider the default index page name and extension indexPageName = config['indexpage'].nil? ? '' : config['indexpage'].split('.')[0] indexPageExt = config['extension'].nil? ? '' : Utils.ensure_leading_dot(config['extension']) indexPageWithExt = indexPageName + indexPageExt # In case there are no (visible) posts, generate the index file anyway total_pages = 1 if total_pages.zero? # Now for each pagination page create it and configure the ranges for the collection # This .pager member is a built in thing in Jekyll and defines the paginator implementation # Simpy override to use mine (1..total_pages).each do |cur_page_nr| # 1. Create the in-memory page # External Proc call to create the actual page for us (this is passed in when the pagination is run) newpage = PaginationPage.new( template, cur_page_nr, total_pages, indexPageWithExt ) # 2. Create the url for the in-memory page (calc permalink etc), construct the title, set all page.data values needed paginated_page_url = config['permalink'] first_index_page_url = "" if template.data['permalink'] first_index_page_url = Utils.ensure_trailing_slash(template.data['permalink']) else first_index_page_url = Utils.ensure_trailing_slash(template.dir) end paginated_page_url = File.join(first_index_page_url, paginated_page_url) # 3. Create the pager logic for this page, pass in the prev and next page numbers, assign pager to in-memory page newpage.pager = Paginator.new( config['per_page'], first_index_page_url, paginated_page_url, using_posts, cur_page_nr, total_pages, indexPageName, indexPageExt) # Create the url for the new page, make sure we prepend any permalinks that are defined in the template page before if newpage.pager.page_path.end_with? '/' newpage.set_url(File.join(newpage.pager.page_path, indexPageWithExt)) elsif newpage.pager.page_path.end_with? indexPageExt # Support for direct .html files newpage.set_url(newpage.pager.page_path) else # Support for extensionless permalinks newpage.set_url(newpage.pager.page_path+indexPageExt) end if( template.data['permalink'] ) newpage.data['permalink'] = newpage.pager.page_path end # Transfer the title across to the new page if( !template.data['title'] ) tmp_title = site_title else tmp_title = template.data['title'] end # If the user specified a title suffix to be added then let's add that to all the pages except the first if( cur_page_nr > 1 && config.has_key?('title') ) newpage.data['title'] = "#{Utils.format_page_title(config['title'], tmp_title, cur_page_nr, total_pages)}" else newpage.data['title'] = tmp_title end # Signals that this page is automatically generated by the pagination logic # (we don't do this for the first page as it is there to mask the one we removed) if cur_page_nr > 1 newpage.data['autogen'] = "jekyll-paginate-v2" end # Add the page to the site @page_add_lambda.call( newpage ) # Store the page in an internal list for later referencing if we need to generate a pagination number path later on newpages << newpage end #each.do total_pages # 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 # simplest is to include all of the links to the pages preceeding the current one # (e.g for page 1 you get the list 2, 3, 4.... and for page 2 you get the list 3,4,5...) if( config['trail'] && !config['trail'].nil? && newpages.size.to_i > 1 ) trail_before = [config['trail']['before'].to_i, 0].max trail_after = [config['trail']['after'].to_i, 0].max trail_length = trail_before + trail_after + 1 if( trail_before > 0 || trail_after > 0 ) newpages.select do | npage | idx_start = [ npage.pager.page - trail_before - 1, 0].max # Selecting the beginning of the trail idx_end = [idx_start + trail_length, newpages.size.to_i].min # Selecting the end of the trail # Always attempt to maintain the max total of pages in the trail (it will look better if the trail doesn't shrink) if( idx_end - idx_start < trail_length ) # Attempt to pad the beginning if we have enough pages idx_start = [idx_start - ( trail_length - (idx_end - idx_start) ), 0].max # Never go beyond the zero index end # Convert the newpages array into a two dimensional array that has [index, page_url] as items #puts( "Trail created for page #{npage.pager.page} (idx_start:#{idx_start} idx_end:#{idx_end})") 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'])} #puts( npage.pager.page_trail ) end #newpages.select end #if trail_before / trail_after end # if config['trail'] end # function paginate end # class PaginationV2 end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/paginationIndexer.rb0000644000175000017500000000712113514472274026554 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # Performs indexing of the posts or collection documents # as well as filtering said collections when requested by the defined filters. class PaginationIndexer # # Create a hash index for all post based on a key in the post.data table # def self.index_posts_by(all_posts, index_key) return nil if all_posts.nil? return all_posts if index_key.nil? index = {} all_posts.each do |post| next if post.data.nil? next if !post.data.has_key?(index_key) next if post.data[index_key].nil? next if post.data[index_key].size <= 0 next if post.data[index_key].to_s.strip.length == 0 # Only tags and categories come as premade arrays, locale does not, so convert any data # elements that are strings into arrays post_data = post.data[index_key] if post_data.is_a?(String) post_data = post_data.split(/;|,|\s/) end post_data.each do |key| key = key.to_s.downcase.strip # If the key is a delimetered list of values # (meaning the user didn't use an array but a string with commas) key.split(/;|,/).each do |k_split| k_split = k_split.to_s.downcase.strip #Clean whitespace and junk if !index.has_key?(k_split) index[k_split.to_s] = [] end index[k_split.to_s] << post end end end return index end # function index_posts_by # # Creates an intersection (only returns common elements) # between multiple arrays # def self.intersect_arrays(first, *rest) return nil if first.nil? return nil if rest.nil? intersect = first rest.each do |item| return [] if item.nil? intersect = intersect & item end return intersect end #function intersect_arrays # # Filters posts based on a keyed source_posts hash of indexed posts and performs a intersection of # the two sets. Returns only posts that are common between all collections # def self.read_config_value_and_filter_posts(config, config_key, posts, source_posts) return nil if posts.nil? return nil if source_posts.nil? # If the source is empty then simply don't do anything return posts if config.nil? return posts if !config.has_key?(config_key) return posts if config[config_key].nil? # Get the filter values from the config (this is the cat/tag/locale values that should be filtered on) config_value = config[config_key] # If we're dealing with a delimitered string instead of an array then let's be forgiving if( config_value.is_a?(String)) config_value = config_value.split(/;|,/) end # Now for all filter values for the config key, let's remove all items from the posts that # aren't common for all collections that the user wants to filter on config_value.each do |key| key = key.to_s.downcase.strip posts = PaginationIndexer.intersect_arrays(posts, source_posts[key]) end # The fully filtered final post list return posts end #function read_config_value_and_filter_posts end #class PaginationIndexer end #module PaginateV2 end #module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/paginationGenerator.rb0000644000175000017500000001571313514472274027112 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # # The main entry point into the generator, called by Jekyll # this function extracts all the necessary information from the jekyll end and passes it into the pagination # logic. Additionally it also contains all site specific actions that the pagination logic needs access to # (such as how to create new pages) # class PaginationGenerator < Generator # This generator is safe from arbitrary code execution. safe true # This generator should be passive with regard to its execution priority :lowest # Generate paginated pages if necessary (Default entry point) # site - The Site. # # Returns nothing. def generate(site) #begin # Generate the AutoPages first PaginateV2::AutoPages.create_autopages(site) # Retrieve and merge the pagination configuration from the site yml file default_config = Jekyll::Utils.deep_merge_hashes(DEFAULT, site.config['pagination'] || {}) # Compatibility Note: (REMOVE AFTER 2018-01-01) # If the legacy paginate logic is configured then read those values and merge with config if !site.config['paginate'].nil? Jekyll.logger.info "Pagination:","Legacy paginate configuration settings detected and will be used." # You cannot run both the new code and the old code side by side if !site.config['pagination'].nil? err_msg = "The new jekyll-paginate-v2 and the old jekyll-paginate logic cannot both be configured in the site config at the same time. Please disable the old 'paginate:' config settings by either omitting the values or setting them to 'paginate:off'." Jekyll.logger.error err_msg raise ArgumentError.new(err_msg) end default_config['per_page'] = site.config['paginate'].to_i default_config['legacy_source'] = site.config['source'] if !site.config['paginate_path'].nil? default_config['permalink'] = site.config['paginate_path'].to_s end # In case of legacy, enable pagination by default default_config['enabled'] = true default_config['legacy'] = true end # Compatibility END (REMOVE AFTER 2018-01-01) # If disabled then simply quit if !default_config['enabled'] Jekyll.logger.info "Pagination:","Disabled in site.config." return end # Handle deprecation of settings and features if( !default_config['title_suffix' ].nil? ) Jekyll::Deprecator.deprecation_message "Pagination: The 'title_suffix' configuration has been deprecated. Please use 'title'. See https://github.com/sverrirs/jekyll-paginate-v2/blob/master/README-GENERATOR.md#site-configuration" end Jekyll.logger.debug "Pagination:","Starting" ################ 0 #################### # Get all pages in the site (this will be used to find the pagination templates) all_pages = site.pages # Get the default title of the site (used as backup when there is no title available for pagination) site_title = site.config['title'] ################ 1 #################### # Specify the callback function that returns the correct docs/posts based on the collection name # "posts" are just another collection in Jekyll but a specialized version that require timestamps # This collection is the default and if the user doesn't specify a collection in their front-matter then that is the one we load # If the collection is not found then empty array is returned collection_by_name_lambda = lambda do |collection_name| coll = [] if collection_name == "all" # the 'all' collection_name is a special case and includes all collections in the site (except posts!!) # this is useful when you want to list items across multiple collections site.collections.each do |coll_name, coll_data| if( !coll_data.nil? && coll_name != 'posts') coll += coll_data.docs.select { |doc| !doc.data.has_key?('pagination') } # Exclude all pagination pages end end else # Just the one collection requested if !site.collections.has_key?(collection_name) return [] end coll = site.collections[collection_name].docs.select { |doc| !doc.data.has_key?('pagination') } # Exclude all pagination pages end return coll end ################ 2 #################### # Create the proc that constructs the real-life site page # This is necessary to decouple the code from the Jekyll site object page_add_lambda = lambda do | newpage | site.pages << newpage # Add the page to the site so that it is generated correctly return newpage # Return the site to the calling code end ################ 2.5 #################### # lambda that removes a page from the site pages list page_remove_lambda = lambda do | page_to_remove | site.pages.delete_if {|page| page == page_to_remove } end ################ 3 #################### # Create a proc that will delegate logging # Decoupling Jekyll specific logging logging_lambda = lambda do | message, type="info" | if type == 'debug' Jekyll.logger.debug "Pagination:","#{message}" elsif type == 'error' Jekyll.logger.error "Pagination:", "#{message}" elsif type == 'warn' Jekyll.logger.warn "Pagination:", "#{message}" else Jekyll.logger.info "Pagination:", "#{message}" end end ################ 4 #################### # Now create and call the model with the real-life page creation proc and site data model = PaginationModel.new(logging_lambda, page_add_lambda, page_remove_lambda, collection_by_name_lambda) if( default_config['legacy'] ) #(REMOVE AFTER 2018-01-01) Jekyll.logger.warn "Pagination:", "You are running jekyll-paginate backwards compatible pagination logic. Please ignore all earlier warnings displayed related to the old jekyll-paginate gem." all_posts = site.site_payload['site']['posts'].reject { |post| post['hidden'] } model.run_compatability(default_config, all_pages, site_title, all_posts) #(REMOVE AFTER 2018-01-01) else count = model.run(default_config, all_pages, site_title) Jekyll.logger.info "Pagination:", "Complete, processed #{count} pagination page(s)" end #rescue => ex # puts ex.backtrace # raise #end end # function generate end # class PaginationGenerator end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/defaults.rb0000644000175000017500000000236213514472274024715 0ustar danieldanielmodule Jekyll module PaginateV2::Generator # The default configuration for the Paginator DEFAULT = { 'enabled' => false, 'collection' => 'posts', 'per_page' => 10, 'permalink' => '/page:num/', # Supports :num as customizable elements 'title' => ':title - page :num', # Supports :num as customizable elements 'page_num' => 1, 'sort_reverse' => false, 'sort_field' => 'date', 'limit' => 0, # Limit how many content objects to paginate (default: 0, means all) 'trail' => { 'before' => 0, # Limits how many links to show before the current page in the pagination trail (0, means off, default: 0) 'after' => 0, # Limits how many links to show after the current page in the pagination trail (0 means off, default: 0) }, 'indexpage' => nil, # The default name of the index pages 'extension' => 'html', # The default extension for the output pages (ignored if indexpage is nil) 'debug' => false, # Turns on debug output for the gem 'legacy' => false # Internal value, do not use (will be removed after 2018-01-01) } end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/generator/compatibilityUtils.rb0000644000175000017500000001266213514472274027004 0ustar danieldanielmodule Jekyll module PaginateV2::Generator class CompatibilityPaginationPage < Page def initialize(site, base, dir, template_path) @site = site @base = base @dir = dir @template = template_path @name = 'index.html' templ_dir = File.dirname(template_path) templ_file = File.basename(template_path) # Path is only used by the convertible module and accessed below when calling read_yaml # in our case we have the path point to the original template instead of our faux new pagination page @path = if site.in_theme_dir(base) == base # we're in a theme site.in_theme_dir(base, templ_dir, templ_file) else site.in_source_dir(base, templ_dir, templ_file) end self.process(@name) self.read_yaml(templ_dir, templ_file) data.default_proc = proc do |_, key| site.frontmatter_defaults.find(File.join(templ_dir, templ_file), type, key) end end end # class CompatibilityPaginationPage # # Static utility functions that provide backwards compatibility with the old # jekyll-paginate gem that this new version superseeds (this code is here to ensure) # that sites still running the old gem work without problems # (REMOVE AFTER 2018-01-01) # # THIS CLASS IS ADAPTED FROM THE ORIGINAL IMPLEMENTATION AND WILL BE REMOVED, THERE ARE DELIBERATELY NO TESTS FOR THIS CLASS # class CompatibilityUtils # Public: Find the Jekyll::Page which will act as the pager template # # Returns the Jekyll::Page which will act as the pager template def self.template_page(site_pages, config_source, config_paginate_path) site_pages.select do |page| CompatibilityUtils.pagination_candidate?(config_source, config_paginate_path, page) end.sort do |one, two| two.path.size <=> one.path.size end.first end # Static: Determine if a page is a possible candidate to be a template page. # Page's name must be `index.html` and exist in any of the directories # between the site source and `paginate_path`. def self.pagination_candidate?(config_source, config_paginate_path, page) page_dir = File.dirname(File.expand_path(Utils.remove_leading_slash(page.path), config_source)) paginate_path = Utils.remove_leading_slash(config_paginate_path) paginate_path = File.expand_path(paginate_path, config_source) page.name == 'index.html' && CompatibilityUtils.in_hierarchy(config_source, page_dir, File.dirname(paginate_path)) end # Determine if the subdirectories of the two paths are the same relative to source # # source - the site source # page_dir - the directory of the Jekyll::Page # paginate_path - the absolute paginate path (from root of FS) # # Returns whether the subdirectories are the same relative to source def self.in_hierarchy(source, page_dir, paginate_path) return false if paginate_path == File.dirname(paginate_path) return false if paginate_path == Pathname.new(source).parent page_dir == paginate_path || CompatibilityUtils.in_hierarchy(source, page_dir, File.dirname(paginate_path)) end # Paginates the blog's posts. Renders the index.html file into paginated # directories, e.g.: page2/index.html, page3/index.html, etc and adds more # site-wide data. # def self.paginate(legacy_config, all_posts, page, page_add_lambda ) pages = Utils.calculate_number_of_pages(all_posts, legacy_config['per_page'].to_i) (1..pages).each do |num_page| pager = Paginator.new( legacy_config['per_page'], page.url, legacy_config['permalink'], all_posts, num_page, pages, '', '' ) if num_page > 1 template_full_path = File.join(page.site.source, page.path) template_dir = File.dirname(page.path) newpage = CompatibilityPaginationPage.new(page.site, page.site.source, template_dir, template_full_path) newpage.pager = pager newpage.dir = CompatibilityUtils.paginate_path(page.url, num_page, legacy_config['permalink']) newpage.data['autogen'] = "jekyll-paginate-v2" # Signals that this page is automatically generated by the pagination logic page_add_lambda.call(newpage) else page.pager = pager end end end # Static: Return the pagination path of the page # # site - the Jekyll::Site object # cur_page_nr - the pagination page number # config - the current configuration in use # # Returns the pagination path as a string def self.paginate_path(template_url, cur_page_nr, permalink_format) return nil if cur_page_nr.nil? return template_url if cur_page_nr <= 1 if permalink_format.include?(":num") permalink_format = Utils.format_page_number(permalink_format, cur_page_nr) else raise ArgumentError.new("Invalid pagination path: '#{permalink_format}'. It must include ':num'.") end Utils.ensure_leading_slash(permalink_format) end #function paginate_path end # class CompatibilityUtils end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/0000755000175000017500000000000013514472274022560 5ustar danieldanieljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/utils.rb0000644000175000017500000000741313514472274024252 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages class Utils # Static: returns a fully formatted string with the tag macro (:tag) replaced # def self.format_tag_macro(toFormat, tag, slugify_config=nil) slugify_mode = slugify_config.has_key?('mode') ? slugify_config['mode'] : nil slugify_cased = slugify_config.has_key?('cased') ? slugify_config['cased'] : false return toFormat.sub(':tag', Jekyll::Utils.slugify(tag.to_s, mode:slugify_mode, cased:slugify_cased)) end #function format_tag_macro # Static: returns a fully formatted string with the category macro (:cat) replaced # def self.format_cat_macro(toFormat, category, slugify_config=nil) slugify_mode = slugify_config.has_key?('mode') ? slugify_config['mode'] : nil slugify_cased = slugify_config.has_key?('cased') ? slugify_config['cased'] : false return toFormat.sub(':cat', Jekyll::Utils.slugify(category.to_s, mode:slugify_mode, cased:slugify_cased)) end #function format_cat_macro # Static: returns a fully formatted string with the collection macro (:coll) replaced # def self.format_coll_macro(toFormat, collection, slugify_config=nil) slugify_mode = slugify_config.has_key?('mode') ? slugify_config['mode'] : nil slugify_cased = slugify_config.has_key?('cased') ? slugify_config['cased'] : false return toFormat.sub(':coll', Jekyll::Utils.slugify(collection.to_s, mode:slugify_mode, cased:slugify_cased)) end #function format_coll_macro # Static: returns all documents from all collections defined in the hash of collections passed in # excludes all pagination pages though def self.collect_all_docs(site_collections) coll = [] site_collections.each do |coll_name, coll_data| if !coll_data.nil? coll += coll_data.docs.select { |doc| !doc.data.has_key?('pagination') }.each{ |doc| doc.data['__coll'] = coll_name } # Exclude all pagination pages and then for every page store it's collection name end end return coll end def self.ap_index_posts_by(all_posts, index_key) return nil if all_posts.nil? return all_posts if index_key.nil? index = {} all_posts.each do |post| next if post.data.nil? next if !post.data.has_key?(index_key) next if post.data[index_key].nil? next if post.data[index_key].size <= 0 next if post.data[index_key].to_s.strip.length == 0 # Only tags and categories come as premade arrays, locale does not, so convert any data # elements that are strings into arrays post_data = post.data[index_key] if post_data.is_a?(String) post_data = post_data.split(/;|,|\s/) end post_data.each do |key| key = key.strip # If the key is a delimetered list of values # (meaning the user didn't use an array but a string with commas) key.split(/;|,/).each do |raw_k_split| k_split = raw_k_split.to_s.downcase.strip #Clean whitespace and junk if !index.has_key?(k_split) # Need to store the original key value here so that I can present it to the users as a page variable they can use (unmodified, e.g. tags not being 'sci-fi' but "Sci-Fi") # Also, only interested in storing all the keys not the pages in this case index[k_split.to_s] = [k_split.to_s, raw_k_split.to_s] end end end end return index end # function index_posts_by end # class Utils end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/pages/0000755000175000017500000000000013514472274023657 5ustar danieldanieljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/pages/tagAutoPage.rb0000644000175000017500000000243713514472274026413 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages class TagAutoPage < BaseAutoPage def initialize(site, base, autopage_config, pagination_config, layout_name, tag, tag_name) # Do we have a slugify configuration available slugify_config = autopage_config.is_a?(Hash) && autopage_config.has_key?('slugify') ? autopage_config['slugify'] : nil # Construc the lambda function to set the config values, # this function received the pagination config hash and manipulates it set_autopage_data_lambda = lambda do | config | config['tag'] = tag end get_autopage_permalink_lambda = lambda do |permalink_pattern| return Utils.format_tag_macro(permalink_pattern, tag, slugify_config) end get_autopage_title_lambda = lambda do |title_pattern| return Utils.format_tag_macro(title_pattern, tag, slugify_config) end # Call the super constuctor with our custom lambda super(site, base, autopage_config, pagination_config, layout_name, set_autopage_data_lambda, get_autopage_permalink_lambda, get_autopage_title_lambda, tag_name) end #function initialize end #class TagAutoPage end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/pages/collectionAutoPage.rb0000644000175000017500000000254413514472274027772 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages class CollectionAutoPage < BaseAutoPage def initialize(site, base, autopage_config, pagination_config, layout_name, collection, collection_name) # Do we have a slugify configuration available slugify_config = autopage_config.is_a?(Hash) && autopage_config.has_key?('slugify') ? autopage_config['slugify'] : nil # Construc the lambda function to set the config values # this function received the pagination config hash and manipulates it set_autopage_data_lambda = lambda do | in_config | in_config['collection'] = collection end get_autopage_permalink_lambda = lambda do |permalink_pattern| return Utils.format_coll_macro(permalink_pattern, collection, slugify_config) end get_autopage_title_lambda = lambda do |title_pattern| return Utils.format_coll_macro(title_pattern, collection, slugify_config) end # Call the super constuctor with our custom lambda super(site, base, autopage_config, pagination_config, layout_name, set_autopage_data_lambda, get_autopage_permalink_lambda, get_autopage_title_lambda, collection_name) end #function initialize end #class CollectionAutoPage end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/pages/categoryAutoPage.rb0000644000175000017500000000252013514472274027446 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages class CategoryAutoPage < BaseAutoPage def initialize(site, base, autopage_config, pagination_config, layout_name, category, category_name) # Do we have a slugify configuration available slugify_config = autopage_config.is_a?(Hash) && autopage_config.has_key?('slugify') ? autopage_config['slugify'] : nil # Construc the lambda function to set the config values # this function received the pagination config hash and manipulates it set_autopage_data_lambda = lambda do | in_config | in_config['category'] = category end get_autopage_permalink_lambda = lambda do |permalink_pattern| return Utils.format_cat_macro(permalink_pattern, category, slugify_config) end get_autopage_title_lambda = lambda do |title_pattern| return Utils.format_cat_macro(title_pattern, category, slugify_config) end # Call the super constuctor with our custom lambda super(site, base, autopage_config, pagination_config, layout_name, set_autopage_data_lambda, get_autopage_permalink_lambda, get_autopage_title_lambda, category_name) end #function initialize end #class CategoryAutoPage end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/pages/baseAutoPage.rb0000644000175000017500000000552513514472274026553 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages class BaseAutoPage < Jekyll::Page def initialize(site, base, autopage_config, pagination_config, layout_name, set_autopage_data_lambda, get_autopage_permalink_lambda, get_autopage_title_lambda, display_name) @site = site @base = base @name = 'index.html' layout_dir = '_layouts' # Path is only used by the convertible module and accessed below when calling read_yaml # Handling themes stored in a gem @path = if site.in_theme_dir(site.source) == site.source # we're in a theme site.in_theme_dir(site.source, layout_dir, layout_name) else site.in_source_dir(site.source, layout_dir, layout_name) end self.process(@name) # Creates the base name and extension self.read_yaml(File.join(site.source, layout_dir), layout_name) # Merge the config with any config that might already be defined in the layout pagination_layout_config = Jekyll::Utils.deep_merge_hashes( pagination_config, self.data['pagination'] || {} ) # Read any possible autopage overrides in the layout page autopage_layout_config = Jekyll::Utils.deep_merge_hashes( autopage_config, self.data['autopages'] || {} ) # Now set the page specific pagination data set_autopage_data_lambda.call(pagination_layout_config) # Get permalink structure permalink_formatted = get_autopage_permalink_lambda.call(autopage_layout_config['permalink']) # Construct the title page_title = autopage_layout_config['title'] # NOTE: Should we set this before calling read_yaml as that function validates the permalink structure self.data['permalink'] = permalink_formatted @url = File.join(permalink_formatted, @name) @dir = permalink_formatted self.data['layout'] = File.basename(layout_name, File.extname(layout_name)) self.data['title'] = get_autopage_title_lambda.call( page_title ) self.data['pagination'] = pagination_layout_config # Store the pagination configuration # Add the auto page flag in there to be able to detect the page (necessary when figuring out where to load it from) # TODO: Need to re-think this variable!!! self.data['autopage'] = {"layout_path" => File.join( layout_dir, layout_name ), 'display_name' => display_name.to_s } data.default_proc = proc do |_, key| site.frontmatter_defaults.find(File.join(layout_dir, layout_name), type, key) end # Trigger a page event #Jekyll::Hooks.trigger :pages, :post_init, self end #function initialize end #class BaseAutoPage end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/defaults.rb0000644000175000017500000000355513514472274024724 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages # The default configuration for the AutoPages DEFAULT = { 'enabled' => false, 'tags' => { 'layouts' => ['autopage_tags.html'], 'title' => 'Posts tagged with :tag', 'permalink' => '/tag/:tag', 'enabled' => true, 'slugify' => { 'mode' => 'none', # [raw default pretty ascii latin], none gives back the same string 'cased'=> false # If cased is true, all uppercase letters in the result string are replaced with their lowercase counterparts. } }, 'categories' => { 'layouts' => ['autopage_category.html'], 'title' => 'Posts in category :cat', 'permalink' => '/category/:cat', 'enabled' => true, 'slugify' => { 'mode' => 'none', # [raw default pretty ascii latin], none gives back the same string 'cased'=> false # If cased is true, all uppercase letters in the result string are replaced with their lowercase counterparts. } }, 'collections' => { 'layouts' => ['autopage_collection.html'], 'title' => 'Posts in collection :coll', 'permalink' => '/collection/:coll', 'enabled' => true, 'slugify' => { 'mode' => 'none', # [raw default pretty ascii latin], none gives back the same string 'cased'=> false # If cased is true, all uppercase letters in the result string are replaced with their lowercase counterparts. } } } end # module PaginateV2::AutoPages end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2/autopages/autoPages.rb0000644000175000017500000001044313514472274025037 0ustar danieldanielmodule Jekyll module PaginateV2::AutoPages # # This function is called right after the main generator is triggered by Jekyll # This code is adapted from Stephen Crosby's code https://github.com/stevecrozz def self.create_autopages(site) # Get the configuration for the auto pages autopage_config = Jekyll::Utils.deep_merge_hashes(DEFAULT, site.config['autopages'] || {}) pagination_config = Jekyll::Utils.deep_merge_hashes(Jekyll::PaginateV2::Generator::DEFAULT, site.config['pagination'] || {}) # If disabled then don't do anything if !autopage_config['enabled'] || autopage_config['enabled'].nil? Jekyll.logger.info "AutoPages:","Disabled/Not configured in site.config." return end # TODO: Should I detect here and disable if we're running the legacy paginate code???! # Simply gather all documents across all pages/posts/collections that we have # we could be generating quite a few empty pages but the logic is just vastly simpler than trying to # figure out what tag/category belong to which collection. posts_to_use = Utils.collect_all_docs(site.collections) ############################################### # Generate the Tag pages if enabled createtagpage_lambda = lambda do | autopage_tag_config, pagination_config, layout_name, tag, tag_original_name | site.pages << TagAutoPage.new(site, site.dest, autopage_tag_config, pagination_config, layout_name, tag, tag_original_name) end autopage_create(autopage_config, pagination_config, posts_to_use, 'tags', 'tags', createtagpage_lambda) # Call the actual function ############################################### # Generate the category pages if enabled createcatpage_lambda = lambda do | autopage_cat_config, pagination_config, layout_name, category, category_original_name | site.pages << CategoryAutoPage.new(site, site.dest, autopage_cat_config, pagination_config, layout_name, category, category_original_name) end autopage_create(autopage_config, pagination_config,posts_to_use, 'categories', 'categories', createcatpage_lambda) # Call the actual function ############################################### # Generate the Collection pages if enabled createcolpage_lambda = lambda do | autopage_col_config, pagination_config, layout_name, coll_name, coll_original_name | site.pages << CollectionAutoPage.new(site, site.dest, autopage_col_config, pagination_config, layout_name, coll_name, coll_original_name) end autopage_create(autopage_config, pagination_config,posts_to_use, 'collections', '__coll', createcolpage_lambda) # Call the actual function end # create_autopages # STATIC: this function actually performs the steps to generate the autopages. It uses a lambda function to delegate the creation of the individual # page types to the calling code (this way all features can reuse the logic). # def self.autopage_create(autopage_config, pagination_config, posts_to_use, configkey_name, indexkey_name, createpage_lambda ) if !autopage_config[configkey_name].nil? ap_sub_config = autopage_config[configkey_name] if ap_sub_config ['enabled'] Jekyll.logger.info "AutoPages:","Generating #{configkey_name} pages" # Roll through all documents in the posts collection and extract the tags index_keys = Utils.ap_index_posts_by(posts_to_use, indexkey_name) # Cannot use just the posts here, must use all things.. posts, collections... index_keys.each do |index_key, value| # Iterate over each layout specified in the config ap_sub_config ['layouts'].each do | layout_name | # Use site.dest here as these pages are never created in the actual source but only inside the _site folder createpage_lambda.call(ap_sub_config, pagination_config, layout_name, index_key, value[-1]) # the last item in the value array will be the display name end end else Jekyll.logger.info "AutoPages:","#{configkey_name} pages are disabled/not configured in site.config." end end end end # module PaginateV2 end # module Jekyll jekyll-paginate-v2-1.9.4/lib/jekyll-paginate-v2.rb0000644000175000017500000000313713514472274021121 0ustar danieldaniel# Jekyll::Paginate V2 is a gem built for Jekyll 3 that generates pagiatation for posts, collections, categories and tags. # # It is based on https://github.com/jekyll/jekyll-paginate, the original Jekyll paginator # which was decommissioned in Jekyll 3 release onwards. This code is currently not officially # supported on Jekyll versions < 3.0 (although it might work) # # Author: Sverrir Sigmundarson # Site: https://github.com/sverrirs/jekyll-paginate-v2 # Distributed Under The MIT License (MIT) as described in the LICENSE file # - https://opensource.org/licenses/MIT require "jekyll-paginate-v2/version" # Files needed for the pagination generator require "jekyll-paginate-v2/generator/defaults" require "jekyll-paginate-v2/generator/compatibilityUtils" require "jekyll-paginate-v2/generator/utils" require "jekyll-paginate-v2/generator/paginationIndexer" require "jekyll-paginate-v2/generator/paginator" require "jekyll-paginate-v2/generator/paginationPage" require "jekyll-paginate-v2/generator/paginationModel" require "jekyll-paginate-v2/generator/paginationGenerator" # Files needed for the auto category and tag pages require "jekyll-paginate-v2/autopages/utils" require "jekyll-paginate-v2/autopages/defaults" require "jekyll-paginate-v2/autopages/autoPages" require "jekyll-paginate-v2/autopages/pages/baseAutoPage" require "jekyll-paginate-v2/autopages/pages/categoryAutoPage" require "jekyll-paginate-v2/autopages/pages/collectionAutoPage" require "jekyll-paginate-v2/autopages/pages/tagAutoPage" module Jekyll module PaginateV2 end # module PaginateV2 end # module Jekylljekyll-paginate-v2-1.9.4/jekyll-paginate-v2.gemspec0000644000175000017500000000327413514472274021375 0ustar danieldaniel# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'jekyll-paginate-v2/version' require 'date' Gem::Specification.new do |spec| spec.name = "jekyll-paginate-v2" spec.version = Jekyll::PaginateV2::VERSION spec.platform = Gem::Platform::RUBY spec.required_ruby_version = '>= 2.0.0' # Same as Jekyll spec.date = DateTime.now.strftime('%Y-%m-%d') spec.authors = ["Sverrir Sigmundarson"] spec.email = ["jekyll@sverrirs.com"] spec.homepage = "https://github.com/sverrirs/jekyll-paginate-v2" spec.license = "MIT" spec.summary = %q{Pagination Generator for Jekyll 3} spec.description = %q{An enhanced zero-configuration in-place replacement for the now decomissioned built-in jekyll-paginate gem. This pagination gem offers full backwards compatability as well as a slew of new frequently requested features with minimal additional site and page configuration. Optional features include auto-generation of paginated collection, tag and category pages.} spec.files = Dir['CODE_OF_CONDUCT.md', 'README*.md', 'LICENSE', 'Rakefile', '*.gemspec', 'Gemfile', 'lib/**/*', 'spec/**/*'] spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] # Gem requires Jekyll to work # ~> is the pessimistic operator and is equivalent to '>= 3.0', '< 4.0' spec.add_runtime_dependency "jekyll", "~> 3.0" # Development requires more spec.add_development_dependency "bundler", "~> 1.5" spec.add_development_dependency "rake", "~> 10.4" spec.add_development_dependency "minitest", '~> 5.4' endjekyll-paginate-v2-1.9.4/Rakefile0000644000175000017500000000057113514472274016065 0ustar danieldanielrequire 'rake' require 'rake/testtask' #$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[lib])) #require 'lib/jekyll-paginate-v2/version' Rake::TestTask.new do |t| t.libs.push 'lib' t.libs.push 'specs' t.verbose = true t.pattern = "spec/**/*_spec.rb" t.test_files = FileList['spec/**/*_spec.rb'] end desc "Run tests" task :default => [:test]jekyll-paginate-v2-1.9.4/README.md0000644000175000017500000001100513514472274015671 0ustar danieldaniel# Jekyll::Paginate V2 Pagination gem built specially for Jekyll 3 and newer that is fully backwards compatable and serves as an enhanced replacement for the previously built-in [jekyll-paginate gem](https://github.com/jekyll/jekyll-paginate). View it on [rubygems.org](https://rubygems.org/gems/jekyll-paginate-v2). [![Gem](https://img.shields.io/gem/v/jekyll-paginate-v2.svg)](https://rubygems.org/gems/jekyll-paginate-v2) [![Join the chat at https://gitter.im/jekyll-paginate-v2/Lobby](https://badges.gitter.im/jekyll-paginate-v2/Lobby.svg)](https://gitter.im/jekyll-paginate-v2/Lobby) [![Build Status](https://travis-ci.org/sverrirs/jekyll-paginate-v2.svg?branch=master)](https://travis-ci.org/sverrirs/jekyll-paginate-v2) [![Dependency Status](https://gemnasium.com/badges/github.com/sverrirs/jekyll-paginate-v2.svg)](https://gemnasium.com/github.com/sverrirs/jekyll-paginate-v2) [![Code Climate](https://codeclimate.com/github/sverrirs/jekyll-paginate-v2/badges/gpa.svg)](https://codeclimate.com/github/sverrirs/jekyll-paginate-v2) [![security](https://hakiri.io/github/sverrirs/jekyll-paginate-v2/master.svg)](https://hakiri.io/github/sverrirs/jekyll-paginate-v2/master) [![Gem](https://img.shields.io/gem/dt/jekyll-paginate-v2.svg)](https://rubygems.org/gems/jekyll-paginate-v2) Reach me at the [project issues](https://github.com/sverrirs/jekyll-paginate-v2/issues) section or via email at [jekyll@sverrirs.com](mailto:jekyll@sverrirs.com), you can also get in touch on the project's [Glitter chat room](https://gitter.im/jekyll-paginate-v2/Lobby). > The code was based on the original design of [jekyll-paginate](https://github.com/jekyll/jekyll-paginate) and features were sourced from discussions such as [#27](https://github.com/jekyll/jekyll-paginate/issues/27) (thanks [Günter Kits](https://github.com/gynter)). * [Installation](#installation) * [Example Sites](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples) * [Pagination Generator](#pagination-generator) * [Auto-Pages](#auto-pages) * [Issues / to-be-completed](#issues--to-be-completed) * [How to Contribute](#contributing) > _"Be excellent to each other"_ :heart: ## Installation ``` gem install jekyll-paginate-v2 ``` Update your [_config.yml](README-GENERATOR.md#site-configuration) and [pages](README-GENERATOR.md#page-configuration). > Although fully backwards compatible, to enable the new features this gem needs slightly extended [site yml](README-GENERATOR.md#site-configuration) configuration and miniscule additional new front-matter for the [pages to paginate on](README-GENERATOR.md#page-configuration). Now you're ready to run `jekyll serve` and your paginated files should be generated. Please see the [Examples](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples) for tips and tricks on how to configure the pagination logic. ## Pagination Generator The [Pagination Generator](README-GENERATOR.md) forms the core of the pagination logic. Calculates and generates the pagination pages. ## Auto Pages The [Auto-Pages](README-AUTOPAGES.md) is an optional feature that auto-magically generates paginated pages for all your tags, categories and collections. ## Issues / to-be-completed * Unit-tests do not cover all critical code paths * No integration tests yet [#2](https://github.com/jekyll/jekyll-paginate/pull/2) * _Exclude_ filter not implemented [#6](https://github.com/jekyll/jekyll-paginate/issues/6) * Elegant way of collecting and printing debug information during pagination I welcome all testers and people willing to give me feedback and code reviews. ## Contributing > Although this project is small it has a [code of conduct](CODE_OF_CONDUCT.md) that I hope everyone will do their best to follow when contributing to any aspects of this project. Be it discussions, issue reporting, documentation or programming. If you don't want to open issues here on Github, send me your feedback by email at [jekyll@sverrirs.com](mailto:jekyll@sverrirs.com). 1. Fork it ( https://github.com/sverrirs/jekyll-paginate-v2/fork ) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Run the unit tests (`rake`) 4. Commit your changes (`git commit -am 'Add some feature'`) 5. Build the gem locally (`gem build jekyll-paginate-v2.gemspec`) 6. Test and verify the gem locally (`gem install ./jekyll-paginate-v2-x.x.x.gem`) 7. Push to the branch (`git push origin my-new-feature`) 8. Create new Pull Request Note: This project uses [semantic versioning](http://semver.org/). jekyll-paginate-v2-1.9.4/README-GENERATOR.md0000644000175000017500000006043513514472274017230 0ustar danieldaniel# Jekyll::Paginate V2::Generator The **Generator** forms the core of the pagination logic. It is responsible for reading the posts and collections in your site and split them correctly across multiple pages according to the supplied configuration. It also performs the necessary functions to link to the previous and next pages in the page-sets that it generates.

> The code was based on the original design of [jekyll-paginate](https://github.com/jekyll/jekyll-paginate) and features were sourced from discussions such as [#27](https://github.com/jekyll/jekyll-paginate/issues/27) (thanks [Günter Kits](https://github.com/gynter)). * [Site configuration](#site-configuration) * [Page configuration](#page-configuration) * [Backwards compatibility](#backwards-compatibility-with-jekyll-paginate) * [Example Sites](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples) * [Paginating collections](#paginating-collections) + [Single collection](#paginating-a-single-collection) + [Multiple collection](#paginating-multiple-collections) + [The special 'all' collection](#the-special-all-collection) * [How to paginate categories, tags, locales](#paginate-categories-tags-locales) + [Filtering categories](#filtering-categories) + [Filtering tags](#filtering-tags) + [Filtering locales](#filtering-locales) * [How to paginate on combination of filters](#paginate-on-combination-of-filters) * [Overriding site configuration](#configuration-overrides) * [Advanced Sorting](#advanced-sorting) * [Creating Pagination Trails](#creating-pagination-trails) * [How to detect auto-generated pages](#detecting-generated-pagination-pages) * [Formatting page titles](#formatting-page-titles) * [Reading pagination meta information](#reading-pagination-meta-information) * [How to generate a JSON API](#generating-a-json-api) * [Renaming pagination file names](#renaming-pagination-file-names) * [Common issues](#common-issues) - [Dependency Error after installing](#i-keep-getting-a-dependency-error-when-running-jekyll-serve-after-installing-this-gem) - [Bundler error upgrading gem (Bundler::GemNotFound)](#im-getting-a-bundler-error-after-upgrading-the-gem-bundlergemnotfound) - [Bundler error running gem (Gem::LoadError)](#im-getting-a-bundler-error-after-upgrading-the-gem-gemloaderror) - [Pagination pages are not found](#my-pagination-pages-are-not-being-found-couldnt-find-any-pagination-page-skipping-pagination) - [Categories cause excess folder nesting](#my-pages-are-being-nested-multiple-levels-deep) - [Pagination pages overwriting each others pages](#my-pagination-pages-are-overwriting-each-others-pages) ## Site configuration The pagination gem is configured in the site's `_config.yml` file by including the `pagination` configuration element ``` yml ############################################################ # Site configuration for the Jekyll 3 Pagination Gem # The values here represent the defaults if nothing is set pagination: # Site-wide kill switch, disabled here it doesn't run at all enabled: true # Set to 'true' to enable pagination debugging. This can be enabled in the site config or only for individual pagination pages debug: false # The default document collection to paginate if nothing is specified ('posts' is default) collection: 'posts' # How many objects per paginated page, used to be `paginate` (default: 0, means all) per_page: 10 # The permalink structure for the paginated pages (this can be any level deep) permalink: '/page/:num/' # Pages are index.html inside this folder (default) #permalink: '/page/:num.html' # Pages are simple html files #permalink: '/page/:num' # Pages are html files, linked jekyll extensionless permalink style. # Optional the title format for the paginated pages (supports :title for original page title, :num for pagination page number, :max for total number of pages) title: ':title - page :num' # Limit how many pagenated pages to create (default: 0, means all) limit: 0 # Optional, defines the field that the posts should be sorted on (omit to default to 'date') sort_field: 'date' # Optional, sorts the posts in reverse order (omit to default decending or sort_reverse: true) sort_reverse: true # Optional, the default category to use, omit or just leave this as 'posts' to get a backwards-compatible behavior (all posts) category: 'posts' # Optional, the default tag to use, omit to disable tag: '' # Optional, the default locale to use, omit to disable (depends on a field 'locale' to be specified in the posts, # in reality this can be any value, suggested are the Microsoft locale-codes (e.g. en_US, en_GB) or simply the ISO-639 language code ) locale: '' # Optional,omit or set both before and after to zero to disable. # Controls how the pagination trail for the paginated pages look like. trail: before: 2 after: 2 # Optional, the default file extension for generated pages (e.g html, json, xml). # Internally this is set to html by default extension: html # Optional, the default name of the index file for generated pages (e.g. 'index.html') # Without file extension indexpage: 'index' ############################################################ ``` Also ensure that you remove the old 'jekyll-paginate' gem from your `gems` list and add this new gem instead ``` yml gems: [jekyll-paginate-v2] ``` ## Page configuration To enable pagination on a page then simply include the minimal pagination configuration in the page front-matter: ``` yml --- layout: page pagination: enabled: true --- ``` Then you can use the normal `paginator.posts` logic to iterate through the posts. ``` html {% for post in paginator.posts %}

{{ post.title }}

{% endfor %} ``` And to display pagination links, simply ``` html {% if paginator.total_pages > 1 %} {% endif %} ``` > All posts that have the `hidden: true` in their front matter are ignored by the pagination logic. Following fields area available on the `paginator` object | Field | Description | | --- | --- | | per_page | Maximum number of posts or documents on each pagination page. | | posts | The list of post objects that belong to this pagination page. | | total_posts | Total number of posts included in pagination. | | total_pages | Total number of pagination pages created. | | page | Number of the current pagination page. | | page_path | The relative Url path of the current pagination page. | | previous_page | Number of the previous page in the pagination. Nil if no previous page is available. | | previous_page_path | The relative Url of the previous page. Nil if no previous page is available. | | next_page | Number of the next page in the pagination. Nil if there is no next page available. | | next_page_path | The relative Url of the next page in the pagination. Nil if there is no next page available. | | first_page | Number of the first page in the pagination (usually this is `1`). | | first_page_path | The relative Url of the first page in the pagination. | | last_page | Number of the last page in the pagination (this is equal to `total_pages`). | | last_page_path | The relative Url of the last page in the pagination. | | page_trail | The [pagination trail](#creating-pagination-trails) structure | The code is fully backwards compatible and you will have access to all the normal paginator variables defined in the [official jekyll documentation](https://jekyllrb.com/docs/pagination/#liquid-attributes-available). Neat! :ok_hand: Don't delay, go see the [Examples](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples), they're way more useful than read-me docs at this point :) ## Backwards compatibility with jekyll-paginate This gem is fully backwards compatible with the old [jekyll-paginate](https://github.com/jekyll/jekyll-paginate) gem and can be used as a zero-configuration replacement for it. If the old site config is detected then the gem will fall back to the old logic of pagination. > You cannot run both the new pagination logic and the old one at the same time The following `_config.yml` settings are honored when running this gem in compatability mode ``` yml paginate: 8 paginate_path: "/legacy/page:num/" ``` See more about the old style of pagination at the [jekyll-paginate](https://github.com/jekyll/jekyll-paginate) page. > :bangbang: **Warning** Backwards compatibility with the old jekyll-paginate gem is currently scheduled to be removed after **1st January 2018**. Users will start receiving warning log messages when running jekyll two months before this date. ## Paginating collections By default the pagination system only paginates `posts`. If you only have `posts` and `pages` in your site you don't need to worry about a thing, everything will work as intended without you configuring anything. However if you use document collections, or would like to, then this pagination gem offers extensive support for paginating documents in one or more collections at the same time. > Collections are groups of documents that belong together but should not be grouped by date. > See more about ['collections'](http://ben.balter.com/2015/02/20/jekyll-collections/) on Ben Balters blog. ### Paginating a single collection Lets expand on Ben's collection discussion (linked above). Let's say that you have hundreds of cupcake pages in your cupcake collection. To create a pagination page for only documents from the cupcake collection you would do this ``` yml --- layout: page title: All Cupcakes pagination: enabled: true collection: cupcakes --- ``` ### Paginating multiple collections Lets say that you want to create a single pagination page for only small cakes on your page (you have both cupcakes and cookies to sell). You could do that like this ``` yml --- layout: page title: Lil'bits pagination: enabled: true collection: cupcakes, cookies --- ``` ### The special 'all' collection Now your site has grown and you have multiple cake collections on it and you want to have a single page that paginates all of your collections at the same time. You can use the special `all` collection name for this. ``` yml --- layout: page title: All the Cakes! pagination: enabled: true collection: all --- ``` > Note: Due to the `all` keyword being reserved for this feature, you cannot have a collection called `all` in your site configuration. Sorry. ## Paginate categories, tags, locales Enabling pagination for specific categories, tags or locales is as simple as adding values to the pagination page front-matter and corresponding values in the posts. ### Filtering categories Filter single category 'software' ``` yml --- layout: post pagination: enabled: true category: software --- ``` Filter multiple categories (lists only posts belonging to all categories) ``` yml pagination: enabled: true category: software, ruby ``` > To define categories you can either specify them in the front-matter or through the [directory structure](http://jekyllrb.com/docs/variables/#page-variables) of your jekyll site (Categories are derived from the directory structure above the \_posts directory). You can actually use both approaches to assign your pages to multiple categories. ### Filtering tags Filter on a single tag ``` yml pagination: enabled: true tag: cool ``` Filter on multiple tags ``` yml pagination: enabled: true tag: cool, life ``` > When specifying tags in your posts make sure that the values are not enclosed in single quotes (double quotes are fine). If they are you will get a cryptic error when generating your site that looks like _"Error: could not read file : did not find expected key while parsing a block mapping at line 2 column 1"_ ### Filtering locales In the case your site offers multiple languages you can include a `locale` item in your post front matter. The paginator can then use this value to filter on The category page front-matter would look like this ``` yml pagination: enabled: true locale: en_US ``` Then for the relevant posts, include the `locale` variable in their front-matter ``` yml locale: en_US ``` ## Paginate on combination of filters Including only posts from categories 'ruby' and 'software' written in English ``` yml pagination: enabled: true category: software, ruby locale: en_US, en_GB, en_WW ``` Only showing posts tagged with 'cool' and in category 'cars' ``` yml pagination: enabled: true category: cars tag: cool ``` ... and so on and so on ## Configuration overrides All of the configuration elements from the `_config.yml` file can be overwritten in the pagination pages. E.g. if you want one category page to have different permalink structure simply override the item like so ``` yml pagination: enabled: true category: cars permalink: '/cars/:num/' ``` Overriding sorting to sort by the post title in ascending order for another paginated page could be done like so ``` yml pagination: enabled: true category: ruby sort_field: 'title' sort_reverse: false ``` ## Advanced Sorting Sorting can be done by any field that is available in the post front-matter. You can even sort by nested fields. > When sorting by nested fields separate the fields with a colon `:` character. As an example, assuming all your posts have the following front-matter ``` yml --- layout: post author: name: first: "John" last: "Smith" born: 1960 --- ``` You can define pagination sorting on the nested `first` field like so ``` yml --- layout: page title: "Authors by first name" pagination: enabled: true sort_field: 'author:name:first' --- ``` To sort by the `born` year in decending order (youngest first) ``` yml --- layout: page title: "Authors by birth year" pagination: enabled: true sort_field: 'author:born' sort_reverse: true --- ``` ## Creating Pagination Trails

Creating a trail structure for your pagination as shown above can be achieved by enabling the `trail` configuration and including a little extra code in your liquid templates. ``` yml pagination: trail: before: 2 # The number of links before the current page after: 2 # The number of links after the current page ``` Your layout file would then have to include code similar to the following to generate the correct HTML structure ``` HTML {% if paginator.page_trail %} {% for trail in paginator.page_trail %}
  • {{ trail.num }}
  • {% endfor %} {% endif %} ``` _See [example 3](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples/03-tags) for a demo of a pagination trail_ The `trail` object exposes three properties: * `num`: The number of the page * `path`: The path to the page * `title`: The title of the page The algorithm will always attempt to keep the same trail length for all pages (`trail length = before + after + 1`). As an example if we have only 7 pagination pages in total and the user is currently on page 6 then the trail would look like this

    Different number of before and after trail links can be specified. Below is an example of how the yml config below would look like when on the same page 4 ``` yml pagination: trail: before: 1 after: 3 ```

    ## Detecting generated pagination pages To identify the auto-generated pages that are created by the pagination logic when iterating through collections such as `site.pages` the `page.autogen` variable can be used like so ``` {% for my_page in site.pages %} {% if my_page.title and my_page.autogen == nil %}

    {{ my_page.title | escape }}

    {% endif %} {% endfor %} ``` _In this example only pages that have a title and are not auto-generated are included._ This variable is created and assigned the value `page.autogen = "jekyll-paginate-v2"` by the pagination logic. This way you can detect which pages are auto-generated and by what gem. ## Formatting page titles The `title` field in both the site.config and the front-matter configuration supports the following macros. | Text | Replaced with | Example | | --- | --- | --- | | :title | original page title | Page with `title: "Index"` and paginate config `title: ":title - split"` becomes `Index - split` | | :num | number of the current page | Page with `title: "Index"` and paginate config `title: ":title (page :num)"` the second page becomes `Index (page 2)` | | :max | total number of pages | Page with paginate config `title: ":num of :max"` the third page of 10 will become `3 of 10"` | ## Reading pagination meta information Each pagination page defines an information structure `pagination_info` that is available to the liquid templates. This structure contains meta information for the pagination process, such as current pagination page and the total number of paginated pages. The following fields are available | Field | Description | | --- | --- | | curr_page | The number of the current pagination page | | total_pages | The total number of pages in this pagination | Below is an example on how to print out a "Page x of n" in the pagination layout ``` html

    Page {{page.pagination_info.curr_page}} of {{page.pagination_info.total_pages}}

    ``` ## Generating a JSON API Delivering content via an API is useful, for a lot of the same reasons that pagination is useful. We want to delivery content, in such a way, that is: 1. Easy for the user to consume. 2. Easy for the browser to load. Paginating content meets both of these requirements, but developers are limited to presenting content statically rather than dynamically. Some example of dynamic content delivery are: - Pop up modals - Infinite scrolling - Multi-tiered pagination (e.g. Netflix UI horizontal scrolling for multiple movie categories) ### So how do I generate a JSON API for Jekyll? First, create a new jekyll page and set its layout to `null` to avoid any extra html to show up. Next, use the `extension` and `indexpage` option to customize the output of the page and its paginated content as JSON files. > Note that the `indexpage` field also supports the same macros as the permalink field Here's an example page: ``` --- layout: null permalink: /api pagination: permalink: '' enabled: true extension: .json indexpage: 'feed-:num' --- { "pages": [{% for post in paginator.posts %} {% if forloop.first != true %},{% endif %} { "title": "{{ post.title }}", "link": "{{ post.url }}" }{% endfor %} ] } ``` Next, run `jekyll build`. This will generate a set of paginated JSON files under the folder `/api`. These JSON files can be loaded via Javascript/AJAX to dynamically load content into your site. Below's an example set of routes that the configuration would generate: - http://localhost:4000/api/feed-1.json - http://localhost:4000/api/feed-2.json - http://localhost:4000/api/feed-3.json And here is an example of one of the feed.json files that are created given the markup above ``` { "pages": [ { "title": "Narcisse Snake Pits", "link": "/2016/11/narcisse-snake-pits.html" },{ "title": "Luft-Fahrzeug-Gesellschaft", "link": "/2016/11/luft-fahrzeug-gesellschaft.html" },{ "title": "Rotary engine", "link": "/2016/11/rotary-engine.html" } ], "next": "/api/feed-11.json", "prev": "/api/feed-9.json", "first": "/api/feed-1.json" } ``` For further information see [Example 4](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples/04-jsonapi), that project can serve as a starting point for your experiments with this feature. ### How did you generate those 'next', 'prev' and 'first' links? All the normal paginator variables can be used in these JSON feed files. You can use them to achive quite powerful features such as pre-loading and detecting when there are no more feeds to load. ``` {% if paginator.next_page %} ,"next": "{{ paginator.next_page_path }}" {% endif %} {% if paginator.last_page %} ,"prev": "{{ paginator.last_page_path }}" {% endif %} {% if paginator.first_page %} ,"first": "{{ paginator.first_page_path }}" {% endif %} ``` ## Renaming pagination file names By default the pagination system creates all paginated pages as `index.html`. The system provides an option to override this name and file extension with the ```yml indexpage: index extension: html ``` If you wanted to generate all pagination files as `default.htm` then the settings should be configured as follows ```yml indexpage: default extension: htm ``` ## Common issues ### I keep getting a dependency error when running jekyll serve after installing this gem > Dependency Error: Yikes! It looks like you don't have jekyll-paginate-v2 or one of its dependencies installed... Check your `Gemfile` in the site root. Ensure that the jekyll-paginate-v2 gem is present in the jekyll_plugins group like the example below. If this group is missing add to the file. ``` ruby group :jekyll_plugins do gem "jekyll-paginate-v2" end ``` ### I'm getting a bundler error after upgrading the gem (Bundler::GemNotFound) > bundler/spec_set.rb:95:in `block in materialize': Could not find jekyll-paginate-v2-1.0.0 in any of the sources (Bundler::GemNotFound) Delete your `Gemfile.lock` file and try again. ### I'm getting a bundler error after upgrading the gem (Gem::LoadError) > bundler/runtime.rb:40:in 'block in setup': You have already activated addressable 2.5.0, but your Gemfile requires addressable 2.4.0. Prepending `bundle exec` to your command may solve this. (Gem::LoadError) Delete your `Gemfile.lock` file and try again. ### My pagination pages are not being found (Couldn't find any pagination page. Skipping pagination) > Pagination: Is enabled, but I couldn't find any pagination page. Skipping pagination... * Ensure that you have the correct minimum front-matter in the pagination pages ``` yml pagination: enabled: true ``` * You can place pagination logic into either the pages or liquid templates (templates are stored under the `_layouts/` and `_includes/` folders). ### My pages are being nested multiple levels deep When using `categories` for posts it is advisable to explicitly state a `permalink` structure in your `_config.yml` file. ``` permalink: /:year/:month/:title.html ``` This is because the default behavior in Jekyll is to nest pages for every category that they belong to and Jekyll unfortunately does not understand multi-categories separated with `,` or `;` but instead does all separation on `[space]` only. ### My pagination pages are overwriting each others pages If you specify multiple pages that paginate in the site root then you must give them unique and separate pagination permalink. This link is set in the pagination page front-matter like so ``` yml pagination: enabled: true permalink: '/cars/:num/' ``` Make absolutely sure that your pagination permalink paths do not clash with any other paths in your final site. For simplicity it is recommended that you keep all custom pagination (non root index.html) in a single or multiple separate sub folders under your site root. jekyll-paginate-v2-1.9.4/README-AUTOPAGES.md0000644000175000017500000001277613514472274017237 0ustar danieldaniel# Jekyll::Paginate V2::AutoPages **AutoPages** are an optional pagination addon that can automatically generate paginated pages for all your collections, tags and categories used in the pages on your site. This is useful if you have a large site where paginating the contents of your collections, tags or category lists provides a better user experience.

    > This feature is based on [code](https://github.com/stevecrozz/lithostech.com/blob/master/_plugins/tag_indexes.rb) written and graciously donated by [Stephen Crosby](https://github.com/stevecrozz). Thanks! :) * [Site configuration](#site-configuration) * [Simple configuration](#simple-configuration) + [Obtaining the original Tag or Category name](#obtaining-the-original-tag-or-category-name) * [Advanced configuration](#advanced-configuration) * [Specialised pages](#specialised-pages) * [Considerations](#considerations) + [Title should not contain pagination macros](#title-should-not-contain-pagination-macros) * [Example Sites](https://github.com/sverrirs/jekyll-paginate-v2/tree/master/examples) * [Common issues](#common-issues) :warning: Please note, this feature is still **experimental** and might not be ready for production yet. ## Site configuration ``` yml ############################################################ # Site configuration for the Auto-Pages feature # The values here represent the defaults if nothing is set autopages: # Site-wide kill switch, disable here and it doesn't run at all enabled: false # Category pages, omit entire config element to disable categories: # Optional, the list of layouts that should be processed for every category found in the site layouts: - 'autopage_category.html' # Optional, the title that each category paginate page should get (:cat is replaced by the Category name) title: 'Posts in category :cat' # Optional, the permalink for the pagination page (:cat is replaced), # the pagination permalink path is then appended to this permalink structure permalink: '/category/:cat' # Collection pages, omit to disable collections: layouts: - 'autopage_collection.html' title: 'Posts in collection :coll' # :coll is replaced by the collection name permalink: '/collection/:coll' # Tag pages, omit to disable tags: layouts: - 'autopage_tags.html' title: 'Posts tagged with :tag' # :tag is replaced by the tag name permalink: '/tag/:tag' ``` ## Simple configuration The only thing needed to enable the auto pages is to add the configuration element to the site.config file and then create a layout that the pages should use. Example of a simple autopage layout can be seen in Example 3 [examples/03-tags/_layouts/autopage_tags.html](https://github.com/sverrirs/jekyll-paginate-v2/blob/master/examples/03-tags/_layouts/autopage_tags.html). The layout does not need anything special beyond the normal pagination logic (e.g. `for post in paginator.posts` and then the next/prev arrows). You can either name the layouts by the default names (see site configuration section above) or give them a custom name and add them to the `layouts:` configuration for the relevant type of autopage. ### Obtaining the original Tag or Category name Internally the autopage system will trim and downcase the indexing key (tag, category or collection name). To retrieve the original name of the index key you can use the `autopages.display_name` liquid variable. ``` html

    All pages tagged with {% if page.autopages %}{{page.autopages.display_name}}{% endif %}

    ``` This variable returns the untouched key value. As an example if your site uses the tag `Science-Fiction` then ``` page.tag = "science-fiction" page.autopages.display_name = "Science-Fiction" ``` See [#6](https://github.com/sverrirs/jekyll-paginate-v2/issues/6) for more information. ## Advanced configuration You can customize the look an feel of the permalink structure and the title for the auto-pages. You can also add front-matter `pagination` configuration to the layout pages you're using to specify things like sorting, filtering and all the other configuration options that are available in the normal pagination generator. > Keep in mind that when the autopages are paginated the pagination permalink structure and pagination title suffix is appended to them. ## Specialised pages Special autopages can be used that leverage the powerful filtering and sorting features of the pagination logic. For example you might want to create special pages autopages that paginate all tags in a certain collection or certain tags within a category. To achieve this you specify the `pagination` front-matter configuration in the autopage layout file. An example of this can be found in [examples/03-tags/_layouts/autopage_collections_tags.html](https://github.com/sverrirs/jekyll-paginate-v2/blob/master/examples/03-tags/_layouts/autopage_collections_tags.html). This page creates paginated pages for all tags that are found in the special _all_ collections. ## Considerations ### Title should not contain pagination macros There is no need to include the pagination title macros `:num`, `:max` or `:title` in the title configuration. The autopages will use the title configuration from the pagination configuration itself. ## Common issues _None reported so far_jekyll-paginate-v2-1.9.4/LICENSE0000644000175000017500000000212613514472274015423 0ustar danieldanielThe MIT License (MIT) Copyright (c) 2016 Sverrir Sigmundarson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jekyll-paginate-v2-1.9.4/Gemfile0000644000175000017500000000055713514472274015717 0ustar danieldanielsource 'https://rubygems.org' # Specify your gem's dependencies in jekyll-paginate-v2.gemspec gemspec if ENV["JEKYLL_VERSION"] gem "jekyll", "~> #{ENV["JEKYLL_VERSION"]}" end # adding dev-dependencies to Gemfile (instead of gemspec) allows calling # `bundle exec [executable] [options]` more easily. group :test do gem "rubocop", "~> 0.51.0" end jekyll-paginate-v2-1.9.4/CODE_OF_CONDUCT.md0000644000175000017500000000670613514472274017225 0ustar danieldaniel# Contributor Covenant Code of Conduct > “Be kind, > > for everyone you meet is fighting a harder battle.” > > --Plato ## TL;DR Try to be the best version of yourself and to be excellent to others. :purple_heart: ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [jekyll@sverrirs.com](mailto:jekyll@sverrirs.com). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/