reitit/modules/reitit-frontend/src/reitit/frontend/history.cljs

124 lines
4 KiB
Text
Raw Normal View History

2018-06-08 13:00:49 +00:00
(ns reitit.frontend.history
""
(:require [reitit.core :as reitit]
[clojure.string :as string]
[goog.events :as e]
[goog.dom :as dom]
2018-07-11 06:52:35 +00:00
[reitit.core :as r]
2018-06-08 13:00:49 +00:00
[reitit.frontend :as rf])
(:import goog.history.Html5History
goog.Uri))
;; Token is for Closure HtmlHistory
;; Path is for reitit
(defn- token->path [history token]
(if (.-useFragment_ history)
2018-06-12 12:44:37 +00:00
;; If no fragment at all, default to "/"
;; If fragment is present, the token already is prefixed with "/"
(if (= "" token)
(.getPathPrefix history)
token)
2018-06-08 13:00:49 +00:00
(str (.getPathPrefix history) token)))
(defn- path->token [history path]
(subs path (if (.-useFragment_ history)
1
(count (.getPathPrefix history)))))
(defn- token->href [history token]
2018-06-12 11:09:55 +00:00
(if token
(str (if (.-useFragment_ history)
(str "#"))
(.getPathPrefix history)
token)))
2018-06-08 13:00:49 +00:00
2018-07-11 06:22:13 +00:00
(def ^:private current-domain (if (exists? js/location)
(.getDomain (.parse Uri js/location))))
2018-06-08 13:00:49 +00:00
(defn ignore-anchor-click
"Ignore click events from a elements, if the href points to URL that is part
of the routing tree."
[router history e]
;; Returns the next matching anchestor of event target
(when-let [el (.closest (.-target e) "a")]
(let [uri (.parse Uri (.-href el))]
(when (and (or (and (not (.hasScheme uri)) (not (.hasDomain uri)))
(= current-domain (.getDomain uri)))
(not (.-altKey e))
(not (.-ctrlKey e))
(not (.-metaKey e))
(not (.-shiftKey e))
(not (contains? #{"_blank" "self"} (.getAttribute el "target")))
;; Left button
(= 0 (.-button e))
(reitit/match-by-path router (.getPath uri)))
(.preventDefault e)
(.replaceToken history (path->token history (.getPath uri)))))))
(defn start!
"Parameters:
- router The reitit routing tree.
- on-navigate Function to be called when route changes.
Options:
- :use-fragment (default true) If true, onhashchange and location hash are used to store the token.
- :path-prefix (default \"/\") If :use-fragment is false, this is prepended to all tokens, and is
removed from start of the token before matching the route."
[router
on-navigate
{:keys [path-prefix use-fragment]
:or {path-prefix "/"
use-fragment true}}]
(let [history
(doto (Html5History.)
(.setEnabled true)
(.setPathPrefix path-prefix)
(.setUseFragment use-fragment))
event-key
(e/listen history goog.history.EventType.NAVIGATE
(fn [e]
(on-navigate (rf/match-by-path router (token->path history (.getToken history))))))
click-listen-key
(if-not use-fragment
(e/listen js/document e/EventType.CLICK
(partial ignore-anchor-click router history)))]
;; Trigger navigate event for current route
(on-navigate (rf/match-by-path router (token->path history (.getToken history))))
{:router router
:history history
:close-fn (fn []
(e/unlistenByKey event-key)
(e/unlistenByKey click-listen-key)
(.setEnabled history false))}))
(defn stop! [{:keys [close-fn]}]
(if close-fn
(close-fn)))
2018-06-12 10:58:43 +00:00
(defn- match->token [history match k params query]
2018-07-11 06:52:35 +00:00
(some->> (r/match->path match query)
(path->token history)))
2018-06-08 13:00:49 +00:00
(defn href
([state k]
(href state k nil))
([state k params]
(href state k params nil))
([{:keys [router history]} k params query]
(let [match (rf/match-by-name! router k params)
2018-06-12 10:58:43 +00:00
token (match->token history match k params query)]
2018-06-08 13:00:49 +00:00
(token->href history token))))
2018-06-12 11:09:55 +00:00
(defn replace-token
([state k params]
(replace-token state k params nil))
([{:keys [router history]} k params query]
(let [match (rf/match-by-name! router k params)
2018-06-12 11:09:55 +00:00
token (match->token history match k params query)]
(.replaceToken history token))))