2022-08-13 06:11:36 +00:00
|
|
|
package models
|
|
|
|
|
|
|
|
import (
|
2024-07-07 19:45:42 +00:00
|
|
|
"fmt"
|
2022-08-13 22:39:31 +00:00
|
|
|
"math"
|
2024-07-07 19:45:42 +00:00
|
|
|
"math/rand"
|
2022-08-13 06:11:36 +00:00
|
|
|
"net/http"
|
|
|
|
"strconv"
|
2022-08-31 05:13:57 +00:00
|
|
|
|
|
|
|
"code.nonshy.com/nonshy/website/pkg/config"
|
2024-07-07 19:45:42 +00:00
|
|
|
"code.nonshy.com/nonshy/website/pkg/encryption"
|
2022-08-13 06:11:36 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Pagination result object.
|
|
|
|
type Pagination struct {
|
2022-08-31 05:13:57 +00:00
|
|
|
Page int // provide <0 to mean "last page"
|
2022-08-13 06:11:36 +00:00
|
|
|
PerPage int
|
|
|
|
Total int64
|
|
|
|
Sort string
|
2022-08-31 05:13:57 +00:00
|
|
|
|
|
|
|
// privates
|
|
|
|
lastPage bool
|
2022-08-13 06:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2022-08-14 00:42:42 +00:00
|
|
|
if a <= 0 {
|
2022-08-31 05:13:57 +00:00
|
|
|
p.lastPage = true
|
2022-08-13 06:11:36 +00:00
|
|
|
a = 1
|
|
|
|
}
|
|
|
|
p.Page = a
|
2022-08-14 00:42:42 +00:00
|
|
|
} else {
|
|
|
|
p.Page = 1
|
2022-08-13 06:11:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-07 19:45:42 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-08-13 06:11:36 +00:00
|
|
|
// Iter the pages, for templates.
|
2024-07-07 19:45:42 +00:00
|
|
|
//
|
|
|
|
// 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.
|
2022-08-13 06:11:36 +00:00
|
|
|
func (p *Pagination) Iter() []Page {
|
2022-08-13 22:39:31 +00:00
|
|
|
var (
|
2024-07-07 19:45:42 +00:00
|
|
|
pages = []Page{}
|
|
|
|
total = p.Pages()
|
2022-08-13 22:39:31 +00:00
|
|
|
)
|
|
|
|
for i := 1; i <= total; i++ {
|
2022-08-13 06:11:36 +00:00
|
|
|
pages = append(pages, Page{
|
|
|
|
Page: i,
|
|
|
|
IsCurrent: i == p.Page,
|
|
|
|
})
|
2022-08-31 05:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Do we have A LOT of pages?
|
2024-07-07 19:45:42 +00:00
|
|
|
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{}
|
|
|
|
)
|
2022-08-31 05:13:57 +00:00
|
|
|
|
2024-07-07 19:45:42 +00:00
|
|
|
// If we are currently selected on an overflow page, set the label to match.
|
|
|
|
if overflow.IsCurrent {
|
|
|
|
overflow.Page = p.Page
|
2022-08-31 05:13:57 +00:00
|
|
|
}
|
|
|
|
|
2024-07-07 19:45:42 +00:00
|
|
|
result = pages[:endLength]
|
|
|
|
result = append(result, overflow)
|
|
|
|
result = append(result, pages[end:]...)
|
|
|
|
|
2022-08-31 05:13:57 +00:00
|
|
|
return result
|
2022-08-13 06:11:36 +00:00
|
|
|
}
|
|
|
|
return pages
|
|
|
|
}
|
|
|
|
|
2024-07-07 19:45:42 +00:00
|
|
|
// 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))))
|
|
|
|
}
|
|
|
|
|
2023-06-16 02:40:40 +00:00
|
|
|
func (p Pagination) Pages() int {
|
2023-02-14 06:19:18 +00:00
|
|
|
if p.PerPage == 0 {
|
|
|
|
return 0
|
|
|
|
}
|
2022-08-13 22:39:31 +00:00
|
|
|
return int(math.Ceil(float64(p.Total) / float64(p.PerPage)))
|
|
|
|
}
|
|
|
|
|
2022-08-13 06:11:36 +00:00
|
|
|
func (p *Pagination) GetOffset() int {
|
2022-08-31 05:13:57 +00:00
|
|
|
// Are we looking for the FINAL page?
|
|
|
|
if p.lastPage && p.Pages() >= 1 {
|
|
|
|
p.Page = p.Pages()
|
|
|
|
}
|
2022-08-13 06:11:36 +00:00
|
|
|
return (p.Page - 1) * p.PerPage
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pagination) HasNext() bool {
|
2022-08-13 22:39:31 +00:00
|
|
|
return p.Page < p.Pages()
|
2022-08-13 06:11:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pagination) HasPrevious() bool {
|
|
|
|
return p.Page > 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pagination) Next() int {
|
2022-08-13 22:39:31 +00:00
|
|
|
if p.Page >= p.Pages() {
|
|
|
|
return p.Pages()
|
2022-08-13 06:11:36 +00:00
|
|
|
}
|
|
|
|
return p.Page + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pagination) Previous() int {
|
|
|
|
if p.Page > 1 {
|
|
|
|
return p.Page - 1
|
|
|
|
}
|
|
|
|
return 1
|
|
|
|
}
|