Created
February 23, 2013 09:46
-
-
Save vmencik/5019145 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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