Skip to content

Instantly share code, notes, and snippets.

@vmencik
Created February 23, 2013 09:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vmencik/5019145 to your computer and use it in GitHub Desktop.
Save vmencik/5019145 to your computer and use it in GitHub Desktop.
package models
import play.api.mvc.Call
class Pagination(cnt: Int, pos: Int, val runover: Boolean, val pageSize: Int = 20,
val forwardCnt: Int = 4, val backwardCnt: Int = 4, val headCnt: Int = 3, val tailCnt: Int = 4,
val indexByPage: Boolean = false, val indexByPageIndex: Boolean = false, val independentVicinity: Boolean = false,
val consoleEnds: Boolean = false, val duplicateEnds: Boolean = false)(navi: Int => Call) {
outer =>
val offset = {
if (indexByPage)
if (pos > 0) (pos - 1) * pageSize else 0
else if (indexByPageIndex) pos * pageSize
else pos
}.max(0)
val totalCount = if (cnt >= 0) cnt else offset + 1
val maxOffset = if (runover) totalCount else totalCount - 1
// Offsets of the first and last vicinity pages
val (vicinityStart: Int, vicinityEnd: Int) = {
def calculateFirstPageOffset(offset: Int) = offset % pageSize
val placeFromStart = offset < totalCount - offset - pageSize
if (placeFromStart) {
val minStart = calculateFirstPageOffset(offset)
val start = (offset - backwardCnt * pageSize).max(minStart)
val end =
if (independentVicinity) (offset + backwardCnt * pageSize)
else (start + (forwardCnt + backwardCnt) * pageSize)
(start, correctEndPageOffset(end))
} else {
val maxEnd = calculateLastPageOffset(offset);
val end = (offset + forwardCnt * pageSize).min(maxEnd)
val start =
if (independentVicinity) (offset - forwardCnt * pageSize)
else (end - (backwardCnt + forwardCnt) * pageSize)
(start.max(0), end)
}
}
def vicinity: IndexedSeq[Page] = pageRange(vicinityStart, vicinityEnd)
def head: IndexedSeq[Page] = {
val ec = (headCnt - 1) * pageSize
val end = if (duplicateEnds) ec else math.min(ec, vicinityStart - pageSize)
pageRange(0, correctEndPageOffset(end))
}
def tail: IndexedSeq[PageLike] =
if (runover) IndexedSeq(Runover)
else {
val lastPageOffset = calculateLastPageOffset(0)
val sc = lastPageOffset - (tailCnt - 1) * pageSize
val start =
if (duplicateEnds) sc
else {
val minStart = lastPageOffset - ((lastPageOffset - vicinityEnd) / pageSize - 1) * pageSize
math.max(sc, minStart)
}
pageRange(start.max(0), lastPageOffset)
}
def console: IndexedSeq[PageLike] = {
def prefix: IndexedSeq[PageLike] = {
if (consoleEnds) {
val h = head
h ++
(for {
last <- h.lastOption if last.offset < vicinityStart - pageSize
} yield Ellipsis)
} else IndexedSeq.empty
}
def suffix: IndexedSeq[PageLike] = {
if (consoleEnds) {
val t = tail
IndexedSeq.empty ++
(for {
first <- t.headOption if first.offset > vicinityEnd + pageSize
} yield Ellipsis) ++ t
} else IndexedSeq.empty
}
prefix ++ vicinity ++ suffix
}
def first: Option[Page] = get(0)
def minus10: Option[Page] = deltaPage(-10)
def prev: Option[Page] = deltaPage(-1)
def current: Option[Page] = deltaPage(0)
def next: Option[Page] = deltaPage(1)
def plus10: Option[Page] = deltaPage(10)
def last: Option[Page] = get(calculateLastPageOffset(0))
private def correctEndPageOffset(offset: Int) = offset.min(maxOffset)
private def calculateLastPageOffset(offset: Int) = ((maxOffset - offset) / pageSize) * pageSize + offset
private val pageCache = collection.mutable.Map.empty[Int, Page]
private def get(offset: Int): Option[Page] = {
def createPage = {
val firstPageItem = offset + 1
val pageNumber = offset / pageSize + 1
val pagePos =
if (indexByPage) pageNumber
else if (indexByPageIndex) pageNumber - 1
else offset
Page(count = math.min(pageSize, totalCount - firstPageItem + 1),
offset = offset, number = pageNumber, pos = pagePos)
}
if (totalCount < 1 || offset < 0 || offset > maxOffset) None
else Some(pageCache.getOrElseUpdate(offset, createPage))
}
private def pageRange(startOffset: Int, endOffset: Int): IndexedSeq[Page] =
for {
i <- startOffset to endOffset by pageSize
p <- get(i)
} yield p
private def deltaPage(delta: Int) = get(offset + delta * pageSize)
sealed abstract class PageLike(val output: String, val offset: Int = 0)
case object Ellipsis extends PageLike("...")
case object Runover extends PageLike("")
case class Page(count: Int, override val offset: Int, number: Int, pos: Int) extends PageLike(number.toString, offset) {
def address = outer.navi(offset)
def shown = offset == outer.offset
def index = number - 1
def firstNumber = offset + 1
def firstIndex = offset
def lastNumber = offset + count
def lastIndex = lastNumber - 1
def prev = outer.get(offset - outer.pageSize)
def next = outer.get(offset + outer.pageSize)
def addr = outer.navi(offset)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment