Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
BenjaminVanRyseghem committed Dec 31, 2014
1 parent 4cc07e8 commit b93144b
Show file tree
Hide file tree
Showing 20 changed files with 3,228 additions and 3 deletions.
5 changes: 5 additions & 0 deletions LICENSE-formative
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## License (extracted from [README](https://github.com/jkk/formative/blob/b881a68b5ffb176a21c5939eb14abdd61c915123/README.md#license))

Copyright © 2012-2013 Justin Kramer

Distributed under the Eclipse Public License, the same as Clojure.
476 changes: 473 additions & 3 deletions README.md

Large diffs are not rendered by default.

Binary file added doc/bootstrap-horizontal.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/bootstrap-stacked.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
33 changes: 33 additions & 0 deletions project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(defproject formidable "0.1.0"
:description "Web forms - rendering, parsing, and validating"
:url "https://github.com/teamwall/formidable"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[jkkramer/verily "0.6.0"]
[clj-time "0.8.0"]
[crate "0.2.5"]
[prismatic/dommy "1.0.0"]
[ring-anti-forgery "0.3.0"]]
:test-paths ["target/test-classes"]
:cljx {:builds [{:source-paths ["src"]
:output-path "target/classes"
:rules :clj}
{:source-paths ["src"]
:output-path "target/classes"
:rules :cljs}
{:source-paths ["test"]
:output-path "target/test-classes"
:rules :clj}
{:source-paths ["test"]
:output-path "target/test-classes"
:rules :cljs}]}
:prep-tasks [["cljx" "once"]]
:profiles {:dev {:dependencies [[com.cemerick/clojurescript.test "0.3.3"]
[org.clojure/clojurescript "0.0-2498"]
[com.cemerick/piggieback "0.1.3"]
[com.keminglabs/cljx "0.5.0"]]
:plugins [[com.keminglabs/cljx "0.5.0"]]
:repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl
cljx.repl-middleware/wrap-cljx]}
:prep-tasks [["cljx" "once"]]}})
457 changes: 457 additions & 0 deletions src/formidable/core.cljx

Large diffs are not rendered by default.

80 changes: 80 additions & 0 deletions src/formidable/data.cljx

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions src/formidable/dom.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
(ns formidable.dom
(:require [formidable.util :as fu]
[formidable.parse :as fp]
[formidable.render :as fr]
[dommy.core :as d :refer-macros [sel sel1]]
[dommy.utils :as du]
[clojure.string :as string]
[crate.core :as crate])
(:require-macros [formidable.macros :refer [with-fallback]]))

(defn serialize
"Returns a form data string for the given form element, suitable for Ajax
GET/POST, or passing to formidable.parse/parse-params."
[form-el]
(->> (for [el (du/->Array (.-elements form-el))
:let [name (.-name el)]
:when (not (string/blank? name))]
(let [node-name (.-nodeName el)
type (.-type el)
value (.-value el)]
(cond
(and (= "INPUT" node-name)
(#{"checkbox" "radio"} type))
(when (.-checked el)
(fu/encode-uri-kv name value))

(and (= "SELECT" node-name)
(= "select-multiple" type))
(->> (for [opt (du/->Array (.-options el))
:when (.-selected opt)]
(fu/encode-uri-kv name (.-value opt)))
(string/join "&"))

(and (= "INPUT" node-name)
(= "file" type))
nil

:else
(fu/encode-uri-kv name value))))
(remove nil?)
(string/join "&")))

(defn get-form-el
"Given a form container element or a form element, returns the form element"
[container-or-form-el]
(if (= "FORM" (.-nodeName container-or-form-el))
container-or-form-el
(sel1 container-or-form-el "form")))

(defn clear-problems
"Clears form problems from the DOM"
[container-or-form-el]
(let [form-el (get-form-el container-or-form-el)]
(when-let [parent-el (.-parentNode form-el)]
(when-let [problems-el (sel1 parent-el ".form-problems")]
(d/remove! problems-el)))
(doseq [el (sel form-el ".problem.error")]
(d/remove-class! el "problem" "error"))))

(defn get-scroll-top
"Returns the top window scroll position"
[]
(if (exists? (.-pageYOffset js/window))
(.-pageYOffset js/window)
(.-scrollTop (or (-> js/document .-documentElement)
(-> js/document .-body .-parentNode)
(-> js/document .-body)))))

(defn get-offset-top
"Returns an element's top offset relative to the window"
[el]
(let [rect (.getBoundingClientRect el)]
(+ (.-top rect) (get-scroll-top))))

(defn scroll-to-el
"Scrolls the window to an element's offset top"
[el]
(.scrollTo js/window 0 (- (get-offset-top el) 10)))

(defn show-problems
"Shows form problems in the DOM"
[form-spec container-or-form-el problems]
(let [form-el (get-form-el container-or-form-el)]
(clear-problems form-el)
(let [problems-el (crate/html (fr/render-problems problems
(:fields form-spec)))]
(d/insert-before! problems-el form-el)
(scroll-to-el problems-el))
(doseq [problem problems
:let [fnames (map name (if (map? problem)
(:keys problem)
[problem]))]
fname fnames]
(let [field-container-id (fu/get-field-container-id
{:id (fu/get-field-id {:name fname})
:name fname})]
(when-let [el (sel1 (str "#" field-container-id))]
(d/add-class! el "problem error"))))))

(defn handle-submit
"Attaches an event handler to a form's \"submit\" browser event, validates
submitted data, then:
* If validation fails, shows the problems (or, if provided, calls a custom
failure function with the problems data as the argument)
* If validation succeeds, calls a success function with parsed params as
the argument"
[form-spec container-or-form-el success & [failure]]
(let [form-el (get-form-el container-or-form-el)
failure (or failure
#(show-problems form-spec form-el %))]
(d/listen! form-el :submit
(fn [event]
(.preventDefault event)
(with-fallback failure
(clear-problems form-el)
(success
(fp/parse-params form-spec (serialize form-el))))))))
14 changes: 14 additions & 0 deletions src/formidable/macros.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(ns formidable.macros)

;; ClojureScript macros

(defmacro with-fallback
"Attempts to run body; if an ExceptionInfo with a :problems key is caught,
calls fallback-fn with the problems as the argument."
[fallback-fn & body]
`(try
~@body
(catch js/Error e#
(if-let [problems# (:problems (ex-data e#))]
(~fallback-fn problems#)
(throw e#)))))
Loading

0 comments on commit b93144b

Please sign in to comment.