1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
package reading
import org.scalajs.dom.window
import org.scalajs.dom.raw.PopStateEvent
import scala.scalajs.js.URIUtils
import rx.Var
import reading.models.{ Filter, FilterKind, Book, Books => BooksModel }
sealed trait Route
object Route {
case class Books(filters: Seq[Filter] = Nil, detail: Option[Book] = None) extends Route
val current: Var[Route] = Var(parse(window.location.hash))
window.onpopstate = (e: PopStateEvent) => {
current() = parse(window.location.hash)
}
def parse(hash: String): Route =
pathAndParams(hash) match {
case ("books" :: Nil, params) => {
val filters = params.flatMap {
case Param(key, value) =>
for {
kind <- FilterKind.withNameOption(key)
filter <- Filter(kind, value)
} yield filter
case _ =>
None
}
val detail = params.collectFirst {
case Param("detail", title) => BooksModel().find(_.title == title)
}.flatten
Books(filters, detail)
}
case _ =>
Books()
}
def pathAndParams(hash: String): (List[String], List[String]) = {
def splitPath(path: String) = path.split("/").drop(1).toList
URIUtils.decodeURI(hash.drop(1)).split('?') match {
case Array(path) => (splitPath(path), Nil)
case Array(path, params) => (splitPath(path), params.split("&").toList)
}
}
def url(route: Route): String = {
val hash = route match {
case Books(filters, detail) => {
val filterParams = filters.map(filter => (filter.kind.toString, filter.nonFormattedName))
val detailParams = detail.map(book => ("detail", book.title))
s"/books${Param.format(filterParams ++ detailParams)}"
}
case _ => "/books"
}
window.location.origin + window.location.pathname + "#" + URIUtils.encodeURI(hash)
}
def goTo(route: Route): Unit = {
push(route)
current() = route
}
def push(route: Route): Unit = {
window.history.pushState(null, "", url(route));
}
}
object Param {
def apply(key: String, value: String): (String, String) = (key, value)
def unapply(x: Any): Option[(String, String)] =
x match {
case str: String =>
str.split("=") match {
case Array(key, value) => Some((key, value))
case _ => None
}
case _ => None
}
def format(params: Seq[(String, String)]): String =
if (params.isEmpty)
""
else
params
.map { case (key, value) => s"$key=$value" }
.mkString("?", "&", "")
}
|