Skip to content

Latest commit

 

History

History
362 lines (256 loc) · 11.2 KB

README.textile

File metadata and controls

362 lines (256 loc) · 11.2 KB

Enfocus

Enfocus is a DOM manipulation and templating library for ClojureScript. Inspired by Christophe Grand’s Enlive, Enfocus’ primary purpose is providing a base for building rich interfaces in ClojureScript.

If you are unfamiliar with enlive I also recommend taking a look at these links.

David Nolen wrote a nice tutorial
Another tutorial by Brian Marick.

Documentation & Examples

Documentation & Demo Site
Example Website

Where do I get support?

On the group

Quick Start

Setup

From any leiningen project file:

[enfocus "2.0.0-SNAPSHOT"] – experimental use at your own risk

[enfocus "1.0.1"] – stable

Then make sure you have your lib folder on your classpath.

For the best development experience, use lein-cljsbuild.

The Basics

Every great journey starts with “Hello world!”

(ns my.namespace
  (:require [enfocus.core :as ef]
            [enfocus.events :as events]
	    [enfocus.effects :as effects])
  (:require-macros [enfocus.macros :as em]))

(defn start [] 
  (ef/at js/document
    ["body"] (ef/content "Hello world!")))  

(set! (.-onload js/window) start)         

The at form

At the core to understanding Enfocus is the at form used in the
“Hello world!” example above. It comes in two basic flavors listed below:

A single transform

(at a-node (transform arg1 ...))

;or with implied js/document

(at selector (transform arg1 ...))     

and a series of transforms

(at a-node
    [selector1] (transform1 arg1 ...)
    [selector2] (transform2 arg1 ...))

;or with implied js/document

(at [selector1] (transform1 arg1 ...)
    [selector2] (transform2 arg1 ...))

In the first case at is passed a node, node set or selector and a transform. This form of at
calls the transform on each element in the node set.

A transform is nothing more than a function that takes a set of arguments and returns a function
that takes a set of nodes. In case of the “Hello World!” example above, we see the use of
(em/content “Hello world!”) this call returns a function that takes node or node set
and replaces the content with “Hello world!”

In the second case, we see at is optionally passed a node or node set and a set of
selector/transform pairs. The selectors are scoped by the node, node set or js/document,
if a node is not passed in, and the results of each selector is passed on to its partner transformation.

A selector is a string representing a CSS3 compliant selector

Handling Events

Enfocus has event handling. Below is a simple example to add an onclick event handler to a button.

(em/defaction change [msg] 
  ["#button1"] (ef/content msg))

