Common isomorphic functionality in one kit.

router.go 2.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // The Isomorphic Go Project
  2. // Copyright (c) Wirecog, LLC. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license, which can be found in the LICENSE file.
  5. package isokit
  6. import (
  7. "context"
  8. "strings"
  9. "github.com/gopherjs/gopherjs/js"
  10. "honnef.co/go/js/dom"
  11. )
  12. type Router struct {
  13. routes []*Route
  14. listener func(*js.Object)
  15. }
  16. func NewRouter() *Router {
  17. initializeHistoryInteractions()
  18. return &Router{
  19. routes: []*Route{},
  20. }
  21. }
  22. func (r *Router) Handle(path string, handler Handler) *Route {
  23. return r.HandleFunc(path, handler.(HandlerFunc))
  24. }
  25. func (r *Router) HandleFunc(path string, handler HandlerFunc) *Route {
  26. route := NewRoute(path, handler)
  27. r.routes = append(r.routes, route)
  28. return route
  29. }
  30. func (r *Router) Listen() {
  31. r.RegisterLinks("body a")
  32. }
  33. func (r *Router) RegisterLinks(querySelector string) {
  34. document := dom.GetWindow().Document().(dom.HTMLDocument)
  35. links := document.QuerySelectorAll(querySelector)
  36. for _, link := range links {
  37. href := link.GetAttribute("href")
  38. switch {
  39. case strings.HasPrefix(href, "/") && !strings.HasPrefix(href, "//"):
  40. if r.listener != nil {
  41. link.RemoveEventListener("click", false, r.listener)
  42. }
  43. r.listener = link.AddEventListener("click", false, r.linkHandler)
  44. }
  45. }
  46. }
  47. func (r *Router) linkHandler(event dom.Event) {
  48. uri := event.CurrentTarget().GetAttribute("href")
  49. path := strings.Split(uri, "?")[0]
  50. // leastParams := -1
  51. var matchedRoute *Route
  52. var parts []string
  53. var lowestMatchCountSet bool = false
  54. var lowestMatchCount int = -1
  55. for _, route := range r.routes {
  56. matches := route.regex.FindStringSubmatch(path)
  57. matchesExist := len(matches) > 0 && matches != nil
  58. isLowestMatchCount := (lowestMatchCountSet == false) || (len(matches) < lowestMatchCount)
  59. if matchesExist && isLowestMatchCount {
  60. matchedRoute = route
  61. parts = matches[1:]
  62. lowestMatchCount = len(matches)
  63. lowestMatchCountSet = true
  64. }
  65. }
  66. if matchedRoute != nil {
  67. event.PreventDefault()
  68. js.Global.Get("history").Call("pushState", nil, "", uri)
  69. routeVars := make(map[string]string)
  70. for i, part := range parts {
  71. routeVars[matchedRoute.varNames[i]+`}`] = part
  72. }
  73. var ctx context.Context
  74. ctx, cancel := context.WithCancel(context.Background())
  75. defer cancel()
  76. k := RouteVarsKey("Vars")
  77. ctx = context.WithValue(ctx, k, routeVars)
  78. go matchedRoute.handler.ServeRoute(ctx)
  79. }
  80. }
  81. func initializeHistoryInteractions() {
  82. // Handler for back/forward button interactions
  83. dom.GetWindow().AddEventListener("popstate", false, func(event dom.Event) {
  84. var location = js.Global.Get("location")
  85. js.Global.Set("location", location)
  86. })
  87. }