From 8d65b787036f849f207604ab4a34f2e037d856d2 Mon Sep 17 00:00:00 2001 From: gered Date: Mon, 13 Jun 2016 12:16:05 -0400 Subject: [PATCH] add various clojurescript utility functions --- project.clj | 21 +++++++--- src/webtools/cljs/ajax.cljs | 62 ++++++++++++++++++++++++++++++ src/webtools/cljs/dom.cljs | 25 ++++++++++++ src/webtools/cljs/utils.cljs | 74 ++++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 src/webtools/cljs/ajax.cljs create mode 100644 src/webtools/cljs/dom.cljs create mode 100644 src/webtools/cljs/utils.cljs diff --git a/project.clj b/project.clj index 016720d..9a0b319 100644 --- a/project.clj +++ b/project.clj @@ -4,8 +4,19 @@ :license {:name "MIT License" :url "http://opensource.org/licenses/MIT"} - :dependencies [[org.clojure/clojure "1.8.0"] - [compojure "1.4.0" :scope "provided"] - [ring/ring-core "1.4.0" :scope "provided"] - [cheshire "5.5.0"] - [prismatic/schema "1.0.4"]]) + :dependencies [[cheshire "5.5.0"] + [prismatic/schema "1.0.4"] + [cljs-ajax "0.5.5"] + [secretary "1.2.3"]] + + :plugins [[lein-cljsbuild "1.1.3"]] + + :profiles {:provided + {:dependencies [[org.clojure/clojure "1.8.0"] + [org.clojure/clojurescript "1.8.51"] + [compojure "1.4.0"] + [ring/ring-core "1.4.0"]]}} + + :cljsbuild {:builds + {:main + {:source-paths ["src"]}}}) diff --git a/src/webtools/cljs/ajax.cljs b/src/webtools/cljs/ajax.cljs new file mode 100644 index 0000000..21855e5 --- /dev/null +++ b/src/webtools/cljs/ajax.cljs @@ -0,0 +1,62 @@ +(ns webtools.cljs.ajax + (:require + [clojure.walk :refer [stringify-keys keywordize-keys]] + [ajax.core :as ajax] + [webtools.cljs.utils :refer [->url get-anti-forgery-token]])) + +(defn add-headers! + [headers] + (let [headers (stringify-keys headers) + interceptor (ajax/to-interceptor + {:request #(assoc % :headers (merge (:headers %) headers))})] + (swap! ajax/default-interceptors (partial cons interceptor)) + headers)) + +(defn add-csrf-header! + [] + (let [token (get-anti-forgery-token)] + (if token (add-headers! {"X-CSRF-Token" token})) + token)) + +(defn GET + [url & {:keys [format params on-success on-error on-complete keywords? headers]}] + (let [url (->url url) + json? (or (= :json format) + (nil? format))] + (ajax/GET url + (merge + {:format (or format :json)} + (if json? {:keywords? (or keywords? true)}) + (if params {:params params}) + (if headers {:headers (stringify-keys headers)}) + (if on-success {:handler on-success}) + (if on-error {:error-handler on-error}) + (if on-complete {:finally on-complete}) + )))) + +(defn POST + [url & {:keys [format params body on-success on-error on-complete keywords? headers]}] + (let [url (->url url) + json? (or (= :json format) + (nil? format))] + (ajax/POST url + (merge + {:format (or format :json)} + (if json? {:keywords? (or keywords? true)}) + (if params {:params params}) + (if body {:body body}) + (if headers {:headers (stringify-keys headers)}) + (if on-success {:handler on-success}) + (if on-error {:error-handler on-error}) + (if on-complete {:finally on-complete}))))) + +(defn fetch! + [destination-atom url & {:keys [format params keywords? on-error headers transform]}] + (let [transform (or transform identity)] + (GET url + :format format + :params params + :keywords? keywords? + :on-error on-error + :headers headers + :on-success #(reset! destination-atom (transform %))))) diff --git a/src/webtools/cljs/dom.cljs b/src/webtools/cljs/dom.cljs new file mode 100644 index 0000000..f0a443f --- /dev/null +++ b/src/webtools/cljs/dom.cljs @@ -0,0 +1,25 @@ +(ns webtools.cljs.dom) + +(defn element-by-id + [id] + (.getElementById js/document id)) + +(defn select-all-elements + [selector] + (vec (.querySelectorAll js/document selector))) + +(defn select-element + [selector] + (.querySelector js/document selector)) + +(defn get-metatag-content + [metatag-name] + (if-let [tag (select-element (str "meta[name='" metatag-name "']"))] + (.-content tag))) + +(defn has-class? + [element class-name] + (if (.-classList element) + (.contains (.-classList element) class-name) + (doto (js/RegExp. (str "(^| )" class-name "( |$)") "gi") + (.test (.-className element))))) diff --git a/src/webtools/cljs/utils.cljs b/src/webtools/cljs/utils.cljs new file mode 100644 index 0000000..6b89b2e --- /dev/null +++ b/src/webtools/cljs/utils.cljs @@ -0,0 +1,74 @@ +(ns webtools.cljs.utils + (:import + goog.History) + (:require + [clojure.string :as string] + [clojure.walk :refer [keywordize-keys]] + [goog.events :as events] + [goog.history.EventType :as EventType] + [secretary.core :as secretary] + [webtools.cljs.dom :as dom])) + +(defn- get-hidden-field-value + [hidden-field-id] + (if-let [hidden-field (dom/element-by-id hidden-field-id)] + (.-value hidden-field))) + +(defn get-anti-forgery-token + [] + ; bunch of common names for this csrf token that i've seen used + ; ring's own anti-forgery middleware has a helper which outputs + ; an with the id "__anti-forgery-token" + (->> [(dom/get-metatag-content "anti-forgery-token") + (dom/get-metatag-content "__anti-forgery-token") + (dom/get-metatag-content "csrf-token") + (get-hidden-field-value "anti-forgery-token") + (get-hidden-field-value "__anti-forgery-token") + (get-hidden-field-value "csrf-token")] + (remove nil?) + (first))) + +(defn dev? + [] + (if (undefined? js/__isDev) + false + (boolean js/__isDev))) + +(defn context-url + [] + (if (undefined? js/__context) + "" + (str js/__context))) + +(defn supports-websockets? + [] + (not (-> (dom/select-element "html") + (dom/has-class? "no-websockets")))) + +(defn old-ie? + [] + (-> (dom/select-element "html") + (dom/has-class? "old-ie"))) + +(defn ->url + [url] + (-> (str (context-url) url) + (string/replace #"(/+)" "/"))) + +(defn hook-browser-navigation! + [] + (doto (History.) + (events/listen + EventType/NAVIGATE + (fn [event] + (secretary/dispatch! (.-token event)))) + (.setEnabled true))) + +(defn redirect! + [secretary-url] + (-> (.-location js/window) + (set! secretary-url))) + +(defn pprint-json + [x] + (.stringify js/JSON (clj->js x) nil " "))