Skip to content

Commit

Permalink
Merge pull request #94 from asmala/void_tags
Browse files Browse the repository at this point in the history
Change container tag check to void tag check
  • Loading branch information
weavejester committed Jan 25, 2014
2 parents 6afda5b + 43342b9 commit 44f270a
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 23 deletions.
34 changes: 21 additions & 13 deletions src/hiccup/compiler.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
(:use hiccup.util)
(:import [clojure.lang IPersistentVector ISeq Named]))

(def ^:dynamic *html-mode* :xml)
(def ^:dynamic *html-mode* :xhtml)

(defn- xml-mode? []
(= *html-mode* :xml))
(#{:xml :xhtml} *html-mode*))

(defn- html-mode? []
(#{:html :xhtml} *html-mode*))

(defn- end-tag []
(if (xml-mode?) " />" ">"))
Expand All @@ -33,14 +36,19 @@
:private true}
re-tag #"([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?")

(def ^{:doc "A list of elements that need an explicit ending tag when rendered."
(def ^{:doc "A list of elements that must be rendered without a closing tag."
:private true}
container-tags
#{"a" "article" "aside" "b" "body" "canvas" "dd" "div" "dl" "dt" "em" "fieldset"
"footer" "form" "h1" "h2" "h3" "h4" "h5" "h6" "head" "header" "hgroup" "html"
"i" "iframe" "label" "li" "nav" "object" "ol" "option" "pre" "section" "select"
"script" "span" "strong" "style" "table" "textarea" "title" "ul" "video" "p"})

void-tags
#{"area" "base" "br" "col" "command" "embed" "hr" "img" "input" "keygen" "link"
"meta" "param" "source" "track" "wbr"})

(defn- container-tag?
"Returns true if the tag has content or is not a void tag. In non-HTML modes,
all contentless tags are assumed to be void tags."
[tag content]
(or content
(and (html-mode?) (not (void-tags tag)))))

(defn- merge-attributes [{:keys [id class]} map-attrs]
(->> map-attrs
(merge (if id {:id id}))
Expand All @@ -67,7 +75,7 @@
"Render an element vector as a HTML element."
[element]
(let [[tag attrs content] (normalize-element element)]
(if (or content (container-tags tag))
(if (container-tag? tag content)
(str "<" tag (render-attr-map attrs) ">"
(render-html content)
"</" tag ">")
Expand Down Expand Up @@ -186,7 +194,7 @@
(defmethod compile-element ::literal-tag-and-attributes
[[tag attrs & content]]
(let [[tag attrs _] (normalize-element [tag attrs])]
(if (or content (container-tags tag))
(if (container-tag? tag content)
`(str ~(str "<" tag) ~(compile-attr-map attrs) ">"
~@(compile-seq content)
~(str "</" tag ">"))
Expand All @@ -202,15 +210,15 @@
attrs-sym (gensym "attrs")]
`(let [~attrs-sym ~attrs]
(if (map? ~attrs-sym)
~(if (or content (container-tags tag))
~(if (container-tag? tag content)
`(str ~(str "<" tag)
(#'render-attr-map (merge ~tag-attrs ~attrs-sym)) ">"
~@(compile-seq content)
~(str "</" tag ">"))
`(str ~(str "<" tag)
(#'render-attr-map (merge ~tag-attrs ~attrs-sym))
~(end-tag)))
~(if (or attrs (container-tags tag))
~(if (container-tag? tag attrs)
`(str ~(str "<" tag (render-attr-map tag-attrs) ">")
~@(compile-seq (cons attrs-sym content))
~(str "</" tag ">"))
Expand Down
26 changes: 16 additions & 10 deletions test/hiccup/test/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@
(is (= (html [:div]) "<div></div>"))
(is (= (html [:h1]) "<h1></h1>"))
(is (= (html [:script]) "<script></script>"))
(is (= (html [:text]) "<text />"))
(is (= (html [:text]) "<text></text>"))
(is (= (html [:a]) "<a></a>"))
(is (= (html [:iframe]) "<iframe></iframe>"))
(is (= (html [:title]) "<title></title>"))
(is (= (html [:section]) "<section></section>"))
(is (= (html [:select]) "<select></select>"))
(is (= (html [:object]) "<object></object>"))
(is (= (html [:video]) "<video></video>")))
(testing "void tags"
(is (= (html [:br]) "<br />"))
(is (= (html [:link]) "<link />"))
(is (= (html [:colgroup {:span 2}] "<colgroup span=\"2\" />")))
(is (= (html [:colgroup [:col]] "<colgroup><col /></colgroup>"))))
(testing "tags containing text"
(is (= (html [:text "Lorem Ipsum"]) "<text>Lorem Ipsum</text>")))
(testing "contents are concatenated"
Expand All @@ -51,13 +56,13 @@

(deftest tag-attributes
(testing "tag with blank attribute map"
(is (= (html [:xml {}]) "<xml />")))
(is (= (html [:xml {}]) "<xml></xml>")))
(testing "tag with populated attribute map"
(is (= (html [:xml {:a "1", :b "2"}]) "<xml a=\"1\" b=\"2\" />"))
(is (= (html [:xml {:a "1", :b "2"}]) "<xml a=\"1\" b=\"2\"></xml>"))
(is (= (html [:img {"id" "foo"}]) "<img id=\"foo\" />"))
(is (= (html [:img {'id "foo"}]) "<img id=\"foo\" />"))
(is (= (html [:xml {:a "1", 'b "2", "c" "3"}])
"<xml a=\"1\" b=\"2\" c=\"3\" />")))
"<xml a=\"1\" b=\"2\" c=\"3\"></xml>")))
(testing "attribute values are escaped"
(is (= (html [:div {:id "\""}]) "<div id=\"&quot;\"></div>")))
(testing "boolean attributes"
Expand All @@ -82,8 +87,8 @@
(is (= (html [:span ({:foo "bar"} :foo)]) "<span>bar</span>")))
(testing "attributes can contain vars"
(let [x "foo"]
(is (= (html [:xml {:x x}]) "<xml x=\"foo\" />"))
(is (= (html [:xml {x "x"}]) "<xml foo=\"x\" />"))
(is (= (html [:xml {:x x}]) "<xml x=\"foo\"></xml>"))
(is (= (html [:xml {x "x"}]) "<xml foo=\"x\"></xml>"))
(is (= (html [:xml {:x x} "bar"]) "<xml x=\"foo\">bar</xml>"))))
(testing "attributes are evaluated"
(is (= (html [:img {:src (str "/foo" "/bar")}])
Expand All @@ -109,10 +114,11 @@

(deftest render-modes
(testing "closed tag"
(is (= (html [:br]) "<br />"))
(is (= (html {:mode :xml} [:br]) "<br />"))
(is (= (html {:mode :sgml} [:br]) "<br>"))
(is (= (html {:mode :html} [:br]) "<br>")))
(is (= (html [:p] [:br]) "<p></p><br />"))
(is (= (html {:mode :xhtml} [:p] [:br]) "<p></p><br />"))
(is (= (html {:mode :html} [:p] [:br]) "<p></p><br>"))
(is (= (html {:mode :xml} [:p] [:br]) "<p /><br />"))
(is (= (html {:mode :sgml} [:p] [:br]) "<p><br>")))
(testing "boolean attributes"
(is (= (html {:mode :xml} [:input {:type "checkbox" :checked true}])
"<input checked=\"checked\" type=\"checkbox\" />"))
Expand Down

0 comments on commit 44f270a

Please sign in to comment.