Skip to content

Commit 6be68dd

Browse files
committed
Pushy returns reify instance which wraps goog.history.Html5History
Previously pushy was constructing instance of goog.history.Html5History before user initialized library. Update API to use IHistory protocol which implements wrapper methods for goog.history.Html5History and `start!` and `stop!` methods for pushy event listeners Test suite also passes!
1 parent 0fdb417 commit 6be68dd

File tree

4 files changed

+177
-144
lines changed

4 files changed

+177
-144
lines changed

README.md

+45-31
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,6 @@
33

44
A Clojurescript library for quick and easy HTML5 pushState.
55

6-
What it does:
7-
8-
* Initializes `goog.history.HTML5History`
9-
* Adds an event listener to all `click` events, which dispatches on all matched routes. Bypasses Alt, Shift, Meta, Ctrl keys as well as middle clicks.
10-
116
## Install
127

138
[![Clojars Project](http://clojars.org/kibu/pushy/latest-version.svg)](http://clojars.org/kibu/pushy)
@@ -16,66 +11,85 @@ What it does:
1611

1712
### Setup
1813

19-
You can initialize pushState by calling the `push-state!` function.
14+
You can construct a new instance by calling the `pushy` function.
2015

21-
This takes in two arguments:
16+
`pushy` takes in two arguments:
2217

2318
* `dispatch` fn: gets called when there is a match
2419
* `match` fn: checks if the path matches any routes defined.
2520

2621
Optionally, you can pass in an `identity` fn which parses and returns the route based on the result of the `match` fn
2722

23+
### Event listeners
24+
25+
You can start the event listeners with the `start!` method.
26+
27+
This adds an event listener to all `click` events, which dispatches on all matched routes.
28+
Bypasses on Alt, Shift, Meta, Ctrl keys and middle clicks.
29+
30+
The `stop!` method will tear down all event listeners of the pushy instance.
2831

29-
pushy should work with any routing library.
32+
### Routing libraries
3033

34+
pushy should work with any routing library:
3135

3236
[Secretary](https://github.com/gf3/secretary)
3337

3438
```clojure
35-
(ns foo.core
36-
(:require [secretary.core :as secretary :include-macros true :refer [defroute]]
37-
[pushy.core :as pushy :refer [push-state!]))
39+
(ns foo.core
40+
(:require [secretary.core :as secretary :include-macros true :refer-macros [defroute]]
41+
[pushy.core :as pushy]))
3842

39-
(secretary/set-config! :prefix "/")
43+
(secretary/set-config! :prefix "/")
4044

41-
(defroute index "/" []
42-
(.log js/console "Hi"))
45+
(defroute index "/" []
46+
(.log js/console "Hi"))
4347

44-
(push-state! secretary/dispatch!
45-
(fn [x] (when (secretary/locate-route x) x)))
48+
(def history (pushy/pushy secretary/dispatch!
49+
(fn [x] (when (secretary/locate-route x) x))))
50+
51+
;; Start event listeners
52+
(pushy/init! history)
4653
```
4754

4855
[Bidi](https://github.com/juxt/bidi)
4956

5057
```clojure
51-
(ns foo.core
52-
(:require [bidi.bidi :as bidi]
53-
[pushy.core :as pushy :refer [push-state!]))
58+
(ns foo.core
59+
(:require [bidi.bidi :as bidi]
60+
[pushy.core :as pushy))
61+
62+
(def state (atom {}))
5463

55-
(def state (atom {}))
64+
(def app-routes
65+
["/" {"foo" :foo}])
5666

57-
(def app-routes
58-
["/" {"foo" :foo}])
67+
(defn set-page! [match]
68+
(swap! state assoc :page match))
5969

60-
(defn set-page! [match]
61-
(swap state assoc :page match))
70+
(def history
71+
(pushy/pushy set-page! (partial bidi/match-route app-routes)))
6272

63-
(push-state! set-page! (partial bidi/match-route app-routes))
73+
(pushy/init! history)
6474
```
6575

66-
### set-token!
76+
### goog.history.HTML5History methods
6777

68-
It is also possible to set the history state by calling the `set-token!` function. This will also call the `dispatch` fn on a successfully matched path.
78+
You can set the history state manually by calling the `set-token!` method. This will call the `dispatch` fn on a successfully matched path.
79+
80+
Example:
6981

7082
```clojure
71-
(set-token! "/foo")
83+
(set-token! history "/foo")
84+
85+
(get-token history)
86+
;; => /foo
7287
```
7388

74-
Likewise, you can call `replace-token!` which will also call the `dispatch` fn, but replaces the current history state without affecting the rest of the history stack.
89+
Likewise, you can call `replace-token!` which will also call the `dispatch` fn and replace the current history state without affecting the rest of the history stack.
7590

7691
## License
7792

7893
Copyright © 2014
7994

80-
Distributed under the Eclipse Public License either version 1.0 or (at
81-
your option) any later version.
95+
Distributed under the Eclipse Public License either version 1.0

project.clj

+3-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
:url "http://www.eclipse.org/legal/epl-v10.html"}
66

77
:dependencies [[org.clojure/clojure "1.6.0"]
8-
[org.clojure/clojurescript "0.0-2356"]]
8+
[org.clojure/clojurescript "0.0-3211"]]
99

1010
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]
1111
"test" ["do" "clean," "with-profile" "dev" "cljsbuild" "test"]}
@@ -14,18 +14,12 @@
1414
:shell ["lein" "deploy"]}
1515

1616
:profiles {:dev {:dependencies [[secretary "1.2.1"]]
17-
18-
:plugins [[lein-cljsbuild "1.0.3"]
19-
[com.cemerick/clojurescript.test "0.3.1"]]
17+
:plugins [[lein-cljsbuild "1.0.5"]
18+
[com.cemerick/clojurescript.test "0.3.3"]]
2019

2120
:cljsbuild
2221
{:test-commands
2322
{"unit" ["phantomjs" :runner
24-
"window.literal_js_was_evaluated=true"
25-
"test/vendor/es5-shim.js"
26-
"test/vendor/es5-sham.js"
27-
"test/vendor/history-shim.js"
28-
"test/vendor/console-polyfill.js"
2923
"target/unit-test.js"]}
3024
:builds
3125
{:test {:source-paths ["src" "test"]

src/pushy/core.cljs

+84-81
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
(recur-href (.-parentNode target)))))
1919

2020
(defn- update-history! [h]
21-
(.setUseFragment h false)
22-
(.setPathPrefix h "")
23-
(.setEnabled h true)
24-
h)
21+
(doto h
22+
(.setUseFragment false)
23+
(.setPathPrefix "")
24+
(.setEnabled true)))
2525

2626
(defn- set-retrieve-token! [t]
2727
(set! (.. t -retrieveToken)
@@ -35,86 +35,89 @@
3535
(str path-prefix token)))
3636
t)
3737

