package models import ( "fmt" "math" "math/rand" "net/http" "strconv" "code.nonshy.com/nonshy/website/pkg/config" "code.nonshy.com/nonshy/website/pkg/encryption" ) // Pagination result object. type Pagination struct { Page int // provide <0 to mean "last page" PerPage int Total int64 Sort string // privates lastPage bool } // Load the page from form or query parameters. func (p *Pagination) ParsePage(r *http.Request) { raw := r.FormValue("page") a, err := strconv.Atoi(raw) if err == nil { if a <= 0 { p.lastPage = true a = 1 } p.Page = a } else { p.Page = 1 } } // Page for Iter. type Page struct { Page int // label to show in the button IsCurrent bool // highlight the currently selected page button // this "button" is a drop-down menu of page numbers in the middle IsOverflow bool overflowFrom int overflowTo int } // Iter the pages, for templates. // // If there are fewer than 7 pages (configurable) this returns a simple slice of buttons // for each of the 7 pages to be drawn. If there are a LOT of pages, the middle button of // the pager should be a drop-down menu to pick from the pages within. func (p *Pagination) Iter() []Page { var ( pages = []Page{} total = p.Pages() ) for i := 1; i <= total; i++ { pages = append(pages, Page{ Page: i, IsCurrent: i == p.Page, }) } // Do we have A LOT of pages? if len(pages) > config.PagerButtonLimit+1 { // The left half of the buttons should be pages 1 thru N. // The right half are the final pages M thru Last. // In the middle will be the overflow drop-down of middle pages. var ( endLength = config.PagerButtonLimit / 2 start = endLength + 1 end = len(pages) - endLength overflow = Page{ Page: start, IsCurrent: p.Page >= start && p.Page <= end, IsOverflow: true, overflowFrom: start, overflowTo: end, } result = []Page{} ) // If we are currently selected on an overflow page, set the label to match. if overflow.IsCurrent { overflow.Page = p.Page } result = pages[:endLength] result = append(result, overflow) result = append(result, pages[end:]...) return result } return pages } // IterOverflow: if the Page represents an overflow drop-down menu, iterate the members of the menu // for easy template integration. func (p Page) IterOverflow() []Page { var result = []Page{} for i := p.overflowFrom; i <= p.overflowTo; i++ { result = append(result, Page{ Page: i, IsCurrent: p.Page == i, }) } return result } // UniqueSerialID will return a unique JavaScript ID. // // It is used in front-end pages such as for Pagination drop-down menus, which may appear multiple times // on a page and each use needs a unique ID attribute to connect the button to the dropdown. func (p *Pagination) UniqueSerialID() string { return encryption.Hash([]byte(fmt.Sprintf("%d", rand.Intn(9000000)))) } func (p Pagination) Pages() int { if p.PerPage == 0 { return 0 } return int(math.Ceil(float64(p.Total) / float64(p.PerPage))) } func (p *Pagination) GetOffset() int { // Are we looking for the FINAL page? if p.lastPage && p.Pages() >= 1 { p.Page = p.Pages() } return (p.Page - 1) * p.PerPage } func (p *Pagination) HasNext() bool { return p.Page < p.Pages() } func (p *Pagination) HasPrevious() bool { return p.Page > 1 } func (p *Pagination) Next() int { if p.Page >= p.Pages() { return p.Pages() } return p.Page + 1 } func (p *Pagination) Previous() int { if p.Page > 1 { return p.Page - 1 } return 1 }