aboutsummaryrefslogtreecommitdiff
path: root/client/src/router/Router.scala
blob: 60e5ef2cc493bd42cc8079bb775082fd94ad2700 (plain)
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
93
94
95
96
97
98
99
100
101
102
103
104
105
package yoga.router

import scala.scalajs.js
import scala.scalajs.js.URIUtils
import scala.xml.Node
import java.net.URI

import mhtml.Var
import org.scalajs.dom.raw.{ Element, Node => RawNode }
import org.scalajs.dom.{ document, window, Event, MouseEvent }

import yoga.model.Route
import yoga.view.index.Index
import yoga.view.licence.LicencePage
import yoga.view.notFound.NotFound

object Router {

  private val route: Var[Route] = Var(parseRoute(Path.current()))

  def render(): Node =
    <div
      mhtml-onmount={ (n: RawNode) =>
        document.addEventListener("click", Handler.click)
        window.addEventListener("popstate", Handler.popState)
      }
      mhtml-onumnount={ (n: RawNode) =>
        document.removeEventListener("click", Handler.click)
        window.removeEventListener("popstate", Handler.popState)
      }
    >
      {route.map {
        case Route.Index => Index()
        case Route.Licence(uri) => LicencePage(uri)
        case Route.NotFound => NotFound()
      }}
    </div>

  object Path {

    val index: String = "/"

    def licence(uri: URI): String = s"/licence/${uri}"

    def current(): String =
      URIUtils.decodeURI(s"${window.location.pathname}${window.location.search}")

  }

  private def parseRoute(path: String): Route =
    pathAndParams(path) match {
      case (List(), _) => Route.Index
      case (List("licence", uri), _) => Route.Licence(URI.create(uri))
      case _ => Route.NotFound
    }

  private def pathAndParams(path: String): (List[String], List[String]) = {
    println(path)
    def splitPath(path: String) = path.split("/").drop(1).toList
    path.split('?') match {
      case Array(path) => (splitPath(path), Nil)
      case Array(path, params) => (splitPath(path), params.split("&").toList)
    }
  }

  private object Handler {

    lazy val click: js.Function1[MouseEvent, Unit] =
      routeTransitionOnAnchorClick { path =>
        route := parseRoute(path)
        window.history.pushState(null, "", path)
      }

    lazy val popState: js.Function1[Event, Unit] =
      _ => route := parseRoute(Path.current())

  }

  private def routeTransitionOnAnchorClick(goTo: String => Unit)(event: MouseEvent): Unit =
    if (event.button == 0)
      Dom.findSelfOrParent("a", event.target.asInstanceOf[RawNode]) match {
        case Some(node) =>
          val elem = node.asInstanceOf[Element]
          if (elem.getAttribute("data-nav") != "ignore") {
            event.preventDefault()
            val href = elem.getAttribute("href")
            if (href != Path.current()) goTo(href)
          }
        case _ =>
      }

  private object Dom {

    @annotation.tailrec
    def findSelfOrParent(nodeName: String, current: RawNode): Option[RawNode] =
      if (current.nodeName.toString.toLowerCase == nodeName.toLowerCase)
        Some(current)
      else if (current.parentNode == null)
        None
      else
        findSelfOrParent(nodeName, current.parentNode)

  }

}