diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+fay-dom
+=======
+
+A FFI wrapper for DOM functions for use with
+[Fay](http://www.fay-lang.org). It includes functions for the more
+commonly used DOM features. See
+[fay-jquery](https://github.com/faylang/fay-jquery) if you want
+wrappers for jQuery.
+
+Usage
+-----
+
+```
+git clone git://github.com/bergmark/fay-dom.git
+cd fay-dom
+cabal install
+```
+
+The library is now installed in your cabal directory and can be seen
+in haskell-mode auto-completion and such. To use this with Fay, use the `--include` flag:
+
+```
+fay --include=~/.cabal/share/fay-dom-0.1.0.0/src myfile.hs
+```
+
+To typecheck `myfile.hs` with ghc, do:
+
+```
+ghc ~/.cabal/share/fay-dom-0.1.0.0/src/ myfile.hs
+```
+
+Development Status
+------------------
+
+Rudimentary at the moment. Functions will be added when people find
+the need for them. A lot of what this library could do already exists
+in fay-jquery, but if anyone wants to write Fay without jQuery feel
+free to add whatever is missing.
+
+
+Contributions
+-------------
+
+Fork on!
+
+Any enhancements are welcome.
+
+The github master might require the latest fay master, available at
+[faylang/fay](https://github.com/faylang/fay/).
diff --git a/fay-dom.cabal b/fay-dom.cabal
--- a/fay-dom.cabal
+++ b/fay-dom.cabal
@@ -1,16 +1,26 @@
 name:                fay-dom
-version:             0.2.0.0
+version:             0.3
 synopsis:            DOM FFI wrapper library for Fay
-description:         DOM FFI wrapper library for Fay.
+description:         DOM FFI wrapper library for Fay
 homepage:            https://github.com/faylang/fay-dom
 bug-reports:         https://github.com/faylang/fay-dom/issues
 license:             BSD3
 license-file:        LICENSE
 author:              Adam Bergmark
-maintainer:          Adam Bergmark
+maintainer:          adam@bergmark.nl
+copyright:           2012 Adam Bergmark
+category:            Web, Fay
 build-type:          Simple
 cabal-version:       >=1.8
+extra-source-files:
+  LICENSE
+  README.md
 data-files:          src/DOM.hs
+
+source-repository head
+  type: git
+  location: https://github.com/faylang/fay-dom.git
+
 
 library
   hs-source-dirs: src
diff --git a/src/DOM.hs b/src/DOM.hs
--- a/src/DOM.hs
+++ b/src/DOM.hs
@@ -29,9 +29,21 @@
 getDocument :: Fay Document
 getDocument = ffi "window.document"
 
+getBody :: Fay Element
+getBody = ffi "document.body"
+
+getElementById :: String -> Fay Element
+getElementById = ffi "document['getElementById'](%1)"
+
+getElementsByName :: String -> Fay [Element]
+getElementsByName = ffi "document['getElementsByName'](%1)"
+
 addEvent :: String -> Fay f -> Fay ()
 addEvent = ffi "window['addEventListener'](%1,%2)"
 
+removeEvent :: Element -> String -> (Event -> Fay f) -> Fay ()
+removeEvent = ffi "%1['removeEventListener'](%2, %3)"
+
 --------------------------------------------------------------------------------
 -- Events.
 
@@ -48,33 +60,185 @@
 createElement = ffi "window['document']['createElement'](%1)"
 
 appendChild :: Element -> Element -> Fay ()
-appendChild = ffi "%1.appendChild(%2)"
+appendChild = ffi "%1['appendChild'](%2)"
 
+appendChildBefore :: Element -> Element -> Fay ()
+appendChildBefore = ffi "%1['parentNode']['insertBefore'](%2, %1)"
+
 removeChild :: Element -> Element -> Fay ()
-removeChild = ffi "%1.removeChild(%2)"
+removeChild = ffi "%1['removeChild'](%2)"
 
 parentNode :: Element -> Fay Element
-parentNode = ffi "%1.parentNode"
+parentNode = ffi "%1['parentNode']"
 
+-- Gets all the child nodes except text, comments etc.
 children :: Element -> Fay NodeList
-children = ffi "%1.children"
+children = ffi "%1['children']"
 
+-- Gets all the child nodes including text, comments etc.
+childNodes :: Element -> Fay NodeList
+childNodes = ffi "%1['childNodes']"
+
+-- Convert a NodeList to an array
+nodeListToArray :: NodeList -> [Element]
+nodeListToArray = ffi "Array.prototype.slice.call(%1)"
+
+-- fetch all nodes between two dom nodes
+nodesBetween :: Element -> Element -> Fay [Element]
+nodesBetween = ffi "\
+  \(function(start, end) {\
+  \  var i, contents, result = [], parentNode = start.parentNode;\
+  \  if(parentNode !== end.parentNode) return;\
+  \  contents = Array.prototype.slice.call(parentNode.childNodes);\
+  \  for(i=contents.indexOf(start); i<contents.indexOf(end); i++) {\
+  \    result.push(contents[i]);\
+  \  }\
+  \})(%1, %2)"
+
+-- remove all nodes between two dom nodes
+removeNodesBetween :: Element -> Element -> Fay ()
+removeNodesBetween = ffi "\
+  \(function(start, end) {\
+  \  var i, contents, parentNode = start.parentNode;\
+  \  if(parentNode !== end.parentNode) return;\
+  \  contents = Array.prototype.slice.call(parentNode.childNodes);\
+  \  for(i=contents.indexOf(start); i<contents.indexOf(end); i++) {\
+  \    parentNode.removeChild(contents[i]);\
+  \  }\
+  \})(%1, %2)"
+
+
+-- Text nodes require special handling
+createTextNode :: String -> Fay Element
+createTextNode = ffi "document['createTextNode'](%1)"
+
+-- Get/Set the text for the text node
+getTextData :: Element -> Fay String
+getTextData = ffi "%1['data']"
+
+-- NOTE: This can only be run on text elements
+setTextData :: Element -> String -> Fay ()
+setTextData = ffi "%1['data'] = %2"
+
+clearInnerHtml :: Element -> Fay ()
+clearInnerHtml = ffi "%1['innerHTML'] = ''"
+
+-- Adding, Removing, and Testing for classes
+-- This only works in modern browsers
+-- https://developer.mozilla.org/en-US/docs/DOM/element.classList
+klass :: Element -> String -> Fay ()
+klass = addClass
+
+addClass :: Element -> String -> Fay ()
+addClass = ffi "%1.classList['add'](%2)"
+
+removeClass :: Element -> String -> Fay ()
+removeClass = ffi "%1['classList']['remove'](%2)"
+
+toggleClass :: Element -> String -> Fay ()
+toggleClass = ffi "%1['classList']['toggle'](%2)"
+
+hasClass :: Element -> String -> Fay Bool
+hasClass = ffi "%1['classList']['contains'](%2)"
+
 --------------------------------------------------------------------------------
+-- Attributes
+
+setAttr :: Element -> String -> String -> Fay ()
+setAttr = ffi "%1['setAttribute'](%2, %3)"
+
+getAttr :: Element -> String -> Fay String
+getAttr = ffi "%1['getAttribute'](%2)"
+
+hasAttr :: Element -> String -> Fay Bool
+hasAttr = ffi "%1['hasAttribute'](%2)"
+
+
+--------------------------------------------------------------------------------
+-- Form elements
+
+-- Get/Set the value for a textfields/textarea/hidden/password input
+-- On checkboxes, this is the value that is sent to the server
+getValue :: Element -> Fay String
+getValue = ffi "%1['value']"
+
+setValue :: Element -> String -> Fay ()
+setValue = ffi "%1['value'] = %2"
+
+-- Get/Set the checked status for checkbox
+isChecked :: Element -> Fay Bool
+isChecked = ffi "%1['checked']"
+
+setChecked :: Element -> Bool -> Fay ()
+setChecked = ffi "%1['checked'] = %2"
+
+-- Get the selected value of a radio group
+getRadioValue :: String -> Fay String
+getRadioValue = ffi "\
+  \(function(name) {\
+  \  var i, rs = document.getElementsByName(name);\
+  \  if(rs) {\
+  \    for(var i=0; i<rs.length; i++) {\
+  \      var radio = rs[i];\
+  \      if(radio.type === 'radio' && radio.checked)\
+  \        return radio.value;\
+  \    }\
+  \  }\
+  \})(%1)"
+
+-- Set the value of a radio group
+setRadioValue :: String -> String -> Fay ()
+setRadioValue = ffi "\
+  \(function(name, val) {\
+  \  var i, rs = document.getElementsByName(name);\
+  \  if(rs) {\
+  \    for(var i=0; i<rs.length; i++) {\
+  \      var radio = rs[i];\
+  \      if(radio.type === 'radio' && radio.value === val)\
+  \        radio.checked = true;\
+  \    }\
+  \  }\
+  \})(%1, %2)"
+
+--------------------------------------------------------------------------------
+-- Location
+
+-- Get current URL
+getCurrentUrl :: Fay String
+getCurrentUrl = ffi "window['location']['href']"
+
+
+--------------------------------------------------------------------------------
+-- Logging
+
+logS :: String -> Fay ()
+logS = ffi "console['log'](%1)"
+
+logF :: f -> Fay ()
+logF = ffi "console['log'](%1)"
+
+
+--------------------------------------------------------------------------------
 -- Timers
 
 -- | setInterval except the calling function gets the timer as an
 -- | argument so the interval can be cancelled from within it.
 setInterval :: Double -> (Timer -> Fay ()) -> Fay Timer
-setInterval = ffi "(function (f,i) { var id = window['setInterval'](function () { f(id); }, i); })(%2,%1)"
+setInterval = ffi "(function (f,i) { var id = window['setInterval'](function () { f(id); }, i); return id; })(%2,%1)"
 
 clearInterval :: Timer -> Fay ()
 clearInterval = ffi "window['clearInterval'](%1)"
 
+-- | setTimeout except the calling function gets the timer as an
+-- | argument. Primarily for symmetry with setInterval.
 setTimeout :: Double -> (Timer -> Fay ()) -> Fay Timer
-setTimeout = ffi "window['setTimeout'](%2,%1)"
+setTimeout = ffi "(function (f,i) { var id = window['setTimeout'](function () { f(id); }, i); return id; })(%2,%1)"
 
+clearTimeout :: Timer -> Fay ()
+clearTimeout = ffi "window['clearTimeout'](%1)"
+
 --------------------------------------------------------------------------------
--- XMLHttpRequest
+-- XHR
 
 data RequestMethod = GET | POST | PUT | HEAD
 
@@ -100,3 +264,26 @@
 
 status :: XMLHttpRequest -> Fay Int
 status = ffi "%1['status']"
+
+--------------------------------------------------------------------------------
+-- Utility
+
+void :: Fay a -> Fay ()
+void m = m >> return ()
+
+-- Read an int
+parseInt :: String -> Fay Int
+parseInt = ffi "parseInt(%1, 10)"
+
+-- Scroll a dom element into view
+scrollIntoView :: Element -> Fay ()
+scrollIntoView = ffi "%1.scrollIntoView()"
+
+-- Scroll the document body by the specified number of pixels
+scrollRelative :: Int -> Fay ()
+scrollRelative = ffi "window.scrollBy(0,%1)"
+
+-- Scroll the document body to the specified pixel height
+scrollAbsolute :: Int -> Fay ()
+scrollAbsolute = ffi "window.scrollTo(0,%1)"
+