(em/defaction setup []
  ["#button1"] (events/listen :click #(change "I have been clicked")))
	
(set! (.-onload js/window) setup)         

The defaction construct is use here instead defn. defaction
creates a function that calls the at form like discussed above
and passes in js/document as the node to be transformed.

Effects

Enfocus has the concept of effects. Effects are nothing more than transformations
over a period of time. Below is a simple example of a resize effect. Notice how the
effects can be chained.

(em/defaction resize-div [width] 
  ["#rz-demo"] (effects/chain 
                 (effects/resize width :curheight 500 20)
                 (effects/resize 5 :curheight 500 20)))

(em/defaction setup []
  ["#button2"] (events/listen #(resize-div 200)))
	
(set! (.-onload js/window) setup)         

Actions, templates and snippets

A snippet is a function that returns a seq of nodes, it can be used as a
building block for more complex templates or actions.

You define a snippet by providing a remote resource, a selector and series of transformations.
The snippet definition below selects a table body from the remote resource
templates/template1.html and grabs the first row. It then fills the content of the row.

  (em/defsnippet snippet2 "templates/template1.html" ["tbody > *:first-child"] 
               [fruit quantity] 
               ["tr > *:first-child"] (ef/content fruit)
               ["tr > *:last-child"] (ef/content (str quantity)))

A template is very similar to a snippet except it does not require a selector to
grap a sub section, instead the entire remote resource is used as the dom.
If the remote resource is a full html document only what is inside the body tag is
brought into the template.

  (em/deftemplate template2 "/templates/template1.html" [fruit-data] 
                ["#heading1"] (ef/content "fruit")  
                ["thead tr > *:last-child"] (ef/content "quantity")
                ["tbody"] (ef/content
                           (map #(snippit2 % (fruit-data %)) (keys fruit-data))))

Normally, snippets and templates are loaded via an AJAX request, but you can
also create :compiled templates, which will be inlined in to resulting code
at compile time:

  (em/deftemplate template2 :compiled "/templates/template1.html" [fruit-data] 
                ["#heading1"] (ef/content "fruit")  
                ["thead tr > *:last-child"] (ef/content "quantity")
                ["tbody"] (ef/content
                           (map #(snippit2 % (fruit-data %)) (keys fruit-data))))

If, snippets and/or templates are loaded via AJAX it is important to make sure the
content has been loaded before calling the template or snippit function. Enfocus
provides a convient macro that works like an onload callback but for AJAX driven
snippets and templates.

(em/wait-for-load (render-page)) 

An action is a set of transforms that take place on the live dom. below is a
definition of a an action.

(em/defaction action2 [] 
             [".cool[foo=false]"] (ef/content (template2 {"banana" 5 "pineapple" 10}))
             ["#heading1"] (ef/set-attr :id "new-heading1"))

Enfocus also support hiccup style emitters introduced in enlive 1.1.0.

(defn hiccup-template [arg1]
  (ef/html
    [:h1#hiccup.clazz {:width arg1} "Hiccup Emitters are Cool"]))

Transformations

A transformation is a function that returns either a node or collection of node.

Enfocus defines several helper functions for transformations:

Supported Enlive Transformations

  content            (content "xyz" a-node "abc")             
  html-content       (html-content "<blink>please no</blink>")
  set-attr           (set-attr :attr1 "val1" :attr2 "val2")
  remove-attr        (remove-attr :attr1 :attr2) 
  add-class          (add-class "foo" "bar")
  remove-class       (remove-class "foo" "bar")
  do->               (do-> transformation1 transformation2) 
  append             (append "xyz" a-node "abc")
  prepend            (prepend "xyz" a-node "abc")
  after              (after "xyz" a-node "abc")
  before             (before "xyz" a-node "abc")
  substitute         (substitute "xyz" a-node "abc")
  clone-for          (clone-for [item items] transformation)
                     or (clone-for [item items] 
                          selector1 transformation1
                          selector2 transformation2)
  wrap               (wrap :div) or (wrap :div {:class "foo"}) 
  unwrap             (unwrap)
  replace-vars       (replace-vars {:var1 "value" :var2 "value")

New Transformations

  focus              (focus)
  blur               (blur)
  set-prop           (set-prop :value "testing")
  set-style          (set-style :font-size "10px" :background "#fff")
  remove-style       (remove-style :font-size :background)
  listen             (listen :mouseover (fn [event] ...))
  remove-listener    (remove-listener :mouseover :mouseout)
  fade-in            (fade-in time)
                     or (fade-in time callback)
                     or (fade-in time callback accelerator)
  fade-out           (fade-out time)
                     or (fade-out time callback)
  resize             (resize width height ttime)
                     or (resize width height ttime callback)
                     or (resize width height ttime callback accelerator)
  move               (move x y ttime)
                     or (move x y ttime callback)
                     or (move x y ttime callback accelerator)
  scroll             (scroll x y ttime)
  		     or (scroll x y ttime callback)
		     or (scroll x y ttime callback accelerator)
  chain              (chain (fade-in ttime) ;serialize async effects
                            (move x y ttime)
                            (fade-out ttime)
                            ...)
  set-data           (set-data key value) 

Currently there is one transformation that is supported by Enlive but not Enfocus. (Patches very welcome!!)

  move               (move [:.footnote] [:#footnotes] content) 
                     ;this will be called relocate in enfocus

Selectors

Enfocus supports both CSS3 and XPath selectors:

(ns example
  (:require [enfocus.core :as ef])
  (:require-macros [enfocus.macros :as em]))

(em/defaction action2 [] 
             [".cool[foo=false]"] (ef/content ....)) ;CSS3
             (ef/xpath "//tr[@id='1']/th[2]") (ef/set-attr :id "new-heading1")) ;XPATH

The from form

The from form is how we data from the dom in enfocus. It comes in two basic flavors listed below:

This form returns the result of the extractor.

(from a-node (extractor arg1 ...))

;or with selector

(from selector (extractor arg1 ...))

and

This form returns a map of the form {:key1 result1 :key2 result2}

(from a-node
    :key1 [selector1] (extractor arg1 ...)
    :key2 [selector2] (extractor arg1 ...))

;or implied js/documnt

(from
    :key1 [selector1] (extractor arg1 ...)
    :key2 [selector2] (extractor arg1 ...))

Extractors

An extractor is a function that takes a node and returns information about.

Enfocus defines several helper fuctions for extractors:

An extractor is a funciton that takes a node and returns information about it.

  get-attr           (get-attr :attr1 :attr2)
  get-text           (get-text)
  get-prop           (get-prop :value)
  get-data           (get-data :key1)
  read-form          (read-form)  - returns {:field1 ("val1" "val2") :field2 val}

Contributing

Compile ClojureScript

 git clone git://github.com/ckirkendall/enfocus.git
 cd enfocus/project
 lein deps
 lein cljx
 lein cljsbuild once

Viewing the Test Page

 cd enfocus/testing
 lein deps
 lein repl
 =>(use 'ring.adapter.jetty)  
 =>(use 'enfocus.ring)
 =>(run-jetty app {:port 3000}) 

Open your browser to:
http://localhost:3000/test.html

License

Copyright © 2012 Creighton Kirkendall

Distributed under the Eclipse Public License, the same as Clojure.

Special Thanks!

Christophe Grand for creating enlive and building a simple api for dom manipulation.