38-
(def transformer
39-
(-> (TokenTransformer.) set-retrieve-token! set-create-url!))
40-
41-
(def history
42-
(-> (Html5History. js/window transformer) update-history!))
43-
44-
(defn set-token!
45-
"Sets the history state"
46-
([token]
47-
(. history (setToken token)))
48-
([token title]
49-
(. history (setToken token title))))
50-
51-
(defn replace-token!
52-
"Replaces the current history state without affecting the rest of the history stack"
53-
([token]
54-
(. history (replaceToken token)))
55-
([token title]
56-
(. history (replaceToken token title))))
57-
58-
(defn get-token
59-
"Returns the current token"
60-
[]
61-
(.getToken history))
62-
63-
(defn supported?
64-
"Returns whether Html5History is supported"
65-
[window]
66-
(.isSupported Html5History window))
67-
68-
(defn push-state!
69-
"Initializes push state using goog.history.Html5History
70-
71-
Adds an event listener to all click events and dispatches `dispatch-fn`
72-
when the target element contains a href attribute that matches
73-
any of the routes returned by `match-fn`
74-
75-
Takes in three functions:
38+
(defn new-history
39+
([]
40+
(new-history (-> (TokenTransformer.) set-retrieve-token! set-create-url!)))
41+
([transformer]
42+
(-> (Html5History. js/window transformer) update-history!)))
43+
44+
(defprotocol IHistory
45+
(set-token! [this token] [this token title])
46+
(replace-token! [this token] [this token title])
47+
(get-token [this])
48+
(start! [this])
49+
(stop! [this]))
50+
51+
(defn pushy
52+
"Takes in three functions:
7653
* dispatch-fn: the function that dispatches when a match is found
7754
* match-fn: the function used to check if a particular route exists
7855
* identity-fn: (optional) extract the route from value returned by match-fn"
7956
([dispatch-fn match-fn]
80-
(push-state! dispatch-fn match-fn identity))
81-
57+
(pushy dispatch-fn match-fn identity))
8258
([dispatch-fn match-fn identity-fn]
83-
;; We want to call `dispatch-fn` on any change to the location
84-
(events/listen history EventType.NAVIGATE
85-
(fn [e]
86-
(if-let [match (-> (.-token e) match-fn identity-fn)]
87-
(dispatch-fn match))))
88-
89-
;; Dispatch on initialization
90-
(when-let [match (-> (get-token) match-fn identity-fn)]
91-
(dispatch-fn match))
59+
(let [history (new-history)
60+
event-keys (atom nil)]
61+
(reify
62+
IHistory
63+
(set-token! [_ token]
64+
(. history (setToken token)))
65+
(set-token! [_ token title]
66+
(. history (setToken token title)))
67+
68+
(replace-token! [_ token]
69+
(. history (setToken token)))
70+
(replace-token! [_ token title]
71+
(. history (setToken token title)))
72+
73+
(get-token [_]
74+
(.getToken history))
75+
76+
(start! [this]
77+
(stop! this)
78+
;; We want to call `dispatch-fn` on any change to the location
79+
(swap! event-keys conj
80+
(events/listen history EventType.NAVIGATE
81+
(fn [e]
82+
(if-let [match (-> (.-token e) match-fn identity-fn)]
83+
(dispatch-fn match)))))
84+
85+
;; Dispatch on initialization
86+
(when-let [match (-> (get-token this) match-fn identity-fn)]
87+
(dispatch-fn match))
88+
89+
(swap! event-keys conj
90+
(on-click
91+
(fn [e]
92+
(when-let [el (recur-href (-> e .-target))]
93+
(let [href (.-href el)
94+
path (->> href (.parse Uri) .getPath)]
95+
;; Proceed if `identity-fn` returns a value and
96+
;; the user did not trigger the event via one of the
97+
;; keys we should bypass
98+
(when (and (identity-fn (match-fn path))
99+
;; Bypass dispatch if any of these keys
100+
(not (.-altKey e))
101+
(not (.-ctrlKey e))
102+
(not (.-metaKey e))
103+
(not (.-shiftKey e))
104+
;; Bypass if target = _blank
105+
(not (= "_blank" (.getAttribute el "target")))
106+
;; Bypass dispatch if middle click
107+
(not= 1 (.-button e)))
108+
;; Dispatch!
109+
(if-let [title (-> el .-title)]
110+
(set-token! this path title)
111+
(set-token! this path))
112+
(.preventDefault e)))))))
113+
nil)
114+
115+
(stop! [this]
116+
(doseq [key @event-keys]
117+
(events/unlistenByKey key))
118+
(reset! event-keys nil))))))
92119

93-
;; Setup event listener on all 'click' events
94-
(on-click
95-
(fn [e]
96-
(when-let [el (recur-href (-> e .-target))]
97-
(let [href (.-href el)
98-
path (->> href (.parse Uri) .getPath)]
99-
;; Proceed if `identity-fn` returns a value and
100-
;; the user did not trigger the event via one of the
101-
;; keys we should bypass
102-
(when (and (identity-fn (match-fn path))
103-
;; Bypass dispatch if any of these keys
104-
(not (.-altKey e))
105-
(not (.-ctrlKey e))
106-
(not (.-metaKey e))
107-
(not (.-shiftKey e))
108-
;; Bypass if target = _blank
109-
(not (= "_blank" (.getAttribute el "target")))
110-
;; Bypass dispatch if middle click
111-
(not= 1 (.-button e)))
112-
;; Dispatch!
113-
(set-token! path (-> el .-title))
114-
(.preventDefault e))))))))
115-
116-
(defn unlisten!
117-
"Closes the pushy event listeners"
118-
[push-state]
119-
(events/unlistenByKey push-state)
120-
(events/unlisten history EventType.NAVIGATE))
120+
(defn supported?
121+
"Returns whether Html5History is supported"
122+
([] (supported? js/window))
123+
([window] (.isSupported Html5History window)))

0 commit comments

Comments
 (0)