Skip to content

Instantly share code, notes, and snippets.

@apg
Created August 24, 2010 11:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save apg/547405 to your computer and use it in GitHub Desktop.
Save apg/547405 to your computer and use it in GitHub Desktop.
selector for hiccup
(defn hiccup-zip
"Returns a zipper for hiccup"
[root]
(zip/zipper
; branch?
(fn [x] (and (vector? x)
(not (empty? x))
(keyword? (first x))))
; children
(fn [x]
(if (map? (second x))
(vec (rest (rest x)))
(vec (rest x))))
; make-node
(fn [node children]
(concat node children))
root))
(defn- find-first-node
"Finds a node `n` in a hiccup tree `t`
`t` should be a hiccup style tree
`n` should be a selector in the form :elem.class or :elem#id
"
[t n]
(let [s (str n)
[te e] (s/split s #"\.|#" 2)
tg (keyword (apply str (rest te)))
cn (if (> (.indexOf s ".") -1) e)
id (if (> (.indexOf s "#") -1) e)
tag-of (fn [x]
(keyword
(.substring
(s/replace x #"[.#].*" "") 1)))]
(loop [z (hiccup-zip t)]
(when-not (zip/end? z)
(let [nd (zip/node z)]
(cond
(= (first nd) n) nd
(and (= tg (first nd))
(and (map? (second nd))
(or (= id ((second nd) :id))
(= class ((second nd) :class))))) nd
; maybe the case where there's no id, or class but we want only the
; element
(and (= (tag-of (first nd)) tg)
(nil? id)
(nil? cn)) nd
:else (recur (zip/next z))))))))
(def hello-world
[:html
{}
[:head {} [:title "hello world"]]
[:body
{}
[:h1.theclass {} "Hello World!"]
[:p#theid "hello world"]
[:p {:class "theclass"}]]])
;; user> (find-first-node hello-world :p)
;; [:p#theid "hello world"]
;; user>
;; [:title "hello world"]
;; user> (find-first-node hello-world :p#theid)
;; [:p#theid "hello world"]
;; user> (find-first-node hello-world :p.theclass)
;; [:p {:class "theclass"}]
;; user>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment