ref: 49f20bbc9bf4eb9ecedfa0fd9dde484256ae91d1
dir: /hugolib/pagination.go/
// Copyright © 2013-14 Steve Francia <spf@spf13.com>.
//
// Licensed under the Simple Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://opensource.org/licenses/Simple-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
	"errors"
	"fmt"
	"github.com/spf13/hugo/helpers"
	"github.com/spf13/viper"
	"html/template"
	"math"
	"path"
)
type pager struct {
	number int
	*paginator
}
type pagers []*pager
var paginatorEmptyPages Pages
type paginator struct {
	paginatedPages []Pages
	pagers
	paginationURLFactory
	total int
	size  int
}
type paginationURLFactory func(int) string
// PageNumber returns the current page's number in the pager sequence.
func (p *pager) PageNumber() int {
	return p.number
}
// URL returns the URL to the current page.
func (p *pager) URL() template.HTML {
	return template.HTML(p.paginationURLFactory(p.PageNumber()))
}
// Url is deprecated. Will be removed in 0.15.
func (p *pager) Url() template.HTML {
    helpers.Deprecated("Paginator", ".Url", ".URL")
    return p.URL()
}
// Pages returns the elements on this page.
func (p *pager) Pages() Pages {
	if len(p.paginatedPages) == 0 {
		return paginatorEmptyPages
	}
	return p.paginatedPages[p.PageNumber()-1]
}
// NumberOfElements gets the number of elements on this page.
func (p *pager) NumberOfElements() int {
	return len(p.Pages())
}
// HasPrev tests whether there are page(s) before the current.
func (p *pager) HasPrev() bool {
	return p.PageNumber() > 1
}
// Prev returns the pager for the previous page.
func (p *pager) Prev() *pager {
	if !p.HasPrev() {
		return nil
	}
	return p.pagers[p.PageNumber()-2]
}
// HasNext tests whether there are page(s) after the current.
func (p *pager) HasNext() bool {
	return p.PageNumber() < len(p.paginatedPages)
}
// Next returns the pager for the next page.
func (p *pager) Next() *pager {
	if !p.HasNext() {
		return nil
	}
	return p.pagers[p.PageNumber()]
}
// First returns the pager for the first page.
func (p *pager) First() *pager {
	return p.pagers[0]
}
// Last returns the pager for the last page.
func (p *pager) Last() *pager {
	return p.pagers[len(p.pagers)-1]
}
// Pagers returns a list of pagers that can be used to build a pagination menu.
func (p *paginator) Pagers() pagers {
	return p.pagers
}
// PageSize returns the size of each paginator page.
func (p *paginator) PageSize() int {
	return p.size
}
// TotalPages returns the number of pages in the paginator.
func (p *paginator) TotalPages() int {
	return len(p.paginatedPages)
}
// TotalNumberOfElements returns the number of elements on all pages in this paginator.
func (p *paginator) TotalNumberOfElements() int {
	return p.total
}
func splitPages(pages Pages, size int) []Pages {
	var split []Pages
	for low, j := 0, len(pages); low < j; low += size {
		high := int(math.Min(float64(low+size), float64(len(pages))))
		split = append(split, pages[low:high])
	}
	return split
}
// Paginator gets this Node's paginator if it's already created.
// If it's not, one will be created with all pages in Data["Pages"].
func (n *Node) Paginator() (*pager, error) {
	var initError error
	n.paginatorInit.Do(func() {
		if n.paginator != nil {
			return
		}
		pagers, err := paginatePages(n.Data["Pages"], n.URL)
		if err != nil {
			initError = err
		}
		if len(pagers) > 0 {
			// the rest of the nodes will be created later
			n.paginator = pagers[0]
			n.Site.addToPaginationPageCount(uint64(n.paginator.TotalPages()))
		}
	})
	if initError != nil {
		return nil, initError
	}
	return n.paginator, nil
}
// Paginator on Page isn't supported, calling this yields an error.
func (p *Page) Paginator() (*pager, error) {
	return nil, errors.New("Paginators not supported for content pages.")
}
// Paginate on Page isn't supported, calling this yields an error.
func (p *Page) Paginate(seq interface{}) (*pager, error) {
	return nil, errors.New("Paginators not supported for content pages.")
}
// Paginate gets this Node's paginator if it's already created.
// If it's not, one will be created with the qiven sequence.
// Note that repeated calls will return the same result, even if the sequence is different.
func (n *Node) Paginate(seq interface{}) (*pager, error) {
	var initError error
	n.paginatorInit.Do(func() {
		if n.paginator != nil {
			return
		}
		pagers, err := paginatePages(seq, n.URL)
		if err != nil {
			initError = err
		}
		if len(pagers) > 0 {
			// the rest of the nodes will be created later
			n.paginator = pagers[0]
			n.Site.addToPaginationPageCount(uint64(n.paginator.TotalPages()))
		}
	})
	if initError != nil {
		return nil, initError
	}
	return n.paginator, nil
}
func paginatePages(seq interface{}, section string) (pagers, error) {
	paginateSize := viper.GetInt("paginate")
	if paginateSize <= 0 {
		return nil, errors.New("'paginate' configuration setting must be positive to paginate")
	}
	var pages Pages
	switch seq.(type) {
	case Pages:
		pages = seq.(Pages)
	case *Pages:
		pages = *(seq.(*Pages))
	case WeightedPages:
		pages = (seq.(WeightedPages)).Pages()
	case PageGroup:
		pages = (seq.(PageGroup)).Pages
	default:
		return nil, errors.New(fmt.Sprintf("unsupported type in paginate, got %T", seq))
	}
	urlFactory := newPaginationURLFactory(section)
	paginator, _ := newPaginator(pages, paginateSize, urlFactory)
	pagers := paginator.Pagers()
	return pagers, nil
}
func newPaginator(pages Pages, size int, urlFactory paginationURLFactory) (*paginator, error) {
	if size <= 0 {
		return nil, errors.New("Paginator size must be positive")
	}
	split := splitPages(pages, size)
	p := &paginator{total: len(pages), paginatedPages: split, size: size, paginationURLFactory: urlFactory}
	var ps pagers
	if len(split) > 0 {
		ps = make(pagers, len(split))
		for i := range p.paginatedPages {
			ps[i] = &pager{number: (i + 1), paginator: p}
		}
	} else {
		ps = make(pagers, 1)
		ps[0] = &pager{number: 1, paginator: p}
	}
	p.pagers = ps
	return p, nil
}
func newPaginationURLFactory(pathElements ...string) paginationURLFactory {
	paginatePath := viper.GetString("paginatePath")
	return func(page int) string {
		var rel string
		if page == 1 {
			rel = fmt.Sprintf("/%s/", path.Join(pathElements...))
		} else {
			rel = fmt.Sprintf("/%s/%s/%d/", path.Join(pathElements...), paginatePath, page)
		}
		return helpers.URLizeAndPrep(rel)
	}
}