Common isomorphic functionality in one kit.

router.go 2.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  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. return &Router{
  18. routes: []*Route{},
  19. }
  20. }
  21. func (r *Router) Handle(path string, handler Handler) *Route {
  22. return r.HandleFunc(path, handler.(HandlerFunc))
  23. }
  24. func (r *Router) HandleFunc(path string, handler HandlerFunc) *Route {
  25. route := NewRoute(path, handler)
  26. r.routes = append(r.routes, route)
  27. return route
  28. }
  29. func (r *Router) Listen() {
  30. r.RegisterLinks("body a")
  31. }
  32. func (r *Router) RegisterLinks(querySelector string) {
  33. document := dom.GetWindow().Document().(dom.HTMLDocument)
  34. links := document.QuerySelectorAll(querySelector)
  35. for _, link := range links {
  36. href := link.GetAttribute("href")
  37. switch {
  38. case strings.HasPrefix(href, "/") && !strings.HasPrefix(href, "//"):
  39. if r.listener != nil {
  40. link.RemoveEventListener("click", false, r.listener)
  41. }
  42. r.listener = link.AddEventListener("click", false, r.linkHandler)
  43. }
  44. }
  45. }
  46. func (r *Router) linkHandler(event dom.Event) {
  47. uri := event.CurrentTarget().GetAttribute("href")
  48. path := strings.Split(uri, "?")[0]
  49. // leastParams := -1
  50. var matchedRoute *Route
  51. var parts []string
  52. var lowestMatchCountSet bool = false
  53. var lowestMatchCount int = -1
  54. for _, route := range r.routes {
  55. matches := route.regex.FindStringSubmatch(path)
  56. matchesExist := len(matches) > 0 && matches != nil
  57. isLowestMatchCount := (lowestMatchCountSet == false) || (len(matches) < lowestMatchCount)
  58. if matchesExist && isLowestMatchCount {
  59. matchedRoute = route
  60. parts = matches[1:]
  61. lowestMatchCount = len(matches)
  62. lowestMatchCountSet = true
  63. }
  64. }
  65. if matchedRoute != nil {
  66. event.PreventDefault()
  67. js.Global.Get("history").Call("pushState", nil, "", uri)
  68. routeVars := make(map[string]string)
  69. for i, part := range parts {
  70. routeVars[matchedRoute.varNames[i]+`}`] = part
  71. }
  72. var ctx context.Context
  73. ctx, cancel := context.WithCancel(context.Background())
  74. defer cancel()
  75. k := RouteVarsKey("Vars")
  76. ctx = context.WithValue(ctx, k, routeVars)
  77. go matchedRoute.handler.ServeRoute(ctx)
  78. }
  79. }