github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/Unknwon/paginater/paginater.go (about)

     1  // Copyright 2015 Unknwon
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // Package paginater is a helper module for custom pagination calculation.
    16  package paginater
    17  
    18  // Paginater represents a set of results of pagination calculations.
    19  type Paginater struct {
    20  	total     int
    21  	pagingNum int
    22  	current   int
    23  	numPages  int
    24  }
    25  
    26  // New initialize a new pagination calculation and returns a Paginater as result.
    27  func New(total, pagingNum, current, numPages int) *Paginater {
    28  	if pagingNum <= 0 {
    29  		pagingNum = 1
    30  	}
    31  	if current <= 0 {
    32  		current = 1
    33  	}
    34  	p := &Paginater{total, pagingNum, current, numPages}
    35  	if p.current > p.TotalPages() {
    36  		p.current = p.TotalPages()
    37  	}
    38  	return p
    39  }
    40  
    41  // IsFirst returns true if current page is the first page.
    42  func (p *Paginater) IsFirst() bool {
    43  	return p.current == 1
    44  }
    45  
    46  // HasPrevious returns true if there is a previous page relative to current page.
    47  func (p *Paginater) HasPrevious() bool {
    48  	return p.current > 1
    49  }
    50  
    51  func (p *Paginater) Previous() int {
    52  	if !p.HasPrevious() {
    53  		return p.current
    54  	}
    55  	return p.current - 1
    56  }
    57  
    58  // HasNext returns true if there is a next page relative to current page.
    59  func (p *Paginater) HasNext() bool {
    60  	return p.total > p.current*p.pagingNum
    61  }
    62  
    63  func (p *Paginater) Next() int {
    64  	if !p.HasNext() {
    65  		return p.current
    66  	}
    67  	return p.current + 1
    68  }
    69  
    70  // IsLast returns true if current page is the last page.
    71  func (p *Paginater) IsLast() bool {
    72  	if p.total == 0 {
    73  		return true
    74  	}
    75  	return p.total > (p.current-1)*p.pagingNum && !p.HasNext()
    76  }
    77  
    78  // Total returns number of total rows.
    79  func (p *Paginater) Total() int {
    80  	return p.total
    81  }
    82  
    83  // TotalPage returns number of total pages.
    84  func (p *Paginater) TotalPages() int {
    85  	if p.total == 0 {
    86  		return 1
    87  	}
    88  	if p.total%p.pagingNum == 0 {
    89  		return p.total / p.pagingNum
    90  	}
    91  	return p.total/p.pagingNum + 1
    92  }
    93  
    94  // Current returns current page number.
    95  func (p *Paginater) Current() int {
    96  	return p.current
    97  }
    98  
    99  // Page presents a page in the paginater.
   100  type Page struct {
   101  	num       int
   102  	isCurrent bool
   103  }
   104  
   105  func (p *Page) Num() int {
   106  	return p.num
   107  }
   108  
   109  func (p *Page) IsCurrent() bool {
   110  	return p.isCurrent
   111  }
   112  
   113  func getMiddleIdx(numPages int) int {
   114  	if numPages%2 == 0 {
   115  		return numPages / 2
   116  	}
   117  	return numPages/2 + 1
   118  }
   119  
   120  // Pages returns a list of nearby page numbers relative to current page.
   121  // If value is -1 means "..." that more pages are not showing.
   122  func (p *Paginater) Pages() []*Page {
   123  	if p.numPages == 0 {
   124  		return []*Page{}
   125  	} else if p.numPages == 1 && p.TotalPages() == 1 {
   126  		// Only show current page.
   127  		return []*Page{{1, true}}
   128  	}
   129  
   130  	// Total page number is less or equal.
   131  	if p.TotalPages() <= p.numPages {
   132  		pages := make([]*Page, p.TotalPages())
   133  		for i := range pages {
   134  			pages[i] = &Page{i + 1, i+1 == p.current}
   135  		}
   136  		return pages
   137  	}
   138  
   139  	numPages := p.numPages
   140  	maxIdx := numPages - 1
   141  	offsetIdx := 0
   142  	hasMoreNext := false
   143  
   144  	// Check more previous and next pages.
   145  	previousNum := getMiddleIdx(p.numPages) - 1
   146  	if previousNum > p.current-1 {
   147  		previousNum -= previousNum - (p.current - 1)
   148  	}
   149  	nextNum := p.numPages - previousNum - 1
   150  	if p.current+nextNum > p.TotalPages() {
   151  		delta := nextNum - (p.TotalPages() - p.current)
   152  		nextNum -= delta
   153  		previousNum += delta
   154  	}
   155  
   156  	offsetVal := p.current - previousNum
   157  	if offsetVal > 1 {
   158  		numPages++
   159  		maxIdx++
   160  		offsetIdx = 1
   161  	}
   162  
   163  	if p.current+nextNum < p.TotalPages() {
   164  		numPages++
   165  		hasMoreNext = true
   166  	}
   167  
   168  	pages := make([]*Page, numPages)
   169  
   170  	// There are more previous pages.
   171  	if offsetIdx == 1 {
   172  		pages[0] = &Page{-1, false}
   173  	}
   174  	// There are more next pages.
   175  	if hasMoreNext {
   176  		pages[len(pages)-1] = &Page{-1, false}
   177  	}
   178  
   179  	// Check previous pages.
   180  	for i := 0; i < previousNum; i++ {
   181  		pages[offsetIdx+i] = &Page{i + offsetVal, false}
   182  	}
   183  
   184  	pages[offsetIdx+previousNum] = &Page{p.current, true}
   185  
   186  	// Check next pages.
   187  	for i := 1; i <= nextNum; i++ {
   188  		pages[offsetIdx+previousNum+i] = &Page{p.current + i, false}
   189  	}
   190  
   191  	return pages
   192  }