diff --git a/CHANGELOG b/CHANGELOG
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,15 @@
 Version change log.
 
+=0.15=
+Bug fixes in the pretty-printer, the parser and the QuickCheck arbitrary
+instance for the AST. Refactored tests to use test-framework. Reorganized
+the interface for the parser to make it consistent; see deprecation
+warnings for details. Added Language.ECMAScript3.SourceDiff -- a simple
+source-based differ for ECMAScript programs, which is now used for
+visualizing test failures. Revised package dependencies. Factored the
+analyses out in a separate package: language-ecmascript-analysis;
+Language.ECMAScript3.Analysis.* are now deprecated.
+
 =0.14=
 Refactoring of the pretty-printing module: added class Pretty with a more
 comprehensive coverage of AST datatypes and better documentation. Removed
diff --git a/language-ecmascript.cabal b/language-ecmascript.cabal
--- a/language-ecmascript.cabal
+++ b/language-ecmascript.cabal
@@ -1,5 +1,5 @@
 Name:           language-ecmascript
-Version:        0.14
+Version:        0.15
 Cabal-Version:	>= 1.10
 Copyright:      (c) 2007-2012 Brown University, (c) 2008-2010 Claudiu Saftoiu,
                 (c) 2012-2013 Stevens Institute of Technology
@@ -11,15 +11,17 @@
 Homepage:       http://github.com/jswebtools/language-ecmascript
 Bug-reports:    http://github.com/jswebtools/language-ecmascript/issues
 Stability:      experimental
-Tested-with:    GHC==7.4.1, GHC==7.6.3
-Extra-Source-Files: tests/parse-pretty/*.js, CHANGELOG
+Tested-with:    GHC==7.6.3
+Extra-Source-Files: test/parse-pretty/*.js, test/diff/left/*.js, test/diff/right/*.js, test/diff/expects/*.diff, CHANGELOG
 Category:       Language
 Build-Type:     Simple
-Synopsis:       JavaScript analysis tools
+Synopsis:       JavaScript parser and pretty-printer library
 Description:
-  Some tools for working with ECMAScript 3 (popularly known as JavaScript). 
-  Includes a parser, pretty-printer, and basic building blocks for more
-  sophisticated tools.
+  Tools for working with ECMAScript 3 (popularly known as JavaScript).
+  Includes a parser, pretty-printer, a simple quasi-quoter and tools
+  for working with source tree annotations and an arbitrary
+  instance. Analyses have migrated to package
+  'language-ecmascript-analysis'.
 
 Source-repository head
    type: git
@@ -28,7 +30,7 @@
 Source-repository this
    type: git
    location: git://github.com/jswebtools/language-ecmascript.git
-   tag: 0.14
+   tag: 0.15
 
 Library
   Hs-Source-Dirs:
@@ -39,12 +41,11 @@
     parsec < 3.2.0,
     pretty >= 0.1,
     containers >= 0.1,
-    syb >= 0.1,
     uniplate >= 1.6 && <1.7,
-    data-default >= 0.4 && <0.6,
+    data-default-class >= 0.0.1 && < 0.1,
     QuickCheck >= 2.5,
     template-haskell,
-    charset >= 0.3.2.1
+    Diff == 0.3.*
   ghc-options:
     -fwarn-incomplete-patterns
   Exposed-Modules:
@@ -58,51 +59,38 @@
     Language.ECMAScript3.Syntax.QuasiQuote
     Language.ECMAScript3.Analysis.Environment
     Language.ECMAScript3.Analysis.LabelSets
+    Language.ECMAScript3.SourceDiff
+  Other-Modules:
     Language.ECMAScript3.Parser.Type
     Language.ECMAScript3.Parser.State
   Default-Extensions:
     DeriveDataTypeable, ScopedTypeVariables, DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleContexts
   Default-Language: Haskell2010
 
-Test-Suite unittest
-  Hs-Source-Dirs: src
-  Type: exitcode-stdio-1.0
-  Main-Is: UnitTest.hs
-  Build-Depends:
-    base >= 4 && < 5,
-    mtl >= 1.1.0.1,
-    parsec < 3.2.0,
-    pretty >= 0.1,
-    containers >= 0.1,
-    syb >= 0.1,
-    directory,
-    filepath,
-    HUnit,
-    data-default >=0.4 && <0.6,
-    QuickCheck >= 2.5,
-    charset >= 0.3.2.1
-  Default-Extensions: DeriveDataTypeable, ScopedTypeVariables, DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleContexts
-  Default-Language: Haskell2010
-  ghc-options:
-    -fwarn-incomplete-patterns
-
-Test-Suite prettytest
-  Hs-Source-Dirs: src
+Test-Suite test
+  Hs-Source-Dirs: test
   Type: exitcode-stdio-1.0
-  Main-Is: PrettyTest.hs
+  Main-Is: TestMain.hs
+  Other-Modules:
+    Test.Diff
+    Test.Unit
+    Test.Pretty
   Build-Depends:
     base >= 4 && < 5,
     mtl >= 1.1.0.1,
     parsec < 3.2.0,
     pretty >= 0.1,
-    containers >= 0.1,
-    syb >= 0.1,
+    containers >= 0.1 && < 0.6,
     directory,
     filepath,
     HUnit,
-    data-default >=0.4 && <0.6,
-    QuickCheck >= 2.5,
-    uniplate >= 1.6 && <1.7
+    QuickCheck >= 2.5 && < 2.6,
+    data-default-class >= 0.0.1 && < 0.1,
+    test-framework >= 0.8 && < 0.9,
+    test-framework-hunit >= 0.3.0 && < 0.4,
+    test-framework-quickcheck2 >= 0.3.0.1 && < 0.4,
+    Diff == 0.3.*,
+    language-ecmascript
   Default-Extensions: DeriveDataTypeable, ScopedTypeVariables, DeriveFunctor, DeriveFoldable, DeriveTraversable, FlexibleContexts
   Default-Language: Haskell2010
   ghc-options:
diff --git a/src/Language/ECMAScript3/Analysis/Environment.hs b/src/Language/ECMAScript3/Analysis/Environment.hs
--- a/src/Language/ECMAScript3/Analysis/Environment.hs
+++ b/src/Language/ECMAScript3/Analysis/Environment.hs
@@ -1,6 +1,8 @@
 -- | A lexical environment analysis of ECMAScript programs
 
 module Language.ECMAScript3.Analysis.Environment
+       {-# DEPRECATED "Use 'Language.ECMAScript.Analysis.LexicalEnvironment'\
+                      \ from package 'language-ecmascript-analysis'" #-}
   ( env
   , localVars
   , EnvTree (..)
diff --git a/src/Language/ECMAScript3/Analysis/LabelSets.hs b/src/Language/ECMAScript3/Analysis/LabelSets.hs
--- a/src/Language/ECMAScript3/Analysis/LabelSets.hs
+++ b/src/Language/ECMAScript3/Analysis/LabelSets.hs
@@ -3,8 +3,11 @@
 -- section 12.12. The result of this analysis are useful for building
 -- control-flow graphs.
 
-module Language.ECMAScript3.Analysis.LabelSets (annotateLabelSets 
-                                               ,Label(..)) where
+module Language.ECMAScript3.Analysis.LabelSets
+       {-# DEPRECATED "Use 'Language.ECMAScript3.Analysis.LabelSet'\
+                      \ from package 'language-ecmascript-analysis'" #-}
+       (annotateLabelSets 
+       ,Label(..)) where
 
 import Language.ECMAScript3.Syntax
 import Language.ECMAScript3.Syntax.Annotations
diff --git a/src/Language/ECMAScript3/Parser.hs b/src/Language/ECMAScript3/Parser.hs
--- a/src/Language/ECMAScript3/Parser.hs
+++ b/src/Language/ECMAScript3/Parser.hs
@@ -2,6 +2,13 @@
 {-# LANGUAGE FlexibleContexts #-}
 module Language.ECMAScript3.Parser
   (parse
+  , Parser  
+  , expression
+  , statement
+  , program
+  , parseFromString
+  , parseFromFile
+  -- old and deprecated   
   , parseScriptFromString
   , parseJavaScriptFromFile
   , parseScript
@@ -15,11 +22,7 @@
   , StatementParser
   , ExpressionParser
   , assignExpr
-  -- debugging, remove the next 2 lines
-  , mkDecimal
-  , intLen
   , parseObjectLit  
-  , Parser  
   ) where
 
 import Language.ECMAScript3.Lexer hiding (identifier)
@@ -28,7 +31,7 @@
 import Language.ECMAScript3.Parser.Type
 import Language.ECMAScript3.Syntax
 import Language.ECMAScript3.Syntax.Annotations
-import Data.Default
+import Data.Default.Class
 import Text.Parsec hiding (parse)
 import Text.Parsec.Expr
 import Control.Monad(liftM,liftM2)
@@ -37,7 +40,25 @@
 import Data.Char
 import Control.Monad.Identity
 import Data.Maybe (isJust, isNothing, fromMaybe)
+import Control.Monad.Error.Class
 
+{-# DEPRECATED ParsedStatement, ParsedExpression, StatementParser,
+               ExpressionParser
+    "These type aliases will be hidden in the next version" #-}
+
+{-# DEPRECATED parseSimpleExpr', parseBlockStmt, parseObjectLit
+   "These parsers will be hidden in the next version" #-}
+
+{-# DEPRECATED assignExpr, parseExpression "Use 'expression' instead" #-}
+
+{-# DEPRECATED parseStatement "Use 'statement' instead" #-}
+
+{-# DEPRECATED parseScript "Use 'program' instead" #-}
+
+{-# DEPRECATED parseScriptFromString, parseString "Use 'parseFromString' instead" #-}
+
+{-# DEPRECATED parseJavaScriptFromFile "Use 'parseFromFile' instead" #-}
+
 -- We parameterize the parse tree over source-locations.
 type ParsedStatement = Statement SourcePos
 type ParsedExpression = Expression SourcePos
@@ -46,7 +67,6 @@
 type StatementParser s = Parser s ParsedStatement
 type ExpressionParser s = Parser s ParsedExpression
 
-
 initialParserState :: ParserState
 initialParserState = []
 
@@ -303,7 +323,7 @@
                       "function body in { ... }"
   return (FunctionStmt pos name args body)
 
-parseStatement:: Stream s Identity Char => StatementParser s
+parseStatement :: Stream s Identity Char => StatementParser s
 parseStatement = parseIfStmt <|> parseSwitchStmt <|> parseWhileStmt 
   <|> parseDoWhileStmt <|> parseContinueStmt <|> parseBreakStmt 
   <|> parseBlockStmt <|> parseEmptyStmt <|> parseForInStmt <|> parseForStmt
@@ -312,6 +332,10 @@
   -- labelled, expression and the error message always go last, in this order
   <|> parseLabelledStmt <|> parseExpressionStmt <?> "statement"
 
+-- | The parser that parses a single ECMAScript statement
+statement :: Stream s Identity Char => Parser s (Statement SourcePos)
+statement = parseStatement
+
 --}}}
 
 --{{{ Expressions
@@ -473,7 +497,7 @@
 --{{{ Parsing numbers.  From pg. 17-18 of ECMA-262.
 hexLit :: Stream s Identity Char => Parser s (Bool, Double)
 hexLit = do
-  try (string "0x")
+  try (char '0' >> oneOf "xX")
   digits <- many1 (oneOf "0123456789abcdefABCDEF")
   [(hex,"")] <- return $ Numeric.readHex digits
   return (True, hex)
@@ -735,6 +759,10 @@
 parseExpression:: Stream s Identity Char => ExpressionParser s
 parseExpression = assignExpr
 
+-- | A parser that parses ECMAScript expressions
+expression :: Stream s Identity Char => Parser s (Expression SourcePos)
+expression = assignExpr
+
 parseListExpr :: Stream s Identity Char => ExpressionParser s
 parseListExpr = assignExpr `sepBy1` comma >>= \exprs ->
   case exprs of
@@ -745,14 +773,39 @@
 parseScript = do
   whiteSpace
   liftM2 Script getPosition (parseStatement `sepBy` whiteSpace)
+
+-- | A parser that parses an ECMAScript program.
+program :: Stream s Identity Char => Parser s (JavaScript SourcePos)
+program = parseScript
   
--- | Parse from a stream; same as 'Text.Parsec.parse'
+-- | Parse from a stream given a parser, same as 'Text.Parsec.parse'
+-- in Parsec. We can use this to parse expressions or statements alone,
+-- not just whole programs.
 parse :: Stream s Identity Char
       => Parser s a -- ^ The parser to use
       -> SourceName -- ^ Name of the source file
       -> s -- ^ the stream to parse, usually a 'String'
       -> Either ParseError a
 parse p = runParser p initialParserState
+
+-- | A convenience function that takes a 'String' and tries to parse
+-- it as an ECMAScript program:
+--
+-- > parseFromString = parse program ""
+parseFromString :: String -- ^ JavaScript source to parse
+                  -> Either ParseError (JavaScript SourcePos)
+parseFromString = parse program ""
+
+-- | A convenience function that takes a filename and tries to parse
+-- the file contents an ECMAScript program, it fails with an error
+-- message if it can't.
+parseFromFile :: (Error e, MonadIO m, MonadError e m) => String -- ^ file name
+                -> m (JavaScript SourcePos)
+parseFromFile fname =
+  liftIO (readFile fname) >>= \source ->
+  case parse program fname source of
+    Left err -> throwError $ strMsg $ show err
+    Right js -> return js
 
 -- | Read a JavaScript program from file an parse it into a list of
 -- statements
diff --git a/src/Language/ECMAScript3/Parser/Type.hs b/src/Language/ECMAScript3/Parser/Type.hs
--- a/src/Language/ECMAScript3/Parser/Type.hs
+++ b/src/Language/ECMAScript3/Parser/Type.hs
@@ -4,4 +4,6 @@
 import Text.Parsec
 import Control.Monad.Identity
 
+-- | The parser type, parametrised by the stream type @s@ and the
+-- return value @a@
 type Parser s a = ParsecT s ParserState Identity a
diff --git a/src/Language/ECMAScript3/PrettyPrint.hs b/src/Language/ECMAScript3/PrettyPrint.hs
--- a/src/Language/ECMAScript3/PrettyPrint.hs
+++ b/src/Language/ECMAScript3/PrettyPrint.hs
@@ -228,13 +228,22 @@
     sel '\\' = "\\\\"
     sel x    = [x]
     -- We don't have to do anything about \X, \x and \u escape sequences.
-    
+
+-- | Escapes a regular expression so that it can be parsed correctly afterwards
 regexpEscape :: String -> String
-regexpEscape "" = ""
-regexpEscape "\\" = "\\\\"
-regexpEscape ('\\':c:rest) = '\\':c:(regexpEscape rest)
-regexpEscape ('/':rest) = '\\':'/':regexpEscape rest
-regexpEscape (c:rest)   = c:regexpEscape rest
+regexpEscape = regexpEscapeChar True
+  where regexpEscapeChar :: Bool -- ^ First char?
+                         -> String -> String
+        regexpEscapeChar first s = 
+          case (s, first) of
+            ("", True) -> "(?:)"
+            ("", False)-> ""
+            -- see spec 7.8.5, RegularExpressionFirstChar
+            ("\\", _) -> "\\\\"
+            ('\\':c:rest, _) -> '\\':c:(regexpEscapeChar False rest)
+            ('/':rest, _) -> '\\':'/':regexpEscapeChar False rest
+            ('*':rest, True) -> ('\\':'*':regexpEscapeChar False rest)
+            (c:rest, _)   -> c:regexpEscapeChar False rest
 
 -- 11.1
 ppPrimaryExpression :: Expression a -> Doc
diff --git a/src/Language/ECMAScript3/SourceDiff.hs b/src/Language/ECMAScript3/SourceDiff.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/ECMAScript3/SourceDiff.hs
@@ -0,0 +1,24 @@
+-- | Simple textual diffing of JavaScript programs for inspecting test
+-- failures
+module Language.ECMAScript3.SourceDiff where
+
+import Data.Algorithm.Diff
+--import Data.Algorithm.DiffOutput
+import Language.ECMAScript3.Syntax
+import Language.ECMAScript3.PrettyPrint
+import Data.List (intersperse)
+
+
+jsDiff :: JavaScript a -> JavaScript a -> String
+jsDiff js1 js2 = 
+  -- let plines = lines . show . prettyPrint
+  -- in  ppDiff $ getGroupedDiff (plines js1) (plines js2)
+  let plines = lines . show . prettyPrint
+      diff = getGroupedDiff (plines js1) (plines js2)
+      formatDiff :: Diff [String] -> String
+      formatDiff d = let (prefix, strs) = case d of
+                           First ss  -> ('-', ss)
+                           Second ss -> ('+', ss)
+                           Both ss _ -> (' ', ss)
+                     in concat $ intersperse "\n" $  map (prefix:) strs
+  in  concat $ intersperse "\n" $ map formatDiff diff
diff --git a/src/Language/ECMAScript3/Syntax.hs b/src/Language/ECMAScript3/Syntax.hs
--- a/src/Language/ECMAScript3/Syntax.hs
+++ b/src/Language/ECMAScript3/Syntax.hs
@@ -26,10 +26,11 @@
                                    ) where
 
 import Text.Parsec.Pos(initialPos,SourcePos) -- used by data JavaScript
-import Data.Generics(Data,Typeable)
+import Data.Data (Data)
+import Data.Typeable (Typeable)
 import Data.Foldable (Foldable)
 import Data.Traversable (Traversable)
-import Data.Default
+import Data.Default.Class
 
 data JavaScript a
   -- |A script in \<script\> ... \</script\> tags.
diff --git a/src/Language/ECMAScript3/Syntax/Arbitrary.hs b/src/Language/ECMAScript3/Syntax/Arbitrary.hs
--- a/src/Language/ECMAScript3/Syntax/Arbitrary.hs
+++ b/src/Language/ECMAScript3/Syntax/Arbitrary.hs
@@ -110,6 +110,12 @@
 atLeastOfSize :: Arbitrary a => Int -> Gen a -> Gen a
 atLeastOfSize l gen = sized $ \s -> if s < l then resize l gen else gen
 
+listOfN :: Arbitrary a => Int -> Gen a -> Gen [a]
+listOfN l gen = sized $ \n ->
+  let l' = l `max` 0
+  in do k <- choose (l', l' `max` n)
+        vectorOf k gen
+
 nonEmptyString :: Gen String
 nonEmptyString = sized $ \s -> if s < 1 then stringOfLength 1 else stringOfLength s
 
@@ -144,7 +150,7 @@
           (2, liftM4 InfixExpr arbitrary arbitrary rarbitrary rarbitrary),
           (3, liftM4 CondExpr arbitrary rarbitrary rarbitrary rarbitrary),
           (3, liftM4 AssignExpr arbitrary rarbitrary rarbitrary rarbitrary),
-          (3, liftM2 ListExpr arbitrary (recursive (atLeastOfSize 2 arbitrary))),
+          (3, liftM2 ListExpr arbitrary (recursive (listOfN 2 arbitrary))),
           (3, liftM3 CallExpr arbitrary rarbitrary rrarbitrary),
           (1, liftM4 FuncExpr arbitrary arbitrary arbitrary rarbitrary)]
     
@@ -216,7 +222,7 @@
           (1, liftM2 ThrowStmt arbitrary rarbitrary),
           (1, liftM2 ReturnStmt arbitrary rarbitrary),
           (2, liftM3 WithStmt arbitrary rarbitrary rarbitrary),
-          (2, liftM2 VarDeclStmt arbitrary rrarbitrary),
+          (2, liftM2 VarDeclStmt arbitrary (listOf1 rrarbitrary)),
           (1, liftM4 FunctionStmt arbitrary arbitrary arbitrary rarbitrary)]
     where arbtry = 
             do (mCatch, mFinally) <- oneof [liftM2 (,) (return Nothing) (liftM Just rarbitrary),
@@ -330,7 +336,7 @@
               else True
   --  _ -> continue stmt inLabeled inIter inSwitch
   where continue stmt inLabeled inIter inSwitch =
-                   and $ map (\s -> isBreakContinueFixable s inLabeled inIter inSwitch) (children stmt)
+          and $ map (\s -> isBreakContinueFixable s inLabeled inIter inSwitch) (children stmt)
                    
 -- | Removes duplicate labels from nested labeled statements in order
 -- to impose restrictions on labeled statements as per ECMAScript 5
diff --git a/src/Language/ECMAScript3/Syntax/QuasiQuote.hs b/src/Language/ECMAScript3/Syntax/QuasiQuote.hs
--- a/src/Language/ECMAScript3/Syntax/QuasiQuote.hs
+++ b/src/Language/ECMAScript3/Syntax/QuasiQuote.hs
@@ -23,13 +23,13 @@
 js = QuasiQuoter {quoteExp = quoteJS}
 
 quoteJSExpr :: String -> TH.ExpQ
-quoteJSExpr = quoteCommon parseExpression
+quoteJSExpr = quoteCommon expression
 
 quoteJSStmt :: String -> TH.ExpQ
-quoteJSStmt = quoteCommon parseStatement
+quoteJSStmt = quoteCommon statement
 
 quoteJS :: String -> TH.ExpQ
-quoteJS = quoteCommon parseScript
+quoteJS = quoteCommon program
 
 quoteCommon :: Data a => Parser String a -> String -> TH.ExpQ
 quoteCommon p s = do loc <- TH.location
diff --git a/src/PrettyTest.hs b/src/PrettyTest.hs
deleted file mode 100644
--- a/src/PrettyTest.hs
+++ /dev/null
@@ -1,29 +0,0 @@
-module Main where
-
-import Language.ECMAScript3.Parser
-import Language.ECMAScript3.PrettyPrint
-import Language.ECMAScript3.Syntax
-import Language.ECMAScript3.Syntax.Arbitrary()
-import Language.ECMAScript3.Syntax.Annotations
-import Test.QuickCheck
-import System.Exit
-
-main :: IO ()
-main = 
-  let qcArgs = Args {maxSuccess = 50
-                    ,maxDiscardRatio = 10
-                    ,maxSize = 10
-                    ,replay = Nothing
-                    ,chatty = True}
-  in quickCheckWithResult qcArgs prettyParseEquivalence >>= \res ->
-  case res of
-    Success {} -> putStrLn "All tests passes"
-    GaveUp {} -> putStrLn "Gave up"
-    Failure {} -> putStrLn "Test failed" >> exitFailure
-    NoExpectedFailure {} -> putStrLn "Unexpected failure" >> exitFailure
-        
-prettyParseEquivalence :: JavaScript () -> Bool
-prettyParseEquivalence js = 
-  case parseScriptFromString "" $ show $ prettyPrint js of
-    Left _ -> False
-    Right parsed -> (reannotate (const ()) parsed) == js
diff --git a/src/UnitTest.hs b/src/UnitTest.hs
deleted file mode 100644
--- a/src/UnitTest.hs
+++ /dev/null
@@ -1,33 +0,0 @@
-module Main where
-
-import Test.HUnit
-import System.Exit
-import System.Directory
-import qualified System.FilePath as FilePath
-import Language.ECMAScript3.Parser
-import Language.ECMAScript3.PrettyPrint
-import Language.ECMAScript3.Syntax
-
-testDir = "tests/parse-pretty"
-
-
--- | tests the parser with pre-defined test-cases
-parsePrettyTest filename = TestLabel filename $ TestCase $ do
-  js <- parseJavaScriptFromFile filename
-  let str = renderStatements js
-  case parseScriptFromString "" str of
-    Left err -> assertFailure (show err)
-    Right (Script _ js') -> do
-      let str' = renderStatements js'
-      assertBool "pretty-printed code should re-parse" (str == str')
-
-main = do
-  allFiles <- getDirectoryContents testDir
-  let files =  map (testDir `FilePath.combine`) $ 
-        filter (\x -> FilePath.takeExtension x == ".js") allFiles
-  let parsePretty = TestLabel "parser - printer composition" 
-        (TestList (map parsePrettyTest files))
-  results <- runTestTT parsePretty
-  if errors results > 0 || failures results > 0 
-    then exitFailure
-    else putStrLn "All tests passed."
diff --git a/test/Test/Diff.hs b/test/Test/Diff.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Diff.hs
@@ -0,0 +1,58 @@
+module Test.Diff where
+
+import Test.HUnit hiding (Test)
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import System.Exit
+import System.Directory
+import qualified System.FilePath as FP
+import Language.ECMAScript3.Parser
+import Language.ECMAScript3.PrettyPrint
+import Language.ECMAScript3.Syntax
+import Language.ECMAScript3.Syntax.Annotations
+import Language.ECMAScript3.SourceDiff
+import Control.Monad
+
+tests_diff :: Test
+tests_diff =
+  buildTest $
+  do allLefts  <- getDirectoryContents leftDir
+     allRights <- getDirectoryContents rightDir
+     allDiffs  <- getDirectoryContents expectsDir
+     let validLefts  = getValidJS allLefts
+     let validRights = getValidJS allRights
+     let validDiffs  = getValidDiffs allDiffs
+     return $ testGroup "Source Diff tests" $
+       map genTest $ filter ((`elem` (map FP.dropExtension validDiffs)) .
+                             FP.dropExtension)
+                   $ filter (`elem` validRights) validLefts
+       where getValidJS = filter $ \x -> FP.takeExtension x == ".js"
+             getValidDiffs = filter $ \x -> FP.takeExtension x == ".diff"
+
+leftDir = "test/diff/left"
+rightDir = "test/diff/right"
+expectsDir = "test/diff/expects"
+
+genTest :: FilePath -> Test
+genTest testFileName = testCase testFileName $
+                       diffTest (leftDir `FP.combine` testFileName)
+                                (rightDir `FP.combine` testFileName)
+                                (expectsDir `FP.combine`
+                                 ((FP.dropExtension testFileName)
+                                  `FP.addExtension` "diff"))
+
+diffTest :: FilePath -> FilePath -> FilePath -> Assertion
+diffTest leftFile rightFile diffFile =
+  do left  <- readFile leftFile
+     right <- readFile rightFile
+     expect<- readFile diffFile
+     let x = do ljs <- parseFromString left
+                rjs <- parseFromString right
+                return (removeAnnotations ljs, removeAnnotations rjs)
+     case x of
+       Left err -> assertFailure $ "Parsing error: " ++ (show err)
+       Right (ljs, rjs) ->
+         let diff = jsDiff ljs rjs
+             msg = "Failed to match diff output to an expected one. Expected:\n"
+                   ++ expect ++ "\nSaw:\n" ++ diff
+         in unless (diff == expect) (assertFailure msg)
diff --git a/test/Test/Pretty.hs b/test/Test/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Pretty.hs
@@ -0,0 +1,43 @@
+module Test.Pretty where
+
+import Test.Framework
+import Test.Framework.Providers.QuickCheck2
+import Language.ECMAScript3.Parser
+import Language.ECMAScript3.PrettyPrint
+import Language.ECMAScript3.Syntax
+import Language.ECMAScript3.Syntax.Arbitrary()
+import Language.ECMAScript3.Syntax.Annotations
+--import System.Exit
+import Language.ECMAScript3.SourceDiff
+import Test.QuickCheck
+
+tests_pretty :: Test
+tests_pretty = testProperty "Parse is the inverse of pretty" prettyParseEquivalence
+
+-- main :: IO ()
+-- main = 
+--   let qcArgs = Args {maxSuccess = 50
+--                     ,maxDiscardRatio = 10
+--                     ,maxSize = 10
+--                     ,replay = Nothing
+--                     ,chatty = False}
+--   in quickCheckWithResult qcArgs prettyParseEquivalence >>= \res ->
+--   case res of
+--     Success {} -> putStrLn "All tests passes"
+--     GaveUp {} -> putStrLn "Gave up"
+--     Failure {} -> putStrLn "Test failed" >> exitFailure
+--     NoExpectedFailure {} -> putStrLn "Unexpected failure" >> exitFailure
+
+prettyParseEquivalence :: JavaScript () -> Property
+prettyParseEquivalence orig =
+  let pp = show $ prettyPrint orig
+  in case parseFromString pp of
+    Left e -> 
+      let err = "Can't parse pretty-printed code. The error was: " ++ (show e) ++
+                "\nThe pretty-printed code in question:\n" ++ pp
+      in whenFail (putStrLn err) False
+    Right parsed ->
+      let eq = (removeAnnotations parsed) == orig
+          msg ="The parse of the pretty-printed AST didn't match the original\n"
+               ++"Diff:\n" ++ jsDiff orig (reannotate (const ()) parsed)
+      in whenFail (putStrLn msg) eq
diff --git a/test/Test/Unit.hs b/test/Test/Unit.hs
new file mode 100644
--- /dev/null
+++ b/test/Test/Unit.hs
@@ -0,0 +1,49 @@
+module Test.Unit where
+
+import Test.Framework
+import Test.Framework.Providers.HUnit
+import Test.HUnit hiding (Test)
+import System.Exit
+import System.Directory
+import qualified System.FilePath as FilePath
+import Language.ECMAScript3.Parser
+import Language.ECMAScript3.PrettyPrint
+import Language.ECMAScript3.Syntax
+import Language.ECMAScript3.Syntax.Annotations
+import Language.ECMAScript3.SourceDiff
+import Control.Monad
+
+
+tests_unit :: Test
+tests_unit =
+  buildTest $
+  do allFiles <- getDirectoryContents testDir
+     let validFiles = filter (\x -> FilePath.takeExtension x == ".js") allFiles
+     return $ testGroup "Parser Unit tests" $  map genTest validFiles
+
+
+genTest :: FilePath -> Test
+genTest file = testCase file $ parsePrettyTest (testDir `FilePath.combine` file) 
+
+testDir = "test/parse-pretty"
+
+-- | tests the parser with pre-defined test-cases
+parsePrettyTest :: FilePath -> Assertion
+parsePrettyTest filename =
+  readFile filename >>= \src ->
+  case parseFromString src of
+    Left err -> assertFailure $ "Can't parse a test-case: " ++ filename
+    Right js -> let str = show $ prettyPrint js
+                in  case parseFromString str of
+                  Left err ->
+                    let msg = "Can't parse pretty-printed code. The error was: "
+                              ++ (show err)
+                              ++ "\nThe pretty-printed code in question:\n" ++ str
+                    in assertFailure msg
+                  Right js' -> do
+                    let str' = show $ prettyPrint js'
+                    unless (str == str') $ do
+                      let msg = "The parse of the pretty-printed AST didn't match the original\n"
+                                ++ "Diff:\n" ++ jsDiff js js'
+                      assertFailure msg
+
diff --git a/test/TestMain.hs b/test/TestMain.hs
new file mode 100644
--- /dev/null
+++ b/test/TestMain.hs
@@ -0,0 +1,12 @@
+-- | main entry point for tests
+module Main where
+
+import Test.Framework
+import Test.Unit
+import Test.Pretty
+import Test.Diff
+          
+-- entry point for the test-suite
+main = defaultMain tests
+
+tests = [tests_diff, tests_pretty, tests_unit]
diff --git a/test/diff/expects/t1.diff b/test/diff/expects/t1.diff
new file mode 100644
--- /dev/null
+++ b/test/diff/expects/t1.diff
@@ -0,0 +1,2 @@
+ x = 1;
+-y = 2;
diff --git a/test/diff/left/t1.js b/test/diff/left/t1.js
new file mode 100644
--- /dev/null
+++ b/test/diff/left/t1.js
@@ -0,0 +1,2 @@
+x = 1;
+y = 2;
diff --git a/test/diff/right/t1.js b/test/diff/right/t1.js
new file mode 100644
--- /dev/null
+++ b/test/diff/right/t1.js
@@ -0,0 +1,1 @@
+x = 1;
diff --git a/test/parse-pretty/add.js b/test/parse-pretty/add.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/add.js
@@ -0,0 +1,1 @@
+1+2;;;;
diff --git a/test/parse-pretty/do-while.js b/test/parse-pretty/do-while.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/do-while.js
@@ -0,0 +1,3 @@
+do {
+  x = y;
+} while (element);
diff --git a/test/parse-pretty/flapjax-fxinternal-22dec2008.js b/test/parse-pretty/flapjax-fxinternal-22dec2008.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/flapjax-fxinternal-22dec2008.js
@@ -0,0 +1,2552 @@
+function flapjaxInit(options) {
+  // compress via http://alex.dojotoolkit.org/shrinksafe/
+  // make sure to change final eval call to use exported library renamed
+  // name
+
+  var defaultOptions = {
+    includeSynonyms: true,
+    exportMisc: true,
+    exportCore: true,
+    exportDOM: true,
+    exportDOMMisc: true,
+    exportWS: true,
+    hide: [ ],
+    show: [ ],
+    redefine: false
+  };
+
+  if (options === false) {
+    options = { hide: [ ], show: [ ] };
+  }
+  else if (options === true || !options) {
+    options = { };
+  }
+
+  // Fill in any missing options using the defaults.
+  for (var option in defaultOptions) {
+    if (options[option] === undefined) {
+      options[option] = defaultOptions[option];
+    }
+  };
+
+  // Transform hide and show lists into tables
+  var hideArray = options.hide;
+  options.hide = { };
+  for (var i = 0; i < hideArray.length; i++) {
+    options.hide[hideArray[i]] = true;
+  }
+  var showArray = options.show;
+  options.show = { };
+  for (var i = 0; i < showArray.length; i++) {
+    options.show[showArray[i]] = true;
+  }
+
+  var warn = function(s) {
+    /*if (console && console.warn) {
+      console.warn(s);
+    }*/
+  };
+
+
+  var flapjax = {
+    version: 4,
+    base: {}, //pulses
+    combinators: {}, // combinators yielding nodes
+	  behaviours: {},
+    dom: {} // dom convenience methods and combinators
+  };
+
+  var d = flapjax.dom;
+
+	flapjax.pub = {util:flapjax};
+
+	var annotate = function(fn,names,protoArg,protoObjs,protoNames) {
+		for(var i=0; i<names.length;i++) {
+			flapjax.pub[names[i]] = fn;
+		}
+		if(protoArg != undefined) {
+			var pf = function() {
+				var args = slice(arguments,0);
+				args.splice(protoArg,0,this);
+				return fn.apply(this,args);
+			};
+			for(var i=0; i<protoObjs.length; i++) {
+				for(var j=0; j<protoNames.length;j++) {
+					protoObjs[i][protoNames[j]] = pf;
+				}
+			}
+		}
+	};
+
+  var exports = { };
+  var synonyms = { };
+
+  var misc = { }; /* Used as a symbol */
+  var core = { }; /* Used as a symbol */
+  var dom = { }; /* Used as a symbol */
+  var domMisc = { }; /* Used as a symbol */
+  var ws = { }; /* Used as a symbol */
+
+  var fxExport = function(exportCategory,exportVal,
+                          exportName /* , synonyms */) {
+    if (!(exportCategory instanceof Object)) {
+      throw 'fxExport: category is ' + exportCategory;
+    };
+
+    if (typeof(exportName) != 'string') {
+      console.log(exportVal);
+      throw 'fxExport: name is ' + exportName;
+    };
+
+    exportVal.__flapjaxCategory = exportCategory;
+    exports[exportName] = exportVal;
+    for (var i = 3; i < arguments.length; i++) {
+      exports[arguments[i]] = exportVal;
+      synonyms[arguments[i]] = true;
+    }
+  };
+
+  var fxMethodSynonyms = function(obj,method) {
+    if (options.includeSynonyms === true) {
+      for (var i = 2; i < arguments.length; i++) {
+        obj.prototype[arguments[i]] = obj.prototype[method];
+      };
+    };
+  };
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Miscellaneous functions
+
+	//credit 4umi
+	//slice: Array a * Integer * Integer -> Array a
+	var slice = function (arr, start, stop) {
+		var i, len = arr.length, r = [];
+		if( !stop ) { stop = len; }
+		if( stop < 0 ) { stop = len + stop; }
+		if( start < 0 ) { start = len - start; }
+		if( stop < start ) { i = start; start = stop; stop = i; }
+		for( i = 0; i < stop - start; i++ ) { r[i] = arr[start+i]; }
+		return r;
+	}
+
+	var isEqual = function (a,b) {
+		return (a == b) ||
+			( (((typeof(a) == 'number') && isNaN(a)) || a == 'NaN') &&
+			  (((typeof(b) == 'number') && isNaN(b)) || b == 'NaN') );
+	};
+
+	//member: a * Array b -> Boolean
+	var member = function(elt, lst) {
+		for (var i = 0; i < lst.length; i++) {
+			if (isEqual(lst[i], elt)) {return true;}
+		}
+		return false;
+	};
+
+	var zip = function(arrays) {
+		if (arrays.length == 0) return [];
+		var ret = [];
+		for(var i=0; i<arrays[0].length;i++) {
+			ret.push([]);
+			for(var j=0; j<arrays.length;j++)
+				ret[i].push(arrays[j][i]);
+		}
+		return ret;
+	};
+
+	//map: (a * ... -> z) * [a] * ... -> [z]
+	var map = function (fn) {
+		var arrays = slice(arguments, 1);
+		if (arrays.length === 0) { return []; }
+		else if (arrays.length === 1) {
+			var ret = [];
+			for(var i=0; i<arrays[0].length; i++) {ret.push(fn(arrays[0][i]));}
+			return ret;
+		}
+		else {
+			var ret = zip(arrays);
+			var o = new Object();
+			for(var i=0; i<ret.length; i++) {ret[i] = fn.apply(o,ret[i]);}
+			return ret;
+		}
+	};
+
+	//filter: (a -> Boolean) * Array a -> Array a
+	var filter = function (predFn, arr) {
+		var res = [];
+		for (var i = 0; i < arr.length; i++) {
+			if (predFn(arr[i])) { res.push(arr[i]); }
+		}
+		return res;
+	};
+
+  //fold: (a * .... * accum -> accum) * accum * [a] * ... -> accum
+  //fold over list(s), left to right
+  var fold = function(fn, init /* arrays */) {
+		var lists = slice(arguments, 2);
+		if (lists.length === 0) { return init; }
+		else if(lists.length === 1) {
+			var acc = init;
+			for(var i = 0; i < lists[0].length; i++) {
+				acc = fn(lists[0][i],acc);
+			}
+			return acc;
+		}
+		else {
+			var acc = init;
+			for (var i = 0; i < lists[0].length; i++) {
+				var args = map( function (lst) { return lst[i];},
+		          lists);
+				args.push(acc);
+				acc = fn.apply({}, args);
+			}
+			return acc;
+		}
+  };
+
+  //foldR: (a * .... * accum -> accum) * accum * [a] * ... -> accum
+	//fold over list(s), right to left, fold more memory efficient (left to right)
+	var foldR = function (fn, init /* arrays */) {
+    var lists = slice(arguments, 2);
+		if (lists.length === 0) { return init; }
+		else if(lists.length === 1) {
+			var acc = init;
+			for(var i=lists[0].length - 1; i > -1; i--)
+				acc = fn(lists[0][i],acc);
+			return acc;
+		}
+		else {
+			var acc = init;
+			for (var i = lists[0].length - 1; i > -1; i--) {
+				var args = map( function (lst) { return lst[i];},
+		          lists);
+				args.push(acc);
+				acc = fn.apply({}, args);
+			}
+			return acc;
+		}
+	};
+
+  var pushAll = function(destArray,srcArray) {
+    for (var i = 0; i < srcArray.length; i++) {
+      destArray.push(srcArray[i]);
+    }
+    return destArray;
+  };
+
+  fxExport(misc,slice,'slice');
+  fxExport(misc,member,'member');
+  fxExport(misc,map,'map');
+  fxExport(misc,map,'forEach');
+  fxExport(misc,filter,'filter');
+  fxExport(misc,fold,'fold');
+  fxExport(misc,foldR,'foldR');
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Flapjax core
+
+  //Pulse: Stamp * Path * Obj
+  var Pulse = function (stamp, value) {
+    this.stamp = stamp;
+    this.value = value;
+  };
+
+
+  //Probably can optimize as we expect increasing insert runs etc
+  var PQ = function () {
+    var ctx = this;
+    ctx.val = [];
+    this.insert = function (kv) {
+			ctx.val.push(kv);
+			var kvpos = ctx.val.length-1;
+			while(kvpos > 0 && kv.k < ctx.val[Math.floor((kvpos-1)/2)].k) {
+				var oldpos = kvpos;
+				kvpos = Math.floor((kvpos-1)/2);
+				ctx.val[oldpos] = ctx.val[kvpos];
+				ctx.val[kvpos] = kv;
+			}
+    };
+    this.isEmpty = function () {
+      return ctx.val.length === 0;
+    };
+    this.pop = function () {
+			if(ctx.val.length == 1) {
+				return ctx.val.pop();
+			}
+			var ret = ctx.val.shift();
+			ctx.val.unshift(ctx.val.pop());
+			var kvpos = 0;
+			var kv = ctx.val[0];
+			while(1) {
+				var leftChild = (kvpos*2+1 < ctx.val.length ? ctx.val[kvpos*2+1].k : kv.k+1);
+				var rightChild = (kvpos*2+2 < ctx.val.length ? ctx.val[kvpos*2+2].k : kv.k+1);
+				if(leftChild > kv.k && rightChild > kv.k)
+					break;
+				else if(leftChild < rightChild) {
+					ctx.val[kvpos] = ctx.val[kvpos*2+1];
+					ctx.val[kvpos*2+1] = kv;
+					kvpos = kvpos*2+1;
+				}
+				else {
+					ctx.val[kvpos] = ctx.val[kvpos*2+2];
+					ctx.val[kvpos*2+2] = kv;
+					kvpos = kvpos*2+2;
+				}
+			}
+			return ret;
+		};
+  };
+
+  var lastRank = 0;
+  var stamp = 1;
+  var nextStamp = function () { return ++stamp; };
+
+  //propagatePulse: Pulse * Array Node ->
+  //Send the pulse to each node
+  var propagatePulse = function (pulse, node) {
+    var queue = new PQ(); //topological queue for current timestep
+	queue.insert({k:node.rank,n:node,v:pulse});
+	while(!(queue.isEmpty())) {
+		var qv = queue.pop();
+		qv.n.updater(function(nextPulse) {
+			for(var i=0; i<qv.n.sendsTo.length;i++)
+				queue.insert({k:qv.n.sendsTo[i].rank,n:qv.n.sendsTo[i],v:nextPulse});
+		},new Pulse(qv.v.stamp,qv.v.value));
+	}
+  };
+
+  //Event: Array Node b * ( (Pulse a -> Void) * Pulse b -> Void)
+  var EventStream = function (nodes,updater) {
+    this.updater = updater;
+
+    this.sendsTo = []; //forward link
+
+    for (var i = 0; i < nodes.length; i++) {
+      nodes[i].sendsTo.push(this);
+    }
+
+    this.rank = ++lastRank;
+  };
+	EventStream.prototype = new Object();
+
+  fxExport(core,EventStream,'EventStream');
+
+
+  //createNode: Array Node a * ( (Pulse b ->) * (Pulse a) -> Void) -> Node b
+  var createNode = function (nodes, updater) {
+		return new EventStream(nodes,updater);
+	};
+
+  //attachListenerNode: Node * Node -> Voids
+  //flow from node to dependent
+  //note: does not add flow as counting for rank nor updates parent ranks
+  var attachListener = function (node, dependent) {
+    if (!(node instanceof EventStream)) { throw 'attachListenenerNode: expects event as first arg';} //SAFETY
+    if (!(dependent instanceof EventStream)) { throw 'attachListenenerNode: expects event as second arg';} //SAFETY
+
+    node.sendsTo.push(dependent);
+		if(node.rank > dependent.rank) {
+			var lowest = lastRank+1;
+			var q = [dependent];
+			while(q.length) {
+				var cur = q.splice(0,1)[0];
+				cur.rank = ++lastRank;
+				q = q.concat(cur.sendsTo);
+			}
+		}
+  };
+
+  //removeListenerNode: Node * Node -> Boolean
+  //remove flow from node to dependent
+  //note: does not remove flow as counting for rank nor updates parent ranks
+  var removeListener = function (node, dependent)
+  {
+    if (!(node instanceof EventStream)) { throw 'removeListenerNode: expects event as first arg';} //SAFETY
+    if (!(dependent instanceof EventStream)) { throw 'removeListenenerNode: expects event as second arg';} //SAFETY
+
+    var foundSending = false;
+    for (var i = 0; i < node.sendsTo.length && !foundSending; i++) {
+      if (node.sendsTo[i] == dependent) {
+        node.sendsTo.splice(i, 1);
+        foundSending = true;
+      }
+    }
+
+    return foundSending;
+  };
+
+  // An internal_e is a node that simply propagates all pulses it receives.  It's used internally by various
+  // combinators.
+  var internal_e = function(dependsOn) {
+    return createNode(dependsOn || [ ],function(send,pulse) { send(pulse); });
+  }
+
+  var zero_e = function() {
+    return createNode([],function(send,pulse) {
+        throw ('zero_e : received a value; zero_e should not receive a value; the value was ' + pulse.value);
+    });
+  };
+  fxExport(core,zero_e,'zero_e');
+
+  var one_e = function(val) {
+    var sent = false;
+    var evt = createNode([],function(send,pulse) {
+      if (sent) {
+        throw ('one_e : received an extra value');
+      }
+      sent = true;
+      send(pulse);
+    });
+    window.setTimeout(function() { sendEvent(evt,val); },0);
+    return evt;
+  };
+  fxExport(core,one_e,'one_e');
+
+  // a.k.a. mplus; merge_e(e1,e2) == merge_e(e2,e1)
+  var merge_e = function() {
+    if (arguments.length == 0) {
+      return zero_e();
+    }
+    else {
+      var deps = slice(arguments,0);
+      return internal_e(deps);
+    }
+  };
+  fxExport(core,merge_e,'merge_e');
+
+  EventStream.prototype.merge_e = function() {
+    var deps = slice(arguments,0);
+    deps.push(this);
+    return internal_e(deps);
+  };
+  fxMethodSynonyms(EventStream,'merge_e','merge');
+
+  EventStream.prototype.constant_e = function(constantValue) {
+    return createNode([this],function(send,pulse) {
+      pulse.value = constantValue;
+      send(pulse);
+    });
+  };
+  fxMethodSynonyms(EventStream,'constant_e','constant','replaceValue_e','replaceValue');
+
+  var constant_e = function(e) { return e.constant_e(); };
+  fxExport(core,constant_e,'constant_e','replaceValue_e');
+
+	var createTimeSyncNode = function(nodes) {
+		var nqs = map(function(n) {
+				var qpulse = [];
+				return {q:qpulse,v:createNode([n],function(s,p) {qpulse.push(p.value); s(p);},nodes)};
+    },nodes);
+		return createNode(
+      map(function(n) {return n.v;},nqs), function(s,p) {
+        var allfull = fold(function(n,acc) {return n.q.length && acc;},true,nqs);
+        if(allfull) {
+          p.value = map(function(n) {return n.q.shift();},nqs);
+          s(p);
+      }});
+	};
+
+	//This is up here so we can add things to its prototype that are in flapjax.combinators
+  var Behaviour = function (event, init, updater) {
+    if (!(event instanceof EventStream)) {
+      throw 'Behaviour: expected event as second arg';
+    }
+
+    var behave = this;
+    this.last = init;
+
+    //sendEvent to this might impact other nodes that depend on this event
+    //sendBehaviour defaults to this one
+    this.underlyingRaw = event;
+
+    //unexposed, sendEvent to this will only impact dependents of this behaviour
+    this.underlying =
+    createNode(
+      [event],
+	  (updater ?
+	   function (s, p) {behave.last = updater(p.value); p.value = behave.last; s(p);} :
+	   function (s, p) {behave.last = p.value;  s(p);}));
+  };
+	Behaviour.prototype = new Object();
+  fxExport(core,Behaviour,'Behaviour');
+  fxExport(core,Behaviour,'Behavior');
+
+  var receiver_e = function() {
+    var evt = internal_e();
+    evt.sendEvent = function(value) {
+      propagatePulse(new Pulse(nextStamp(), value),evt);
+    };
+    return evt;
+  };
+  fxExport(core,receiver_e,'receiver_e');
+
+  //note that this creates a new timestamp and new event queue
+  var sendEvent = function (node, value) {
+    if (!(node instanceof EventStream)) { throw 'sendEvent: expected Event as first arg'; } //SAFETY
+
+    propagatePulse(new Pulse(nextStamp(), value),node);
+  };
+  fxExport(core,sendEvent,'sendEvent');
+  synonyms['sendEvent'] = true; // Hack: sendEvent simply should not be exported; calling it a synonym deprecates it.
+
+  // bind_e :: EventStream a * (a -> EventStream b) -> EventStream b
+  EventStream.prototype.bind_e = function(k) {
+    /* m.sendsTo result_e
+     * result_e.sendsTo prev_e
+     * prev_e.sendsTo return_e
+     */
+    var m = this;
+    var prev_e = false;
+
+    var out_e = createNode([],function(send,pulse) { send(pulse); });
+    out_e.name = "bind out_e";
+
+    var in_e = createNode([m], function (send,pulse) {
+      if (prev_e) {
+        removeListener(prev_e,out_e);
+      }
+      prev_e = k(pulse.value);
+      if (prev_e instanceof EventStream) {
+        attachListener(prev_e,out_e);
+      }
+      else {
+        throw "bind_e : expected EventStream";
+      }
+    });
+    in_e.name = "bind in_e";
+
+    return out_e;
+  };
+
+  /* Could be written as:
+   *
+   * e.bind_e(function(v) { return one_e(f(v)); })
+   */
+  EventStream.prototype.lift_e = function(f) {
+    if (!(f instanceof Function)) {
+      throw ('lift_e : expected a function as the first argument; received ' + f);
+    };
+
+    return createNode([this],function(send,pulse) {
+      pulse.value = f(pulse.value);
+      send(pulse);
+    });
+  };
+  fxMethodSynonyms(EventStream,'lift_e',
+    'transform_e','map_e','apply_e','lift','transform','map','apply');
+
+  EventStream.prototype.not_e = function() { return this.lift_e(function(v) { return !v; }); };
+  fxMethodSynonyms(EventStream,'not_e','not');
+
+  var not_e = function(e) { return e.not_e(); };
+  fxExport(core,not_e,'not_e','not');
+
+  EventStream.prototype.filter_e = function(pred) {
+    if (!(pred instanceof Function)) {
+      throw ('filter_e : expected predicate; received ' + pred);
+    };
+
+    // Can be a bind_e
+    return createNode([this],
+      function(send,pulse) {
+        if (pred(pulse.value)) { send(pulse); }
+    });
+  };
+  fxMethodSynonyms(EventStream,'filter_e','filter');
+
+  var filter_e = function(e,p) { return e.filter_e(p); };
+  fxExport(core,filter_e,'filter_e');
+
+  // Fires just once.
+  EventStream.prototype.once_e = function() {
+    var done = false;
+    // Alternately: this.collect_e(0,\n v -> (n+1,v)).filter_e(\(n,v) -> n == 1).lift_e(fst)
+    return createNode([this],function(send,pulse) {
+      if (!done) { done = true; send(pulse); }
+    });
+  };
+  fxMethodSynonyms(EventStream,'once_e','once');
+
+  var once_e = function(e) { return e.once_e(); };
+  fxExport(core,once_e,'once_e');
+
+  EventStream.prototype.skipFirst_e = function() {
+    var skipped = false;
+    return createNode([this],function(send,pulse) {
+      if (skipped)
+        { send(pulse); }
+      else
+        { skipped = true; }
+    });
+  };
+  fxMethodSynonyms(EventStream,'skipFirst_e','skipFirst');
+
+  var skipFirst_e = function(e) { return e.skipFirst_e(); };
+  fxExport(core,skipFirst_e,'skipFirst_e');
+
+  EventStream.prototype.collect_e = function(init,fold) {
+    var acc = init;
+    return this.lift_e(
+      function (n) {
+        var next = fold(n, acc);
+        acc = next;
+        return next;
+      });
+  };
+  fxMethodSynonyms(EventStream,'collect_e','collect','transformWithMemory');
+
+  var collect_e = function(e,i,f) { return e.collect_e(i,f); };
+  fxExport(core,collect_e,'collect_e');
+
+  // a.k.a. join
+  EventStream.prototype.switch_e = function() {
+    return this.bind_e(function(v) { return v; });
+  };
+  fxMethodSynonyms(EventStream,'switch_e','forwardLatest');
+
+  var switch_e = function(e) { return e.switch_e(); };
+  fxExport(core,switch_e,'switch_e');
+
+  EventStream.prototype.if_e = function(thenE,elseE) {
+    var testStamp = -1;
+    var testValue = false;
+
+    createNode([this],function(_,pulse) { testStamp = pulse.stamp; testValue = pulse.value; });
+
+    return merge_e(
+      createNode([thenE],function(send,pulse) { if (testValue && (testStamp == pulse.stamp)) { send(pulse); } }),
+      createNode([elseE],function(send,pulse) { if (!testValue && (testStamp == pulse.stamp)) { send(pulse); } }));
+  };
+  fxMethodSynonyms(EventStream,'if_e','choose_e','choose');
+
+  var if_e = function(test,thenE,elseE) {
+    if (test instanceof EventStream)
+      { return test.if_e(thenE,elseE); }
+    else
+      { return test ? thenE : elseE; }
+  };
+  fxExport(core,if_e,'if_e','choose_e');
+
+  var cond_e = function (/*. predValArrays */) {
+    var predValArrays = slice(arguments, 0);
+    var acc = zero_e();
+    for (var i = predValArrays.length - 1; i > -1; i--) {
+      acc = if_e(predValArrays[i][0],predValArrays[i][1], acc);
+    }
+    return acc;
+  };
+  fxExport(core,cond_e,'cond_e');
+
+  var and_e = function (/* . nodes */) {
+    var nodes = slice(arguments, 0);
+
+    var acc = (nodes.length > 0)?
+    nodes[nodes.length - 1] : one_e(true);
+
+    for (var i = nodes.length - 2; i > -1; i--) {
+      acc = if_e(
+        nodes[i],
+        acc,
+        nodes[i].constant_e(false));
+    }
+    return acc;
+  };
+  fxExport(core,and_e,'and_e');
+
+  EventStream.prototype.and_e = function( /* others */ ) {
+    var deps = [this].concat(slice(arguments,0));
+    return and_e.apply(this,deps);
+  };
+  fxMethodSynonyms(EventStream,'and_e','and');
+
+  var or_e = function () {
+    var nodes = slice(arguments, 0);
+    var acc = (nodes.length > 2)?
+    nodes[nodes.length - 1] : one_e(false);
+    for (var i = nodes.length - 2; i > -1; i--) {
+      acc = if_e(
+        nodes[i],
+        nodes[i],
+        acc);
+    }
+    return acc;
+  };
+  fxExport(core,or_e,'or_e');
+
+  EventStream.prototype.or_e = function(/*others*/) {
+    var deps = [this].concat(slice(arguments,0));
+    return or_e.apply(this,deps);
+  };
+  fxMethodSynonyms(EventStream,'or_e','or');
+
+  var delayStatic_e = function (event, time) {
+
+    var resE = internal_e();
+
+    createNode(
+      [event],
+      function (s, p) {
+        setTimeout(
+          function () { sendEvent(resE, p.value);},
+      time ); });
+
+    return resE;
+  };
+
+  //delay_e: Event a * [Behaviour] Number ->  Event a
+  EventStream.prototype.delay_e = function (time) {
+    var event = this;
+
+    if (time instanceof Behaviour) {
+
+      var receiverEE = internal_e();
+      var link =
+      {
+        from: event,
+        towards: delayStatic_e(event, valueNow(time))
+      };
+
+			//TODO: Change semantics such that we are always guaranteed to get an event going out?
+      var switcherE =
+      createNode(
+        [changes(time)],
+        function (s, p) {
+          removeListener(link.from, link.towards);
+          link =
+          {
+            from: event,
+            towards: delayStatic_e(event, p.value)
+          };
+          sendEvent(receiverEE, link.towards);
+        });
+
+      var resE = receiverEE.switch_e();
+
+      sendEvent(switcherE, valueNow(time));
+      return resE;
+
+        } else { return delayStatic_e(event, time); }
+  };
+  fxMethodSynonyms(EventStream,'delay_e','delay');
+
+  var delay_e = function(sourceE,interval) {
+    return sourceE.delay_e(interval);
+  };
+  fxExport(core,delay_e,'delay_e');
+
+  //lift_e: ([Event] (. Array a -> b)) . Array [Event] a -> [Event] b
+  var lift_e = function (fn /*, [node0 | val0], ...*/) {
+    //      if (!(fn instanceof Function)) { throw 'lift_e: expected fn as second arg'; } //SAFETY
+
+    var valsOrNodes = slice(arguments, 0);
+    //selectors[i]() returns either the node or real val, optimize real vals
+    var selectors = [];
+    var selectI = 0;
+    var nodes = [];
+    for (var i = 0; i < valsOrNodes.length; i++) {
+      if (valsOrNodes[i] instanceof EventStream) {
+        nodes.push(valsOrNodes[i]);
+        selectors.push(
+          (function(ii) {
+              return function(realArgs) {
+                return realArgs[ii];
+              };
+          })(selectI));
+        selectI++;
+      } else {
+        selectors.push(
+          (function(aa) {
+              return function () {
+                return aa;
+              };
+          })(valsOrNodes[i]));
+      }
+    }
+
+    var context = this;
+		var nofnodes = slice(selectors,1);
+
+    if (nodes.length === 0) {
+      return one_e(fn.apply(context, valsOrNodes));
+    } else if ((nodes.length === 1) && (fn instanceof Function)) {
+      return nodes[0].lift_e(
+        function () {
+          var args = arguments;
+          return fn.apply(
+            context,
+            map(function (s) {return s(args);}, nofnodes));
+        });
+    } else if (nodes.length === 1) {
+      return fn.lift_e(
+        function (v) {
+          var args = arguments;
+          return v.apply(
+            context,
+            map(function (s) {return s(args);}, nofnodes));
+        });
+    } else if (fn instanceof Function) {
+      return createTimeSyncNode(nodes).lift_e(
+        function (arr) {
+          return fn.apply(
+            this,
+            map(function (s) { return s(arr); }, nofnodes));
+        });
+    } else if (fn instanceof EventStream) {
+      return createTimeSyncNode(nodes).lift_e(
+        function (arr) {
+          return arr[0].apply(
+            this,
+            map(function (s) {return s(arr); }, nofnodes));
+        });
+        } else {throw 'unknown lift_e case';}
+  };
+  fxExport(core,lift_e,'lift_e','transform_e','map_e','apply_e');
+
+  EventStream.prototype.snapshot_e = function (valueB) {
+    return createNode(
+      [this],
+      function (s, p) {
+        p.value = valueNow(valueB);
+        s(p);
+      }
+      );
+  };
+  fxMethodSynonyms(EventStream,'snapshot_e','snapshot','takeSnapshot');
+
+  var snapshot_e = function(triggerE,valueB) {
+    return triggerE.snapshot_e(valueB);
+  };
+  fxExport(core,snapshot_e,'snapshot_e');
+
+  EventStream.prototype.filterRepeats_e = function(optStart) {
+    var hadFirst = optStart === undefined ? false : true;
+    var prev = optStart;
+
+    return this.filter_e(function (v) {
+      if (!hadFirst || !(isEqual(prev,v))) {
+        hadFirst = true;
+        prev = v;
+        return true;
+      }
+      else {
+        return false;
+      }
+    });
+  };
+  fxMethodSynonyms(EventStream,'filterRepeats_e','filterRepeats');
+
+  var filterRepeats_e = function(sourceE,optStart) {
+    return sourceE.filterRepeats_e(optStart);
+  };
+  fxExport(core,filterRepeats_e,'filterRepeats_e','filterRepeats');
+
+  //credit Pete Hopkins
+  var calmStatic_e = function (triggerE, time) {
+		var out = internal_e();
+    createNode(
+      [triggerE],
+      function() {
+        var towards = null;
+        return function (s, p) {
+          if (towards !== null) { clearTimeout(towards); }
+          towards = setTimeout( function () { towards = null; sendEvent(out,p.value) }, time );
+        };
+      }());
+		return out;
+  };
+
+  //calm_e: Event a * [Behaviour] Number -> Event a
+  EventStream.prototype.calm_e = function(time) {
+    if (time instanceof Behaviour) {
+			var out = internal_e();
+      createNode(
+        [this],
+        function() {
+          var towards = null;
+          return function (s, p) {
+            if (towards !== null) { clearTimeout(towards); }
+             towards = setTimeout( function () { towards = null; sendEvent(out,p.value);}, valueNow(time));
+          };
+        }());
+			return out;
+    } else {
+      return calmStatic_e(this,time);
+    }
+  };
+  fxMethodSynonyms(EventStream,'calm_e','calm');
+
+  var calm_e = function(sourceE,interval) {
+    return sourceE.calm_e(interval);
+  };
+  fxExport(core,calm_e,'calm_e');
+
+  EventStream.prototype.blind_e = function (time) {
+    return createNode(
+      [this],
+      function () {
+        var intervalFn =
+        time instanceof Behaviour?
+        function () { return valueNow(time); }
+        : function () { return time; };
+        var lastSent = (new Date()).getTime() - intervalFn() - 1;
+        return function (s, p) {
+          var curTime = (new Date()).getTime();
+          if (curTime - lastSent > intervalFn()) {
+            lastSent = curTime;
+            s(p);
+          }
+        };
+      }());
+  };
+  fxMethodSynonyms(EventStream,'blind_e','blind');
+
+  var blind_e = function(sourceE,interval) {
+    return sourceE.blind_e(interval);
+  };
+  fxExport(core,blind_e,'blind_e');
+
+  EventStream.prototype.hold = function(init) {
+    return new Behaviour(this,init);
+  };
+  fxMethodSynonyms(EventStream,'hold','startsWith','toBehavior');
+
+  var hold = function(e,init) {
+    if (!(e instanceof EventStream)) {
+      throw 'hold: expected EventStream; received ' + e;
+    }
+    return e.hold(init);
+  };
+  fxExport(core,hold,'hold');
+
+  Behaviour.prototype.valueNow = function() {
+    return this.last;
+  };
+  var valueNow = function(behavior) { return behavior.valueNow(); };
+  fxExport(core,valueNow,'valueNow');
+
+  Behaviour.prototype.changes = function() {
+    return this.underlying;
+  };
+  fxMethodSynonyms(Behaviour,'changes','toEvent');
+
+   var changes = function (behave) { return behave.changes(); };
+  fxExport(core,changes,'changes');
+
+  Behaviour.prototype.switch_b = function() {
+    var behaviourCreatorsB = this;
+    var init = valueNow(behaviourCreatorsB);
+
+    var prevSourceE = null;
+
+    var receiverE = new internal_e();
+
+		//XXX could result in out-of-order propagation! Fix!
+    var makerE =
+    createNode(
+      [changes(behaviourCreatorsB)],
+      function (_, p) {
+        if (!(p.value instanceof Behaviour)) { throw 'switch_b: expected Behaviour as value of Behaviour of first argument'; } //SAFETY
+        if (prevSourceE != null) {
+          removeListener(prevSourceE, receiverE);
+        }
+
+        prevSourceE = changes(p.value);
+        attachListener(prevSourceE, receiverE);
+
+        sendEvent(receiverE, valueNow(p.value));
+      });
+
+    if (init instanceof Behaviour) {
+      sendEvent(makerE, init);
+    }
+
+    return hold(
+      receiverE,
+      init instanceof Behaviour? valueNow(init) : init);
+  };
+  fxMethodSynonyms(Behaviour,'switch_b','forwardLatest');
+
+  var switch_b = function (b) { return b.switch_b(); };
+  fxExport(core,switch_b,'switch_b');
+
+  //TODO test, signature
+  var timer_b = function(interval) {
+    return hold(timer_e(interval), (new Date()).getTime());
+  };
+  fxExport(dom,timer_b,'timer_b','asTimer_b');
+
+  //TODO test, signature
+  var delayStatic_b = function (triggerB, time, init) {
+    return hold(delayStatic_e(changes(triggerB), time), init);
+  };
+
+  //TODO test, signature
+  Behaviour.prototype.delay_b = function (time, init) {
+    var triggerB = this;
+    if (time instanceof Behaviour) {
+      return hold(
+        delay_e(
+          changes(triggerB),
+          time),
+        arguments.length > 3 ? init : valueNow(triggerB));
+    } else {
+      return delayStatic_b(
+        triggerB,
+        time,
+        arguments.length > 3 ? init : valueNow(triggerB));
+    }
+  };
+  fxMethodSynonyms(Behaviour,'delay_b','delay');
+
+  var delay_b = function(srcB, timeB, init) {
+    return srcB.delay_b(timeB,init);
+  };
+  fxExport(core,delay_b,'delay_b');
+
+  //artificially send a pulse to underlying event node of a behaviour
+  //note: in use, might want to use a receiver node as a proxy or an identity map
+  Behaviour.prototype.sendBehaviour = function(val) {
+    sendEvent(this.underlyingRaw,val);
+  };
+  Behaviour.prototype.sendBehavior = Behaviour.prototype.sendBehaviour;
+
+  var sendBehaviour = function (b,v) { b.sendBehaviour(v); };
+  fxExport(core,sendBehaviour,'sendBehaviour');
+  fxExport(core,sendBehaviour,'sendBehavior');
+
+  Behaviour.prototype.if_b = function(trueB,falseB) {
+    var testB = this;
+    //TODO auto conversion for behaviour funcs
+    if (!(trueB instanceof Behaviour)) { trueB = constant_b(trueB); }
+    if (!(falseB instanceof Behaviour)) { falseB = constant_b(falseB); }
+    return lift_b(function(te,t,f) { return te ? t : f; },testB,trueB,falseB);
+  };
+  fxMethodSynonyms(Behaviour,'if_b','choose_b','choose');
+
+  var if_b = function(test,cons,altr) {
+    if (!(test instanceof Behaviour)) { test = constant_b(test); };
+
+    return test.if_b(cons,altr);
+  };
+  fxExport(core,if_b,'if_b','choose_b');
+
+
+  //cond_b: . [Behaviour boolean, Behaviour a] -> Behaviour a
+  var cond_b = function (/* . pairs */ ) {
+    var pairs = slice(arguments, 0);
+	return lift_b.apply({},[function() {
+			for(var i=0;i<pairs.length;i++) {
+				if(arguments[i]) return arguments[pairs.length+i];
+			}
+			return undefined;
+		}].concat(map(function(pair) {return pair[0];},pairs).concat(map(function(pair) {return pair[1];},pairs))));
+  };
+  fxExport(core,cond_b,'cond_b');
+
+  //TODO optionally append to objects
+  //createConstantB: a -> Behaviour a
+  var constant_b = function (val) {
+    return new Behaviour(internal_e(), val);
+  };
+  fxExport(core,constant_b,'constant_b','receiver_b');
+
+	var lift_b = function (fn /* . behaves */) {
+    var args = slice(arguments, 1);
+
+    //dependencies
+    var constituentsE =
+      map(changes,
+      filter(function (v) { return v instanceof Behaviour; },
+        arguments));
+
+    //calculate new vals
+    var getCur = function (v) {
+      return v instanceof Behaviour ? v.last : v;
+    };
+
+    var ctx = this;
+    var getRes = function () {
+      return getCur(fn).apply(ctx, map(getCur, args));
+    };
+
+    if(constituentsE.length == 1) {
+      return new Behaviour(constituentsE[0],getRes(),getRes);
+    }
+
+    //gen/send vals @ appropriate time
+    var prevStamp = -1;
+    var mid = createNode(constituentsE, function (s, p) {
+        if (p.stamp != prevStamp) {
+          prevStamp = p.stamp;
+          s(p);
+    }});
+    return new Behaviour(mid,getRes(),getRes);
+  };
+  fxExport(core,lift_b,'lift_b','transform_b','apply_b');
+
+  Behaviour.prototype.lift_b = function(/* args */) {
+    var args= slice(arguments,0).concat([this]);
+    return lift_b.apply(this,args);
+  };
+  fxMethodSynonyms(Behaviour,'lift_b','transform_b','apply_b','transform',
+    'lift');
+
+  var and_b = function (/* . behaves */) {
+	return lift_b.apply({},[function() {
+			for(var i=0; i<arguments.length; i++) {if(!arguments[i]) return false;}
+			return true;
+	}].concat(slice(arguments,0)));
+  };
+  fxExport(core, and_b, 'and_b');
+
+  Behaviour.prototype.and_b = function() {
+    return and_b([this].concat(arguments));
+  };
+  fxMethodSynonyms(Behaviour,'and_b','and');
+
+  var or_b = function (/* . behaves */ ) {
+	return lift_b.apply({},[function() {
+			for(var i=0; i<arguments.length; i++) {if(arguments[i]) return true;}
+			return false;
+	}].concat(slice(arguments,0)));
+  };
+  fxExport(core,or_b,'or_b');
+
+  Behaviour.prototype.or_b = function () {
+    return or_b([this].concat(arguments));
+  };
+  fxMethodSynonyms(Behaviour, 'or_b', 'or');
+
+  Behaviour.prototype.not_b = function() {
+    return this.lift_b(function(v) { return !v; });
+  };
+  fxMethodSynonyms(Behaviour,'not_b','not');
+
+  var not_b = function(b) { return b.not_b(); };
+  fxExport(core,not_b,'not_b');
+
+  Behaviour.prototype.blind_b = function (intervalB) {
+    return changes(this).blind_e(intervalB).hold(this.valueNow());
+  };
+  fxMethodSynonyms(Behaviour,'blind_b','blind');
+
+  var blind_b = function(srcB,intervalB) {
+    return srcB.blind_b(intervalB);
+  };
+  fxExport(core,blind_b,'blind_b');
+
+  Behaviour.prototype.calm_b = function (intervalB) {
+    return this.changes().calm_e(intervalB).hold(this.valueNow());
+  };
+  fxMethodSynonyms(Behaviour,'calm_b','calm');
+
+  var calm_b = function (srcB,intervalB) {
+    return srcB.calm_b(intervalB);
+  };
+  fxExport(core,calm_b,'calm_b');
+
+  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // DOM Utilities
+
+	//credit Scott Andrew
+	//usage: addEvent(myDomObj, "mouseover", event->void )
+	//warning: do not use 'this' as meaning depends on browser (myDomObj vs window)
+	//addEvent: Dom * String DomEventEnum * (DomEvent -> a) -> Void
+	var addEvent = function (obj, evType, fn) {
+		//TODO encode mouseleave evt, formalize new evt interface
+
+		if (obj.addEventListener) {
+      obj.addEventListener(evType, fn, false); //TODO true fails on Opera
+      return true;
+		} else if (obj.attachEvent) {
+      //some reason original had code bloat here
+      return obj.attachEvent("on"+evType, fn);
+		} else {
+      return false;
+		}
+	};
+  fxExport(domMisc,addEvent,'addEvent');
+
+  // The Flapjax library does not use this function
+	//credit Dustin Diaz
+	//note: node/tag optional
+	//getElementsByClass: Regexp CSSSelector * Dom * String DomNodeEnum -> Array Dom
+	var getElementsByClass = function (searchClass, node, tag) {
+		var classElements = [];
+		if ( (node === null) || (node === undefined) ) { node = document; }
+		if ( (tag === null) || (tag === undefined) ) { tag = '*'; }
+		var els = node.getElementsByTagName(tag);
+		var elsLen = els.length;
+		var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
+		for (var i = 0, j = 0; i < elsLen; i++) {
+      if ( pattern.test(els[i].className) ) {
+        classElements.push(els[i]);
+      }
+		}
+		return classElements;
+	};
+  fxExport(domMisc,getElementsByClass,'getElementsByClass','$$');
+
+	//assumes IDs already preserved
+	//swapDom: (Dom a U String) [* (Dom b U String)] -> Dom a
+	var swapDom = function(replaceMe, withMe) {
+		if ((replaceMe === null) || (replaceMe === undefined)) { throw ('swapDom: expected dom node or id, received: ' + replaceMe); } //SAFETY
+
+		var replaceMeD = getObj(replaceMe);
+		if (!(replaceMeD.nodeType > 0)) { throw ('swapDom expected a Dom node as first arg, received ' + replaceMeD); } //SAFETY
+
+		if (withMe) {
+      var withMeD = getObj(withMe);
+      if (!(withMeD.nodeType > 0)) { throw 'swapDom: can only swap with a DOM object'; } //SAFETY
+      try {
+        if(withMeD != replaceMeD) replaceMeD.parentNode.replaceChild(withMeD, replaceMeD);
+      } catch (e) {
+        throw('swapDom error in replace call: withMeD: ' + withMeD + ', replaceMe Parent: ' + replaceMeD + ', ' + e + ', parent: ' + replaceMeD.parentNode);
+      }
+		} else {
+      replaceMeD.parentNode.removeChild(replaceMeD); //TODO isolate child and set innerHTML to "" to avoid psuedo-leaks?
+		}
+		return replaceMeD;
+	};
+  fxExport(domMisc,swapDom,'swapDom');
+
+	//getObj: String U Dom -> Dom
+	//throws
+	//  'getObj: expects a Dom obj or Dom id as first arg'
+	//  'getObj: flapjax: cannot access object'
+	//  'getObj: no obj to get
+	//also known as '$'
+	//TODO Maybe alternative?
+	var getObj = function (name) {
+		if (typeof(name) == 'object') { return name; }
+		else if ((typeof(name) == 'null') || (typeof(name) == 'undefined')) {
+      throw 'getObj: expects a Dom obj or Dom id as first arg';
+		} else {
+
+      var res =
+			document.getElementById ? document.getElementById(name) :
+			document.all ? document.all[name] :
+			document.layers ? document.layers[name] :
+			(function(){ throw 'getObj: flapjax: cannot access object';})();
+      if ((res === null) || (res === undefined)) {
+        throw ('getObj: no obj to get: ' + name);
+      }
+      return res;
+		}
+	};
+  fxExport(domMisc,getObj,'getObj');
+  fxExport(domMisc,getObj,'$');
+
+  //helper to reduce obj look ups
+  //getDynObj: domNode . Array (id) -> domObj
+  //obj * [] ->  obj
+  //obj * ['position'] ->  obj
+  //obj * ['style', 'color'] ->  obj.style
+  var getMostDom = function (domObj, indices) {
+    var acc = getObj(domObj);
+    if ( (indices === null) || (indices === undefined) || (indices.length < 1)) {
+      return acc;
+    } else {
+      for (var i = 0; i < indices.length - 1; i++) {
+        acc = acc[indices[i]];
+      }
+      return acc;
+    }
+  };
+
+  var getDomVal = function (domObj, indices) {
+    var val = getMostDom(domObj, indices);
+    if (indices && indices.length > 0) {
+      val = val[indices[indices.length - 1]];
+    }
+    return val;
+  };
+
+  //TODO: manual timer management stinks.
+  // TODO: Name turn off or somethin
+  var ___timerID = 0;
+  var __getTimerId = function () { return ++___timerID; };
+  var timerDisablers = [];
+
+  var disableTimerNode = function (node) { timerDisablers[node.__timerId](); };
+
+  var disableTimer = function (v) {
+    if (v instanceof Behaviour) {
+      disableTimerNode(v.underlyingRaw);
+    } else if (v instanceof EventStream) {
+      disableTimerNode(v);
+    }
+  };
+  fxExport(dom,disableTimer,'disableTimer');
+
+  var createTimerNodeStatic = function (interval) {
+    var node = internal_e();
+    node.__timerId = __getTimerId();
+    var fn = function () { sendEvent(node, (new Date()).getTime());};
+    var timer = setInterval(fn, interval);
+    timerDisablers[node.__timerId] = function () {clearInterval(timer); };
+    return node;
+  };
+
+  var timer_e = function (interval) {
+    if (interval instanceof Behaviour) {
+      var receiverE = internal_e();
+
+      //the return
+      var res = receiverE.switch_e();
+
+      //keep track of previous timer to disable it
+      var prevE = createTimerNodeStatic(valueNow(interval));
+
+      //init
+      sendEvent(receiverE, prevE);
+
+      //interval changes: collect old timer
+      createNode(
+        [changes(interval)],
+        function (_, p) {
+          disableTimerNode(prevE);
+          prevE = createTimerNodeStatic(p.value);
+          sendEvent(receiverE, prevE);
+        });
+
+      res.__timerId = __getTimerId();
+      timerDisablers[res.__timerId] = function () {
+        disableTimerNode[prevE]();
+        return;
+      };
+
+      return res;
+    } else {
+      return createTimerNodeStatic(interval);
+    }
+  };
+  fxExport(dom,timer_e,'timer_e','asTimer_e');
+
+	d.TagB = function(tagName,args) {
+		this.resE = internal_e();
+		this.currentTag = document.createElement(tagName);
+		this.extractParameters(args);
+		this.insertChildrenNodes();
+		this.styleHooks = [];
+		this.styleChangedE = internal_e();
+		var ctx = this;
+    this.styleChangedE.lift_e(function(_) {
+				var oldTag = ctx.currentTag;
+				ctx.currentTag = document.createElement(tagName);
+        while (oldTag.firstChild) {
+          ctx.currentTag.appendChild(oldTag.removeChild(oldTag.firstChild));
+				}
+				while(ctx.styleHooks.length) removeListener(ctx.styleHooks.pop(),ctx.styleChangedE);
+        ctx.enstyle(ctx.currentTag,ctx.attribs);
+        sendEvent(ctx.resE, ctx.currentTag);
+		});
+		this.enstyle(this.currentTag,this.attribs);
+    this.resB = hold(this.resE, this.currentTag);
+	};
+	d.TagB.prototype = {
+    // Array [[Behaviour] Object *] [[Behaviour] Array] [Behaviour] Dom U String U undefined
+    //   --> {attribs: Array [Behaviour] Object, arrs: Array [Behaviour] Array [Behaviour] Dom }
+    // split an arguments array into:
+    //   1. arrs: (coalesced, and possibly time varying) arrays of dom objects
+    //   2. attribs: attribute objects
+		extractParameters: function(args) {
+			this.arrs = [];
+			var attribs = [];
+
+			var curarr = [];
+			this.arrs.push(curarr);
+			for (var i = 0; i < args.length; i++) {
+				if (args[i] instanceof Behaviour) {
+					var vn = valueNow(args[i]);
+					if (vn instanceof Array) {
+						this.arrs.push(args[i]);
+						curarr = [];
+						this.arrs.push(curarr);
+					} else {
+						if ( ((typeof(vn) == 'object') && (vn.nodeType == 1)) ||
+              (typeof(vn) == 'string') || (vn == undefined)) {
+            curarr.push(args[i]);
+              } else if (typeof(vn) == 'object') {
+                attribs.push(args[i]);
+              }
+              else { throw ('createParameterizedTagB: unknown behaviour argument argument ' + i); } //SAFETY
+					}
+				} else {
+					if (args[i] instanceof Array) {
+						var arr = args[i];
+						for (var j = 0; j < arr.length; j++) { curarr.push(arr[j]); }
+					} else {
+						var vn = args[i];
+						if ( ((typeof(vn) == 'object') && (vn.nodeType == 1)) ||
+              (typeof(vn) == 'string') || (vn == undefined)) {
+						curarr.push(args[i]);
+              } else if (typeof(vn) == 'object') {
+                attribs.push(args[i]);
+              }
+					}
+				}
+			};
+			if(attribs.length > 1) throw ('createParameterizedTagB ' + tagName + ': more than one attribute (' + attribs.length + ')');
+			this.attribs = attribs.length > 0 ? attribs[0] : {};
+		},
+		insertChildrenNodes: function() {
+			var ctx = this;
+
+			function quickNode(e) {
+        if ((typeof(e) == 'object') && (e.nodeType))
+					return e;
+        else if ( e == undefined )
+					return document.createTextNode('');
+        else
+          return document.createTextNode(e);
+      }
+
+			function unBehaviourize(arr) {
+				return map(function(n) {return (n instanceof Behaviour) ? valueNow(n) : n;},arr)
+			}
+
+			var lnodes = map(function() {return [];},this.arrs);
+			var arrLastVals = map(unBehaviourize,unBehaviourize(this.arrs));
+
+			var arrChangesE = internal_e();
+			var nodeChangesE = internal_e();
+
+			function attachNodes(i,arr) {
+				for(var j=0;j<arr.length;j++) {
+					var cnode = arr[j];
+					if(cnode instanceof Behaviour) {
+						var newnode = (function(jj) {
+                return changes(cnode).lift_e(function(n) {return {index:i,jdex:jj,val:n};});
+						})(j);
+						lnodes[i].push(newnode);
+						attachListener(newnode,nodeChangesE);
+						cnode = valueNow(cnode);
+					}
+				}
+			}
+
+			var childChangesE = merge_e(
+				// Behaviour arrays change
+				arrChangesE.lift_e(function(ai) {
+            var i = ai.index;
+            var newarr = ai.val;
+            while(lnodes[i].length) {
+              var ln = lnodes[i].pop();
+              removeListener(ln,nodeChangesE);
+            }
+            var newvals = map(function(n) {return quickNode(n);},unBehaviourize(newarr));
+            for(var j=0;j<arrLastVals[i].length;j++)
+				try {
+					ctx.currentTag.removeChild(arrLastVals[i][j]);
+				}
+				catch(e) {}
+            if(newvals.length) {
+              var nextNode = null;
+              for(ii = i+1; ii < arrLastVals.length && !(arrLastVals[ii].length) ; ii++);
+              if(ii < arrLastVals.length) nextNode = arrLastVals[ii][0];
+              for(var j=0; j<newvals.length; j++) ctx.currentTag.insertBefore(newvals[j],nextNode);
+            }
+            arrLastVals[i] = newvals;
+            attachNodes(i,newarr);
+            return ctx.currentTag;
+				}),
+				// Behaviour nodes change
+				nodeChangesE.lift_e(function(ni) {
+            var i = ni.index;
+            var j = ni.jdex;
+            var newnode = quickNode(ni.val);
+            swapDom(arrLastVals[i][j],newnode);
+            arrLastVals[i][j] = newnode;
+            return ctx.currentTag;
+				}));
+			childChangesE.lift_e(function(cc) {sendEvent(ctx.resE,cc);});
+
+			for(var i=0; i<this.arrs.length;i++) {
+				for(var j=0; j<arrLastVals[i].length; j++) {
+					arrLastVals[i][j] = quickNode(arrLastVals[i][j]);
+					this.currentTag.appendChild(arrLastVals[i][j]);
+				}
+				if(this.arrs[i] instanceof Behaviour) {
+					attachNodes(i,valueNow(this.arrs[i]));
+					var newnode = (function(ii) {return changes(ctx.arrs[ii]).lift_e(function(na) {return {index:ii,val:na};});})(i);
+					attachListener(newnode,arrChangesE);
+				}
+				else {
+					attachNodes(i,this.arrs[i]);
+				}
+			}
+		},
+		enstyle: function(obj,vals) {
+			//build & record hook if dynamic collection
+			if (vals instanceof Behaviour) {
+				if (!(typeof(valueNow(vals)) == 'object')) { throw 'enstyle: expected object literal as behaviour value'; } //SAFETY
+				this.styleHooks.push(changes(vals));
+				attachListener(changes(vals),this.styleChangedE);
+			}
+			var valsV = vals instanceof Behaviour ? valueNow(vals) : vals;
+			if (typeof(valsV) == 'object') {
+				for (var i in valsV) {
+					if (!(Object.prototype) || !(Object.prototype[i])) {
+						this.enstyleProperty(obj,valsV, i);
+					}
+				}
+			}
+			else { throw 'enstyle: expected object literals'; } //SAFETY
+		},
+    enstyleProperty: function (obj, vals, i) {
+      if (vals[i] instanceof Behaviour) {
+        if (typeof(valueNow(vals[i])) == 'object') {
+          this.enstyle(obj[i], vals[i]);
+        }
+				else {
+          obj[i] = valueNow(vals[i]);
+					changes(vals[i]).lift_e(function(v) {obj[i] = v;});
+				}
+			}
+			else {
+        if (typeof(vals[i]) == 'object') {
+          this.enstyle(obj[i], vals[i]);
+        } else {
+          obj[i] = vals[i];
+        }
+      }
+    }
+	};
+
+	d.createParameterizedTagB = function(tagName) {
+		return new d.TagB(tagName,slice(arguments,1)).resB;
+	};
+
+  d.enstyleStaticProperty = function (obj, props, index) {
+    if (typeof(props[index]) == 'object') {
+      for (var i in props[index]) {
+        if (!(Object.prototype) || !(Object.prototype[i])) {
+          d.enstyleStaticProperty(obj[index], props[index], i);
+        }
+      }
+    } else {
+      obj[index] = props[index];
+			if (index == 'checked')	obj['defaultChecked'] = props[index]; /* TODO: this should maybe be elsewhere? */
+			if (index == 'selected') obj['defaultSelected'] = props[index]; /* TODO: this should maybe be elsewhere? */
+    }
+  };
+
+  d.staticTagMaker = function (tagName) {
+
+    return function () {
+
+      var tagD = document.createElement(tagName);
+      if (!(tagD.nodeType > 0)) { throw (tagName + ': invalid tag name'); } //SAFETY
+
+      //partition input
+
+      //          if (arguments[1] === null || arguments[1] === undefined) { arguments[1] = {}; }
+      var attribs = [];
+      for (var i = 0; i < arguments.length; i++) {
+        if (arguments[i] instanceof Array) {
+          for (var j = 0; j < arguments[i].length; j++) {
+            if (arguments[i][j]) {
+              tagD.appendChild(
+                (typeof(arguments[i][j]) == 'object' &&
+                  arguments[i][j].nodeType > 0)?
+                arguments[i][j] :
+                document.createTextNode(arguments[i][j]));
+            }
+          }
+        } else if (!arguments[i]) {
+          //ignore
+        } else if ((typeof(arguments[i]) == 'object') &&
+          (arguments[i].nodeType > 0)) {
+        tagD.appendChild(arguments[i]);
+          } else if (typeof(arguments[i]) == 'object') {
+            attribs.push(arguments[i]);
+          } else {
+            tagD.appendChild(document.createTextNode(arguments[i]));
+          }
+      }
+
+      if (attribs.length == 1) {
+        for (var k in attribs[0]) {
+          if (!(Object.prototype) || !(Object.prototype[k])) {
+            d.enstyleStaticProperty(tagD, attribs[0], k);
+          }
+        }
+      } else if (attribs.length >  0) {
+        throw 'static enstyle: expected object literals'; //SAFETY
+      } /* else {
+      alert('no attribs on: ' + tagName);
+      } */
+
+
+      return tagD;
+    };
+  };
+
+
+
+  var generatedTags =
+  [ "a", "b", "blockquote", "br", "button", "canvas", "div", "fieldset",
+  "form", "font", "h1", "h2", "h3", "h4", "hr", "img", "iframe", "input",
+  "label", "legend", "li", "ol", "optgroup", "option",
+  "p", "pre", "select", "span", "strong", "table", "tbody",
+  "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul" ];
+
+  map(
+    function (tagName) {
+
+      var upper = tagName.toUpperCase();
+
+      //d.<TAG>B
+      d[upper + 'B'] = function () {
+        return d.createParameterizedTagB.apply(this, [tagName].concat(slice(arguments,0)));
+      };
+			annotate(d[upper+'B'],[upper+'B']);
+
+      //d.<TAG>
+      d[upper] = d.staticTagMaker(tagName);  //faster, simple
+      /*              function () {
+      // 8/16/06 - leo - arguments bug
+      // more forgiving: allows reactive children (just doesn't propagate them)
+      var args = [b.maybeEmpty].concat(slice(arguments, 0));
+      return valueNow(d[upper + 'B'].apply(this, args));
+      }; */
+			annotate(d[upper],[upper]);
+    },
+    generatedTags);
+
+  //TEXTB: Behaviour a -> Behaviour Dom TextNode
+  d.TEXTB = function (strB) {
+    //      if (!(strB instanceof Behaviour || typeof(strB) == 'string')) { throw 'TEXTB: expected Behaviour as second arg'; } //SAFETY
+    if (!(strB instanceof Behaviour)) { strB = constant_b(strB); }
+
+    return hold(
+      changes(strB).lift_e(
+        function (txt) { return document.createTextNode(txt); }),
+      document.createTextNode(valueNow(strB)));
+  };
+	annotate(d.TEXTB,['TEXTB']);
+
+  var TEXT = function (str) {
+    return document.createTextNode(str);
+  };
+  fxExport(dom,TEXT,'TEXT');
+
+  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Reactive DOM
+
+  //tagRec: Array (EventName a) *
+  //      ( .Array (Event a) * Array (Event a) -> Behaviour Dom) -> Behaviour Dom
+  var tagRec = function (eventNames, maker) {
+    if (!(eventNames instanceof Array)) { throw 'tagRec: expected array of event names as first arg'; } //SAFETY
+    if (!(maker instanceof Function)) { throw 'tagRec: expected function as second arg'; } //SAFETY
+
+    var numEvents = eventNames.length;
+    var internals = [ ];
+    var switches = [ ];
+    for (var i = 0; i < numEvents; i++) {
+      internals[i] = internal_e();
+      switches[i] = internals[i].switch_e();
+    };
+
+    var domB = maker.apply(this,switches);
+
+    var prevValue;
+
+    var interceptE = createNode([domB.changes()],function (_,p) {
+      if (isEqual(p.value,prevValue)) { return; }
+
+      prevValue = p.value;
+      for (var i = 0; i < numEvents; i++) {
+        sendEvent(internals[i],extractEvent_e(prevValue,eventNames[i]));
+      };
+    });
+
+    sendEvent(interceptE,domB.valueNow());
+
+    return domB;
+  };
+  fxExport(dom,tagRec,'tagRec','nodeWithOwnEvents');
+
+  //extractEventStatic_e: Dom * String -> Event
+  var extractEventStatic_e = function (domObj, eventName) {
+    if (!eventName) { throw 'extractEvent_e : no event name specified'; }
+    if (!domObj) { throw 'extractEvent_e : no DOM element specified'; }
+
+    domObj = getObj(domObj);
+
+    var primEventE = internal_e();
+    addEvent(domObj,eventName,function(evt) {
+       sendEvent(primEventE, evt || window.event);
+       // Important for IE; false would prevent things like a checkbox actually
+       // checking.
+       return true;
+    });
+
+    return primEventE;
+  };
+
+  //extractEvent_e: [Behaviour] Dom * String -> Event
+  var extractEvent_e = function (domB, eventName) {
+    if (!(domB instanceof Behaviour)) {
+      return extractEventStatic_e(domB,eventName);
+    }
+    else {
+      var domE = domB.changes();
+
+      var eventEE = domE.lift_e(function(dom) {
+        return extractEventStatic_e(dom,eventName);
+      });
+
+      var resultE = eventEE.switch_e();
+
+      sendEvent(domE,domB.valueNow());
+
+      return resultE;
+    };
+  };
+  fxExport(dom,extractEvent_e,'extractEvent_e','$EVT');
+
+  //extractEvents_e:
+  //      [Behaviour] Dom
+  //      . Array String
+  //      -> Event
+  // ex: extractEvents_e(m, 'body', 'mouseover', 'mouseout')
+  d.extractEvents_e = function (domObj /* . eventNames */) {
+    var eventNames = slice(arguments, 1);
+
+    var events = map(
+      function (eventName) {
+        return extractEvent_e(domObj, eventName);
+      },
+      eventNames.length === 0 ? [] : eventNames);
+
+    return merge_e.apply(this, events);
+  };
+  annotate(d.extractEvents_e,['extractEvents_e'],0,[Behaviour.prototype],['extractEvents_e']);
+
+  //value of dom form object during trigger
+  d.extractValueOnEvent_e = function (triggerE, domObj) {
+    if (!(triggerE instanceof EventStream)) { throw 'extractValueOnEvent_e: expected Event as first arg'; } //SAFETY
+
+    return changes(d.extractValueOnEvent_b.apply(this, arguments));
+
+  };
+  annotate(d.extractValueOnEvent_e,['extractValueOnEvent_e']);
+
+  //extractDomFieldOnEvent_e: Event * Dom U String . Array String -> Event a
+  d.extractDomFieldOnEvent_e = function (triggerE, domObj /* . indices */) {
+    if (!(triggerE instanceof EventStream)) { throw 'extractDomFieldOnEvent_e: expected Event as first arg'; } //SAFETY
+    var indices = slice(arguments, 2);
+    var res =
+    triggerE.lift_e(
+      function () { return getDomVal(domObj, indices); });
+    return res;
+  };
+
+  d.extractValue_e = function (domObj) {
+    return changes(d.extractValue_b.apply(this, arguments));
+  };
+  annotate(d.extractValue_e,['extractValue_e','$E']);
+
+  //extractValueOnEvent_b: Event * DOM -> Behaviour
+  // value of a dom form object, polled during trigger
+  d.extractValueOnEvent_b = function (triggerE, domObj) {
+    return d.extractValueStatic_b(domObj, triggerE);
+  };
+  annotate(d.extractValueOnEvent_b,['extractValueOnEvent_b']);
+
+  //extractValueStatic_b: DOM [ * Event ] -> Behaviour a
+  //If no trigger for extraction is specified, guess one
+  d.extractValueStatic_b = function (domObj, triggerE) {
+
+    var objD;
+    try {
+      objD = getObj(domObj);
+	    //This is for IE
+	    if(typeof(domObj) == 'string' && objD.id != domObj) {
+	    	throw 'Make a radio group';
+	    }
+    } catch (e) {
+      objD = {type: 'radio-group', name: domObj};
+    }
+
+    var getter; // get value at any current point in time
+
+
+    switch (objD.type)  {
+
+      //TODO: checkbox.value instead of status?
+    case 'checkbox':
+
+      return hold(
+        filterRepeats_e(
+          d.extractDomFieldOnEvent_e(
+            triggerE ? triggerE :
+            d.extractEvents_e(
+              objD,
+              'click', 'keyup', 'change'),
+            objD,
+            'checked'),objD.checked),
+        objD.checked);
+
+      case 'select-one':
+
+        getter = function (_) {
+          return objD.selectedIndex > -1 ?
+          (objD.options[objD.selectedIndex].value ?
+            objD.options[objD.selectedIndex].value :
+            objD.options[objD.selectedIndex].innerText)
+			    : undefined;
+        };
+
+        return hold(
+          filterRepeats_e(
+              (triggerE ? triggerE :
+              d.extractEvents_e(
+                objD,
+                'click', 'keyup', 'change')).lift_e(getter)),getter(),
+          getter());
+
+        case 'select-multiple':
+          //TODO ryan's cfilter adapted for equality check
+
+          getter = function (_) {
+            var res = [];
+            for (var i = 0; i < objD.options.length; i++) {
+              if (objD.options[i].selected) {
+                res.push(objD.options[i].value ? objD.options[i].value : objD.options[i].innerText);
+              }
+            }
+            return res;
+          };
+
+
+          return hold(
+              (triggerE ? triggerE :
+              d.extractEvents_e(
+                objD,
+                'click', 'keyup', 'change')).lift_e(getter),
+            getter());
+
+          case 'text':
+          case 'textarea':
+          case 'hidden':
+          case 'password':
+
+            return hold(
+              filterRepeats_e(
+                d.extractDomFieldOnEvent_e(
+                  triggerE ? triggerE :
+                  d.extractEvents_e(
+                    objD,
+                    'click', 'keyup', 'change'),
+                  objD,
+                  'value'),objD.value),
+              objD.value);
+
+            case 'button': //same as above, but don't filter repeats
+
+              return hold(
+                d.extractDomFieldOnEvent_e(
+                  triggerE ? triggerE :
+                  d.extractEvents_e(
+                    objD,
+                    'click', 'keyup', 'change'),
+                  objD,
+                  'value'),
+                objD.value);
+
+
+              case 'radio':
+              case 'radio-group':
+
+                //TODO returns value of selected button, but if none specified,
+                //      returns 'on', which is ambiguous. could return index,
+                //      but that is probably more annoying
+
+                var radiosAD = filter(
+                  function (elt) {
+                    return (elt.type == 'radio') &&
+                    (elt.getAttribute('name') == objD.name);
+                  },
+                  document.getElementsByTagName('input'));
+
+                getter =
+                objD.type == 'radio' ?
+
+                function (_) {
+                  return objD.checked;
+                } :
+
+                function (_) {
+                  for (var i = 0; i < radiosAD.length; i++) {
+                    if (radiosAD[i].checked) {
+                      return radiosAD[i].value;
+                    }
+                  }
+                  return undefined; //TODO throw exn?
+                };
+
+                var actualTriggerE = triggerE ? triggerE :
+                merge_e.apply(
+                  this,
+                  map(
+                    function (radio) {
+                      return d.extractEvents_e(
+                        radio,
+                    'click', 'keyup', 'change'); },
+                      radiosAD));
+
+                return hold(
+                  filterRepeats_e(
+                      actualTriggerE.lift_e(getter),getter()),
+                  getter());
+
+                default:
+
+                  throw ('extractValueStatic_b: unknown value type "' + objD.type + '"');
+    }
+  };
+
+  d.extractValue_b = function (domObj) {
+    if (domObj instanceof Behaviour) {
+      return lift_b(function (dom) { return d.extractValueStatic_b(dom); },
+                    domObj)
+             .switch_b();
+    } else {
+      return d.extractValueStatic_b(domObj);
+    }
+  };
+	annotate(d.extractValue_b,['extractValue_b','$B'],0,[Behaviour.prototype],['extractValue_b','$B']);
+
+  //into[index] = deepValueNow(from) via descending from object and mutating each field
+  d.deepStaticUpdate = function (into, from, index) {
+    var fV = (from instanceof Behaviour)? valueNow(from) : from;
+    if (typeof(fV) == 'object') {
+      for (var i in fV) {
+        if (!(Object.prototype) || !(Object.prototype[i])) {
+          d.deepStaticUpdate(index? into[index] : into, fV[i], i);
+        }
+      }
+    } else {
+      var old = into[index];
+      into[index] = fV;
+    }
+  };
+
+  //note: no object may be time varying, just the fields
+  //into[index] = from
+  //only updates on changes
+  d.deepDynamicUpdate = function (into, from, index) {
+    var fV = (from instanceof Behaviour)? valueNow(from) : from;
+    if (typeof(fV) == 'object') {
+      if (from instanceof Behaviour) {
+        throw 'deepDynamicUpdate: dynamic collections not supported';
+      }
+      for (var i in fV) {
+        if (!(Object.prototype) || !(Object.prototype[i])) {
+          d.deepDynamicUpdate(index? into[index] : into, fV[i], i);
+        }
+      }
+    } else {
+      if (from instanceof Behaviour) {
+        createNode(
+          [changes(from)],
+          function (s, p) {
+            if (index) {
+              var old = into[index];
+              into[index] = p.value;
+            }
+            else { into = p.value; } //TODO notify topE?
+          });
+      }
+    }
+  };
+
+
+  d.insertValue = function (val, domObj /* . indices */) {
+    var indices = slice(arguments, 2);
+    var parent = getMostDom(domObj, indices);
+    d.deepStaticUpdate(parent, val, indices ? indices[indices.length - 1] : undefined);
+  };
+  annotate(d.insertValue,['insertValue']);
+
+  //TODO convenience method (default to firstChild nodeValue)
+  d.insertValueE = function (triggerE, domObj /* . indices */) {
+    if (!(triggerE instanceof EventStream)) { throw 'insertValueE: expected Event as first arg'; } //SAFETY
+
+    var indices = slice(arguments, 2);
+    var parent = getMostDom(domObj, indices);
+
+      triggerE.lift_e(function (v) {
+        d.deepStaticUpdate(parent, v, indices? indices[indices.length - 1] : undefined);
+      });
+  };
+  annotate(d.insertValueE,['insertValueE'],0,[EventStream.prototype],['insertValueE']);
+
+  //insertValueB: Behaviour * domeNode . Array (id) -> void
+  //TODO notify adapter of initial state change?
+  d.insertValueB = function (triggerB, domObj /* . indices */) {
+
+    var indices = slice(arguments, 2);
+    var parent = getMostDom(domObj, indices);
+
+
+    //NOW
+    d.deepStaticUpdate(parent, triggerB, indices ? indices[indices.length - 1] : undefined);
+
+    //LATER
+    d.deepDynamicUpdate(parent, triggerB, indices? indices[indices.length -1] : undefined);
+
+  };
+  annotate(d.insertValueB,['insertValueB'],0,[Behaviour.prototype],['insertValueB']);
+
+  //TODO copy dom event call backs of original to new? i don't thinks so
+  //  complication though: registration of call backs should be scoped
+  d.insertDomE = function (triggerE, domObj) {
+
+    if (!(triggerE instanceof EventStream)) { throw 'insertDomE: expected Event as first arg'; } //SAFETY
+
+    var objD = getObj(domObj);
+
+    var res = triggerE.lift_e(
+      function (newObj) {
+        //TODO safer check
+        if (!((typeof(newObj) == 'object') && (newObj.nodeType == 1))) {
+          newObj = d.SPAN({}, newObj);
+        }
+        swapDom(objD, newObj);
+        objD = newObj;
+        return newObj; // newObj;
+      });
+
+    return res;
+  };
+  annotate(d.insertDomE,['insertDomE'],0,[EventStream.prototype],['insertDomE']);
+
+  //insertDom: dom
+  //          * dom
+  //          [* (null | undefined | 'over' | 'before' | 'after' | 'leftMost' | 'rightMost' | 'beginning' | 'end']
+  //          -> void
+  // TODO: for consistency, switch replaceWithD, hookD argument order
+  d.insertDom = function (hookD, replaceWithD, optPosition) {
+    switch (optPosition)
+    {
+    case undefined:
+    case null:
+    case 'over':
+      swapDom(hookD,replaceWithD);
+      break;
+    case 'before':
+      hookD.parentNode.insertBefore(replaceWithD, hookD);
+      break;
+    case 'after':
+      if (hookD.nextSibling) {
+        hookD.parentNode.insertBefore(replaceWithD, hookD.nextSibling);
+      } else {
+        hookD.parentNode.appendChild(replaceWithD);
+      }
+      break;
+    case 'leftMost':
+      if (hookD.parentNode.firstChild) {
+        hookD.parentNode.insertBefore(
+          replaceWithD,
+          hookD.parentNode.firstChild);
+                } else { hookD.parentNode.appendChild(replaceWithD); }
+                break;
+              case 'rightMost':
+                hookD.parentNode.appendChild(replaceWithD);
+                break;
+              case 'beginning':
+                if (hookD.firstChild) {
+                  hookD.insertBefore(
+                    replaceWithD,
+                    hookD.firstChild);
+                } else { hookD.appendChild(replaceWithD); }
+                break;
+              case 'end':
+                hookD.appendChild(replaceWithD);
+                break;
+              default:
+                throw ('domInsert: unknown position: ' + optPosition);
+    }
+  };
+
+  //insertDom: dom
+  //          * dom U String domID
+  //          [* (null | undefined | 'over' | 'before' | 'after' | 'leftMost' | 'rightMost' | 'beginning' | 'end']
+  //          -> void
+  d.insertDomClean = function (replaceWithD, hook, optPosition) {
+    //TODO span of textnode instead of textnode?
+    d.insertDom(
+	    getObj(hook),
+	    ((typeof(replaceWithD) == 'object') && (replaceWithD.nodeType > 0)) ? replaceWithD :
+      document.createTextNode(replaceWithD),
+	    optPosition);
+  };
+  annotate(d.insertDomClean,['insertDom']);
+
+  //TODO test
+  //insertDomB:
+  //      [Behaviour] String U Dom
+  //      [* ( id U null U undefined )
+  //          [* ('before' U 'after' U 'leftMost' U 'rightMost' U 'over' U 'beginning' U 'end')]]
+  //      -> Behaviour a
+  //if optID not specified, id must be set in init val of trigger
+  //if position is not specified, default to 'over'
+  //performs initial swap onload
+  d.insertDomB = function (initTriggerB, optID, optPosition) {
+
+    if (!(initTriggerB instanceof Behaviour)) {
+      initTriggerB = constant_b(initTriggerB);
+    }
+
+    var triggerB =
+    lift_b(
+      function (d) {
+        if ((typeof(d) == 'object') && (d.nodeType >  0)) {
+          return d;
+        } else {
+          var res = document.createElement('span'); //TODO createText instead
+          res.appendChild(document.createTextNode(d));
+          return res;
+        }
+      },
+      initTriggerB);
+
+    var initD = valueNow(triggerB);
+    if (!((typeof(initD) == 'object') && (initD.nodeType == 1))) { throw ('insertDomB: initial value conversion failed: ' + initD); } //SAFETY
+
+    d.insertDom(
+      optID === null || optID === undefined ? getObj(initD.getAttribute('id')) : getObj(optID),
+      initD,
+      optPosition);
+
+    var resB = hold(
+      d.insertDomE(
+        changes(triggerB),
+        initD),
+      initD);
+
+    return resB;
+  };
+  annotate(d.insertDomB,['insertDomB'],0,[Behaviour.prototype],['insertDomB']);
+
+
+  d.extractId_b = function (id, start)
+  {
+    return hold(
+      createNode( start instanceof Behaviour? [changes(start)] :
+        [],
+        function (s, p) {
+          p.value = getObj(id);
+          s(p);
+        }),
+      getObj(id));
+  };
+  annotate(d.insertDomB,['extractId_b'],1,[Behaviour.prototype],['extractId_b']);
+
+  var mouse_e = function(elem) {
+    return extractEvent_e(elem,'mousemove')
+    .lift_e(function(evt) {
+        if (evt.pageX | evt.pageY) {
+          return { left: evt.pageX, top: evt.pageY };
+        }
+        else if (evt.clientX || evt.clientY) {
+          return { left : evt.clientX + document.body.scrollLeft,
+                   top: evt.clientY + document.body.scrollTop };
+        }
+        else {
+          return { left: 0, top: 0 };
+        }
+    });
+  };
+
+  var mouse_b = function(elem) {
+    return mouse_e(elem).hold({ left: 0, top: 0 });
+  }
+  fxExport(dom,mouse_b,'mouse_b');
+
+  var mouseLeft_b = function(elem) {
+    return lift_b(function(v) { return v.left; },mouse_b(elem));
+  };
+  fxExport(dom,mouseLeft_b,'mouseLeft_b');
+
+  var mouseTop_b = function(elem) {
+    return mouse_b(elem).lift_b(function(v) { return v.top; });
+  };
+
+  fxExport(dom,mouseTop_b,'mouseTop_b');
+
+  var clicks_e = function(elem) {
+    return extractEvent_e(elem,'click');
+  };
+  fxExport(dom,clicks_e,'clicks_e');
+
+  //////////////////////////////////////////////////////////////////////////////
+  // Combinators for web services
+
+
+	//credit Matt White
+	var getURLParam = function (param) {
+    var lparam = param.toLowerCase();
+    var aReturn = [];
+    var strHref = window.location.href;
+    var endstr = (strHref.indexOf('#') > -1) ? strHref.indexOf('#') : strHref.length;
+    if ( strHref.indexOf("?") > -1 ) {
+      var strQueryString = strHref.substring(strHref.indexOf("?")+1,endstr);
+      map(function(qp) {
+          var eq = qp.indexOf('=');
+          var qname = qp.substr(0,eq+1).toLowerCase();
+          if(qname == lparam+"=") aReturn.push(decodeURIComponent(qp.substr(eq+1)));
+      },strQueryString.split("&"));
+    }
+    if (aReturn.length == 0) return undefined;
+    else if(aReturn.length == 1) return aReturn[0];
+    else return aReturn;
+	};
+  fxExport(domMisc,getURLParam,'getURLParam','$URL');
+
+	//credit Quirksmode
+	//readCookie: String -> String U Undefined
+	var readCookie = function (name) {
+    var nameEQ = name + "=";
+    var ca = document.cookie.split(';');
+    for (var i=0; i < ca.length; i++) {
+      var co = ca[i];
+      while (co.charAt(0) == ' ') { co = co.substring(1, co.length); }
+      if (co.indexOf(nameEQ) === 0) {
+        return co.substring(nameEQ.length, co.length);
+      }
+    }
+    return undefined;
+	};
+  fxExport(domMisc,readCookie,'readCookie');
+
+  //========== dynamic scripts ==========
+  var scriptCounter = 0;
+  var deleteScript = function (scriptID) {
+    var scriptD = getObj(scriptID);
+    scriptD.parentNode.removeChild(scriptD); //TODO isolate child and set innerHTML to "" to avoid psuedo-leaks?
+  };
+
+  // optional fn/param that gets polled until parm is defined
+  var runScript = function (url, fn, param) {
+    var script = document.createElement("script");
+    script.src = url;
+    var scriptID = 'scriptFnRPC' + scriptCounter++;
+    script.setAttribute('id', scriptID);
+    document.getElementsByTagName("head").item(0).appendChild(script);
+    var timer = {};
+    var check =
+    function () {
+      eval("try { if (" + param + "!== undefined) {var stat = " + param + ";}} catch (e) {}");
+      if (stat !== undefined) {
+        eval(param + " = undefined;");
+        clearInterval(timer.timer);
+        clearInterval(timer.timeout);
+        if (fn instanceof Function) {
+          fn(stat);
+        }
+        deleteScript(scriptID);
+      }
+    };
+    timer.timer = setInterval(check, 3500);
+    timer.timeout =
+    setTimeout(
+      function () {
+        try { clearInterval(timer.timer); }
+        catch (e) {}
+      },
+      5000); //TODO make parameter?
+  };
+
+  // Node {url, globalArg} -> Node a
+  //load script @ url and poll until param is set, then pass it along
+  var evalForeignScriptVal_e = function(urlArg_e) {
+    var result = receiver_e();
+    urlArg_e.lift_e(function(urlArg) {
+        runScript(urlArg.url,
+          function(val) { result.sendEvent(val); },
+          urlArg.globalArg);
+    });
+
+    return result;
+  };
+  fxExport(ws,evalForeignScriptVal_e,'evalForeignScriptVal_e');
+
+  var ajaxRequest = function(method,url,body,async,useFlash,callback) {
+    var xhr;
+    if (useFlash) {
+      xhr = new FlashXMLHttpRequest();
+      xhr.onload = function() { callback(xhr); };
+    }
+    else if (window.XMLHttpRequest && !(window.ActiveXObject)) {
+      xhr = new window.XMLHttpRequest();
+      xhr.onload = function() { callback(xhr); };
+    }
+    else if (window.ActiveXObject) {
+      try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); }
+      catch(e) { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
+
+      xhr.onreadystatechange = function() {
+        if (xhr.readyState == 4) { callback(xhr); }
+      };
+    };
+
+    xhr.open(method,url,async);
+
+    if (method == 'POST') {
+      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
+    }
+
+    xhr.send(body);
+    return xhr;
+  };
+
+  var encodeREST = function(obj) {
+    var str = "";
+    for (var field in obj) {
+      if (typeof(obj[field]) !== 'function') { // skips functions in the object
+        if (str != '') { str += '&'; }
+        str += field + '=' + encodeURIComponent(obj[field]);
+      }
+    }
+    return str;
+  };
+
+  // From json.org
+  var parseJSON = function(str) {
+    try {
+        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
+                str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
+            eval('(' + str + ')');
+    } catch (e) {
+        throw 'cannot parse JSON string: ' + e;
+    }
+  };
+
+  var toJSONString = (function() {
+    var m = {
+            '\b': '\\b',
+            '\t': '\\t',
+            '\n': '\\n',
+            '\f': '\\f',
+            '\r': '\\r',
+            '"' : '\\"',
+            '\\': '\\\\'
+        };
+    var s = {
+      array: function (x) {
+        var a = ['['], b, f, i, l = x.length, v;
+        for (i = 0; i < l; i += 1) {
+          v = x[i];
+          f = s[typeof v];
+          if (f) {
+            v = f(v);
+            if (typeof v == 'string') {
+              if (b) {
+                a[a.length] = ',';
+              }
+              a[a.length] = v;
+              b = true;
+            }
+          }
+        }
+        a[a.length] = ']';
+        return a.join('');
+      },
+      'boolean': function (x) {
+        return String(x);
+      },
+      'null': function (x) {
+        return "null";
+      },
+      number: function (x) {
+        return isFinite(x) ? String(x) : 'null';
+      },
+      object: function (x) {
+        if (x) {
+          if (x instanceof Array) {
+            return s.array(x);
+          }
+          var a = ['{'], b, f, i, v;
+          for (i in x) {
+            v = x[i];
+            f = s[typeof v];
+            if (f) {
+              v = f(v);
+              if (typeof v == 'string') {
+                if (b) {
+                  a[a.length] = ',';
+                }
+                a.push(s.string(i), ':', v);
+                b = true;
+              }
+            }
+          }
+          a[a.length] = '}';
+          return a.join('');
+        }
+        return 'null';
+      },
+      string: function (x) {
+        if (/["\\\x00-\x1f]/.test(x)) {
+          x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
+              var c = m[b];
+              if (c) {
+                return c;
+              }
+              c = b.charCodeAt();
+              return '\\u00' +
+              Math.floor(c / 16).toString(16) +
+              (c % 16).toString(16);
+          });
+        }
+        return '"' + x + '"';
+      }
+    };
+    return function(val) {
+      var f = s[typeof val];
+      if (f) { return f(val); }
+      else { throw 'parseJSON: unknown object type: ' + (typeof val); }
+    };
+  })();
+
+  var serverRequest_e = function(useFlash,requestE) {
+    var response_e = receiver_e();
+
+    requestE.lift_e(function (obj) {
+        var body = '';
+        var method = 'GET';
+        var url = obj.url;
+
+        var reqType = obj.request ? obj.request : (obj.fields ? 'post' : 'get');
+        if (obj.request == 'get') {
+          url += "?" + encodeREST(obj.fields);
+          body = '';
+          method = 'GET';
+        } else if (obj.request == 'post') {
+          body = toJSONString(obj.fields);
+          method = 'POST';
+        } else if (obj.request == 'rawPost') {
+          body = obj.body;
+          method = 'POST';
+        }
+        else if (obj.request == 'rest') {
+          body = encodeREST(obj.fields);
+          method = 'POST';
+        }
+        else {
+          raise ("Invalid request type: " + obj.request);
+        }
+
+        var async = obj.async;
+
+        var xhr;
+
+        // Branch on the response type to determine how to parse it
+        if (obj.response == 'json') {
+          xhr = ajaxRequest(method,url,body,async,useFlash,
+            function(xhr) {
+              response_e.sendEvent(parseJSON(xhr.responseText));
+            });
+        }
+        else if (obj.response == 'xml') {
+          ajaxRequest(method,url,body,async,useFlash,
+            function(xhr) {
+              response_e.sendEvent(xhr.responseXML);
+            });
+        }
+        else if (obj.response == 'plain' || !obj.response) {
+          ajaxRequest(method,url,body,async,useFlash,
+            function(xhr) {
+              response_e.sendEvent(xhr.responseText);
+          });
+        }
+        else {
+          raise('Unknown response format: ' + obj.response);
+        }
+    });
+
+    return response_e;
+  };
+
+  var getWebServiceObject_e = function(requestE) {
+    return serverRequest_e(false,requestE);
+  };
+  fxExport(ws,getWebServiceObject_e,'getWebServiceObject_e');
+
+  var getForeignWebServiceObject_e = function(requestE) {
+    return serverRequest_e(true,requestE);
+  };
+  fxExport(ws,getForeignWebServiceObject_e,'getForeignWebServiceObject_e');
+
+  var cumulativeOffset = function(element) {
+    var valueT = 0, valueL = 0;
+    do {
+      valueT += element.offsetTop  || 0;
+      valueL += element.offsetLeft || 0;
+      element = element.offsetParent;
+    } while (element);
+    return { left: valueL, top: valueT };
+ };
+fxExport(domMisc,cumulativeOffset,'cumulativeOffset');
+
+  ///////// OPTIONALLY EXPORT PUBLIC FUNCTIONS TO GLOBAL NAMESPACE
+  if (!options.hideAll) {
+    for (var zz in flapjax.pub) {
+      if (!options[zz]) {
+        window[zz] = flapjax.pub[zz]; // use eval instead so as to achieve Flash compatibility
+      }
+    }
+  }
+
+  for (var id in exports) {
+    if (window[id]) {
+      if (options.redefine === false) {
+        warn('window.' + id + ' is already defined; Flapjax will not ' +
+             'redefine it.');
+      }
+      else {
+        warn('Flapjax is redefining window.' + id + '.');
+      }
+    }
+
+    if (window[id] && options.redefine === false) {
+      // Don't redefine id
+    }
+    else if (options.show && options.hide && options.show[id] === true &&
+        options.hide[id] === true) {
+      throw('flapjaxInit: invalid options: ' + id + " specified as 'show' and 'hide'");
+    }
+    else if (options.show && options.show[id]) {
+      window[id] = exports[id];
+    }
+    else if (options.hide && options.hide[id]) {
+      // do nothing -- don't export
+    }
+    // Default behavior--check if it is a synonym first ...
+    else if (!synonyms[id] || options.includeSynonyms) {
+      // ... then export if its of the right category
+      if (options.exportMisc && exports[id].__flapjaxCategory === misc) {
+        window[id] = exports[id];
+      }
+      else if (options.exportDOMMisc && exports[id].__flapjaxCategory === domMisc) {
+        window[id] = exports[id];
+      }
+      else if (options.exportDOM && exports[id].__flapjaxCategory === dom) {
+        window[id] = exports[id];
+      }
+      else if (options.exportCore && exports[id].__flapjaxCategory === core) {
+        window[id] = exports[id];
+      }
+      else if (options.exportWS && exports[id].__flapjaxCategory === ws) {
+        window[id] = exports[id];
+      }
+    }
+  }
+
+  return exports;
+}
diff --git a/test/parse-pretty/neg.js b/test/parse-pretty/neg.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/neg.js
@@ -0,0 +1,2 @@
+var foo = -x;
+var bar = +y;
diff --git a/test/parse-pretty/numbers.js b/test/parse-pretty/numbers.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/numbers.js
@@ -0,0 +1,2 @@
+var x = 23;
+var y = 23.0;
diff --git a/test/parse-pretty/object-lit-1.js b/test/parse-pretty/object-lit-1.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/object-lit-1.js
@@ -0,0 +1,4 @@
+var x = {  // avoid ambiguity with BlockStmt
+  foo : 23, 
+  bar : 50 
+}
diff --git a/test/parse-pretty/object-lit-2.js b/test/parse-pretty/object-lit-2.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/object-lit-2.js
@@ -0,0 +1,4 @@
+var x = {  // avoid ambiguity with BlockStmt
+  foo : 23, 
+  bar : 50, 
+} 
diff --git a/test/parse-pretty/prefix-chain.js b/test/parse-pretty/prefix-chain.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/prefix-chain.js
@@ -0,0 +1,4 @@
+var w = typeof typeof 34;
+var x = ! -- x; // derived from jQuery
+var y = ! !x; // derived from Prototype
+var z = !! -- x;
diff --git a/test/parse-pretty/strings.js b/test/parse-pretty/strings.js
new file mode 100644
--- /dev/null
+++ b/test/parse-pretty/strings.js
@@ -0,0 +1,6 @@
+var x = '\b';
+var z = {
+  '\b': '\\b' // lhs is a PropString, not a StringLit, but must be printed
+              //  as a StringLit
+};
+var w = "\\s";
diff --git a/tests/parse-pretty/add.js b/tests/parse-pretty/add.js
deleted file mode 100644
--- a/tests/parse-pretty/add.js
+++ /dev/null
@@ -1,1 +0,0 @@
-1+2;;;;
diff --git a/tests/parse-pretty/do-while.js b/tests/parse-pretty/do-while.js
deleted file mode 100644
--- a/tests/parse-pretty/do-while.js
+++ /dev/null
@@ -1,3 +0,0 @@
-do {
-  x = y;
-} while (element);
diff --git a/tests/parse-pretty/flapjax-fxinternal-22dec2008.js b/tests/parse-pretty/flapjax-fxinternal-22dec2008.js
deleted file mode 100644
--- a/tests/parse-pretty/flapjax-fxinternal-22dec2008.js
+++ /dev/null
@@ -1,2552 +0,0 @@
-function flapjaxInit(options) {
-  // compress via http://alex.dojotoolkit.org/shrinksafe/
-  // make sure to change final eval call to use exported library renamed
-  // name
-
-  var defaultOptions = {
-    includeSynonyms: true,
-    exportMisc: true,
-    exportCore: true,
-    exportDOM: true,
-    exportDOMMisc: true,
-    exportWS: true,
-    hide: [ ],
-    show: [ ],
-    redefine: false
-  };
-
-  if (options === false) {
-    options = { hide: [ ], show: [ ] };
-  }
-  else if (options === true || !options) {
-    options = { };
-  }
-
-  // Fill in any missing options using the defaults.
-  for (var option in defaultOptions) {
-    if (options[option] === undefined) {
-      options[option] = defaultOptions[option];
-    }
-  };
-
-  // Transform hide and show lists into tables
-  var hideArray = options.hide;
-  options.hide = { };
-  for (var i = 0; i < hideArray.length; i++) {
-    options.hide[hideArray[i]] = true;
-  }
-  var showArray = options.show;
-  options.show = { };
-  for (var i = 0; i < showArray.length; i++) {
-    options.show[showArray[i]] = true;
-  }
-
-  var warn = function(s) {
-    /*if (console && console.warn) {
-      console.warn(s);
-    }*/
-  };
-
-
-  var flapjax = {
-    version: 4,
-    base: {}, //pulses
-    combinators: {}, // combinators yielding nodes
-	  behaviours: {},
-    dom: {} // dom convenience methods and combinators
-  };
-
-  var d = flapjax.dom;
-
-	flapjax.pub = {util:flapjax};
-
-	var annotate = function(fn,names,protoArg,protoObjs,protoNames) {
-		for(var i=0; i<names.length;i++) {
-			flapjax.pub[names[i]] = fn;
-		}
-		if(protoArg != undefined) {
-			var pf = function() {
-				var args = slice(arguments,0);
-				args.splice(protoArg,0,this);
-				return fn.apply(this,args);
-			};
-			for(var i=0; i<protoObjs.length; i++) {
-				for(var j=0; j<protoNames.length;j++) {
-					protoObjs[i][protoNames[j]] = pf;
-				}
-			}
-		}
-	};
-
-  var exports = { };
-  var synonyms = { };
-
-  var misc = { }; /* Used as a symbol */
-  var core = { }; /* Used as a symbol */
-  var dom = { }; /* Used as a symbol */
-  var domMisc = { }; /* Used as a symbol */
-  var ws = { }; /* Used as a symbol */
-
-  var fxExport = function(exportCategory,exportVal,
-                          exportName /* , synonyms */) {
-    if (!(exportCategory instanceof Object)) {
-      throw 'fxExport: category is ' + exportCategory;
-    };
-
-    if (typeof(exportName) != 'string') {
-      console.log(exportVal);
-      throw 'fxExport: name is ' + exportName;
-    };
-
-    exportVal.__flapjaxCategory = exportCategory;
-    exports[exportName] = exportVal;
-    for (var i = 3; i < arguments.length; i++) {
-      exports[arguments[i]] = exportVal;
-      synonyms[arguments[i]] = true;
-    }
-  };
-
-  var fxMethodSynonyms = function(obj,method) {
-    if (options.includeSynonyms === true) {
-      for (var i = 2; i < arguments.length; i++) {
-        obj.prototype[arguments[i]] = obj.prototype[method];
-      };
-    };
-  };
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Miscellaneous functions
-
-	//credit 4umi
-	//slice: Array a * Integer * Integer -> Array a
-	var slice = function (arr, start, stop) {
-		var i, len = arr.length, r = [];
-		if( !stop ) { stop = len; }
-		if( stop < 0 ) { stop = len + stop; }
-		if( start < 0 ) { start = len - start; }
-		if( stop < start ) { i = start; start = stop; stop = i; }
-		for( i = 0; i < stop - start; i++ ) { r[i] = arr[start+i]; }
-		return r;
-	}
-
-	var isEqual = function (a,b) {
-		return (a == b) ||
-			( (((typeof(a) == 'number') && isNaN(a)) || a == 'NaN') &&
-			  (((typeof(b) == 'number') && isNaN(b)) || b == 'NaN') );
-	};
-
-	//member: a * Array b -> Boolean
-	var member = function(elt, lst) {
-		for (var i = 0; i < lst.length; i++) {
-			if (isEqual(lst[i], elt)) {return true;}
-		}
-		return false;
-	};
-
-	var zip = function(arrays) {
-		if (arrays.length == 0) return [];
-		var ret = [];
-		for(var i=0; i<arrays[0].length;i++) {
-			ret.push([]);
-			for(var j=0; j<arrays.length;j++)
-				ret[i].push(arrays[j][i]);
-		}
-		return ret;
-	};
-
-	//map: (a * ... -> z) * [a] * ... -> [z]
-	var map = function (fn) {
-		var arrays = slice(arguments, 1);
-		if (arrays.length === 0) { return []; }
-		else if (arrays.length === 1) {
-			var ret = [];
-			for(var i=0; i<arrays[0].length; i++) {ret.push(fn(arrays[0][i]));}
-			return ret;
-		}
-		else {
-			var ret = zip(arrays);
-			var o = new Object();
-			for(var i=0; i<ret.length; i++) {ret[i] = fn.apply(o,ret[i]);}
-			return ret;
-		}
-	};
-
-	//filter: (a -> Boolean) * Array a -> Array a
-	var filter = function (predFn, arr) {
-		var res = [];
-		for (var i = 0; i < arr.length; i++) {
-			if (predFn(arr[i])) { res.push(arr[i]); }
-		}
-		return res;
-	};
-
-  //fold: (a * .... * accum -> accum) * accum * [a] * ... -> accum
-  //fold over list(s), left to right
-  var fold = function(fn, init /* arrays */) {
-		var lists = slice(arguments, 2);
-		if (lists.length === 0) { return init; }
-		else if(lists.length === 1) {
-			var acc = init;
-			for(var i = 0; i < lists[0].length; i++) {
-				acc = fn(lists[0][i],acc);
-			}
-			return acc;
-		}
-		else {
-			var acc = init;
-			for (var i = 0; i < lists[0].length; i++) {
-				var args = map( function (lst) { return lst[i];},
-		          lists);
-				args.push(acc);
-				acc = fn.apply({}, args);
-			}
-			return acc;
-		}
-  };
-
-  //foldR: (a * .... * accum -> accum) * accum * [a] * ... -> accum
-	//fold over list(s), right to left, fold more memory efficient (left to right)
-	var foldR = function (fn, init /* arrays */) {
-    var lists = slice(arguments, 2);
-		if (lists.length === 0) { return init; }
-		else if(lists.length === 1) {
-			var acc = init;
-			for(var i=lists[0].length - 1; i > -1; i--)
-				acc = fn(lists[0][i],acc);
-			return acc;
-		}
-		else {
-			var acc = init;
-			for (var i = lists[0].length - 1; i > -1; i--) {
-				var args = map( function (lst) { return lst[i];},
-		          lists);
-				args.push(acc);
-				acc = fn.apply({}, args);
-			}
-			return acc;
-		}
-	};
-
-  var pushAll = function(destArray,srcArray) {
-    for (var i = 0; i < srcArray.length; i++) {
-      destArray.push(srcArray[i]);
-    }
-    return destArray;
-  };
-
-  fxExport(misc,slice,'slice');
-  fxExport(misc,member,'member');
-  fxExport(misc,map,'map');
-  fxExport(misc,map,'forEach');
-  fxExport(misc,filter,'filter');
-  fxExport(misc,fold,'fold');
-  fxExport(misc,foldR,'foldR');
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Flapjax core
-
-  //Pulse: Stamp * Path * Obj
-  var Pulse = function (stamp, value) {
-    this.stamp = stamp;
-    this.value = value;
-  };
-
-
-  //Probably can optimize as we expect increasing insert runs etc
-  var PQ = function () {
-    var ctx = this;
-    ctx.val = [];
-    this.insert = function (kv) {
-			ctx.val.push(kv);
-			var kvpos = ctx.val.length-1;
-			while(kvpos > 0 && kv.k < ctx.val[Math.floor((kvpos-1)/2)].k) {
-				var oldpos = kvpos;
-				kvpos = Math.floor((kvpos-1)/2);
-				ctx.val[oldpos] = ctx.val[kvpos];
-				ctx.val[kvpos] = kv;
-			}
-    };
-    this.isEmpty = function () {
-      return ctx.val.length === 0;
-    };
-    this.pop = function () {
-			if(ctx.val.length == 1) {
-				return ctx.val.pop();
-			}
-			var ret = ctx.val.shift();
-			ctx.val.unshift(ctx.val.pop());
-			var kvpos = 0;
-			var kv = ctx.val[0];
-			while(1) {
-				var leftChild = (kvpos*2+1 < ctx.val.length ? ctx.val[kvpos*2+1].k : kv.k+1);
-				var rightChild = (kvpos*2+2 < ctx.val.length ? ctx.val[kvpos*2+2].k : kv.k+1);
-				if(leftChild > kv.k && rightChild > kv.k)
-					break;
-				else if(leftChild < rightChild) {
-					ctx.val[kvpos] = ctx.val[kvpos*2+1];
-					ctx.val[kvpos*2+1] = kv;
-					kvpos = kvpos*2+1;
-				}
-				else {
-					ctx.val[kvpos] = ctx.val[kvpos*2+2];
-					ctx.val[kvpos*2+2] = kv;
-					kvpos = kvpos*2+2;
-				}
-			}
-			return ret;
-		};
-  };
-
-  var lastRank = 0;
-  var stamp = 1;
-  var nextStamp = function () { return ++stamp; };
-
-  //propagatePulse: Pulse * Array Node ->
-  //Send the pulse to each node
-  var propagatePulse = function (pulse, node) {
-    var queue = new PQ(); //topological queue for current timestep
-	queue.insert({k:node.rank,n:node,v:pulse});
-	while(!(queue.isEmpty())) {
-		var qv = queue.pop();
-		qv.n.updater(function(nextPulse) {
-			for(var i=0; i<qv.n.sendsTo.length;i++)
-				queue.insert({k:qv.n.sendsTo[i].rank,n:qv.n.sendsTo[i],v:nextPulse});
-		},new Pulse(qv.v.stamp,qv.v.value));
-	}
-  };
-
-  //Event: Array Node b * ( (Pulse a -> Void) * Pulse b -> Void)
-  var EventStream = function (nodes,updater) {
-    this.updater = updater;
-
-    this.sendsTo = []; //forward link
-
-    for (var i = 0; i < nodes.length; i++) {
-      nodes[i].sendsTo.push(this);
-    }
-
-    this.rank = ++lastRank;
-  };
-	EventStream.prototype = new Object();
-
-  fxExport(core,EventStream,'EventStream');
-
-
-  //createNode: Array Node a * ( (Pulse b ->) * (Pulse a) -> Void) -> Node b
-  var createNode = function (nodes, updater) {
-		return new EventStream(nodes,updater);
-	};
-
-  //attachListenerNode: Node * Node -> Voids
-  //flow from node to dependent
-  //note: does not add flow as counting for rank nor updates parent ranks
-  var attachListener = function (node, dependent) {
-    if (!(node instanceof EventStream)) { throw 'attachListenenerNode: expects event as first arg';} //SAFETY
-    if (!(dependent instanceof EventStream)) { throw 'attachListenenerNode: expects event as second arg';} //SAFETY
-
-    node.sendsTo.push(dependent);
-		if(node.rank > dependent.rank) {
-			var lowest = lastRank+1;
-			var q = [dependent];
-			while(q.length) {
-				var cur = q.splice(0,1)[0];
-				cur.rank = ++lastRank;
-				q = q.concat(cur.sendsTo);
-			}
-		}
-  };
-
-  //removeListenerNode: Node * Node -> Boolean
-  //remove flow from node to dependent
-  //note: does not remove flow as counting for rank nor updates parent ranks
-  var removeListener = function (node, dependent)
-  {
-    if (!(node instanceof EventStream)) { throw 'removeListenerNode: expects event as first arg';} //SAFETY
-    if (!(dependent instanceof EventStream)) { throw 'removeListenenerNode: expects event as second arg';} //SAFETY
-
-    var foundSending = false;
-    for (var i = 0; i < node.sendsTo.length && !foundSending; i++) {
-      if (node.sendsTo[i] == dependent) {
-        node.sendsTo.splice(i, 1);
-        foundSending = true;
-      }
-    }
-
-    return foundSending;
-  };
-
-  // An internal_e is a node that simply propagates all pulses it receives.  It's used internally by various
-  // combinators.
-  var internal_e = function(dependsOn) {
-    return createNode(dependsOn || [ ],function(send,pulse) { send(pulse); });
-  }
-
-  var zero_e = function() {
-    return createNode([],function(send,pulse) {
-        throw ('zero_e : received a value; zero_e should not receive a value; the value was ' + pulse.value);
-    });
-  };
-  fxExport(core,zero_e,'zero_e');
-
-  var one_e = function(val) {
-    var sent = false;
-    var evt = createNode([],function(send,pulse) {
-      if (sent) {
-        throw ('one_e : received an extra value');
-      }
-      sent = true;
-      send(pulse);
-    });
-    window.setTimeout(function() { sendEvent(evt,val); },0);
-    return evt;
-  };
-  fxExport(core,one_e,'one_e');
-
-  // a.k.a. mplus; merge_e(e1,e2) == merge_e(e2,e1)
-  var merge_e = function() {
-    if (arguments.length == 0) {
-      return zero_e();
-    }
-    else {
-      var deps = slice(arguments,0);
-      return internal_e(deps);
-    }
-  };
-  fxExport(core,merge_e,'merge_e');
-
-  EventStream.prototype.merge_e = function() {
-    var deps = slice(arguments,0);
-    deps.push(this);
-    return internal_e(deps);
-  };
-  fxMethodSynonyms(EventStream,'merge_e','merge');
-
-  EventStream.prototype.constant_e = function(constantValue) {
-    return createNode([this],function(send,pulse) {
-      pulse.value = constantValue;
-      send(pulse);
-    });
-  };
-  fxMethodSynonyms(EventStream,'constant_e','constant','replaceValue_e','replaceValue');
-
-  var constant_e = function(e) { return e.constant_e(); };
-  fxExport(core,constant_e,'constant_e','replaceValue_e');
-
-	var createTimeSyncNode = function(nodes) {
-		var nqs = map(function(n) {
-				var qpulse = [];
-				return {q:qpulse,v:createNode([n],function(s,p) {qpulse.push(p.value); s(p);},nodes)};
-    },nodes);
-		return createNode(
-      map(function(n) {return n.v;},nqs), function(s,p) {
-        var allfull = fold(function(n,acc) {return n.q.length && acc;},true,nqs);
-        if(allfull) {
-          p.value = map(function(n) {return n.q.shift();},nqs);
-          s(p);
-      }});
-	};
-
-	//This is up here so we can add things to its prototype that are in flapjax.combinators
-  var Behaviour = function (event, init, updater) {
-    if (!(event instanceof EventStream)) {
-      throw 'Behaviour: expected event as second arg';
-    }
-
-    var behave = this;
-    this.last = init;
-
-    //sendEvent to this might impact other nodes that depend on this event
-    //sendBehaviour defaults to this one
-    this.underlyingRaw = event;
-
-    //unexposed, sendEvent to this will only impact dependents of this behaviour
-    this.underlying =
-    createNode(
-      [event],
-	  (updater ?
-	   function (s, p) {behave.last = updater(p.value); p.value = behave.last; s(p);} :
-	   function (s, p) {behave.last = p.value;  s(p);}));
-  };
-	Behaviour.prototype = new Object();
-  fxExport(core,Behaviour,'Behaviour');
-  fxExport(core,Behaviour,'Behavior');
-
-  var receiver_e = function() {
-    var evt = internal_e();
-    evt.sendEvent = function(value) {
-      propagatePulse(new Pulse(nextStamp(), value),evt);
-    };
-    return evt;
-  };
-  fxExport(core,receiver_e,'receiver_e');
-
-  //note that this creates a new timestamp and new event queue
-  var sendEvent = function (node, value) {
-    if (!(node instanceof EventStream)) { throw 'sendEvent: expected Event as first arg'; } //SAFETY
-
-    propagatePulse(new Pulse(nextStamp(), value),node);
-  };
-  fxExport(core,sendEvent,'sendEvent');
-  synonyms['sendEvent'] = true; // Hack: sendEvent simply should not be exported; calling it a synonym deprecates it.
-
-  // bind_e :: EventStream a * (a -> EventStream b) -> EventStream b
-  EventStream.prototype.bind_e = function(k) {
-    /* m.sendsTo result_e
-     * result_e.sendsTo prev_e
-     * prev_e.sendsTo return_e
-     */
-    var m = this;
-    var prev_e = false;
-
-    var out_e = createNode([],function(send,pulse) { send(pulse); });
-    out_e.name = "bind out_e";
-
-    var in_e = createNode([m], function (send,pulse) {
-      if (prev_e) {
-        removeListener(prev_e,out_e);
-      }
-      prev_e = k(pulse.value);
-      if (prev_e instanceof EventStream) {
-        attachListener(prev_e,out_e);
-      }
-      else {
-        throw "bind_e : expected EventStream";
-      }
-    });
-    in_e.name = "bind in_e";
-
-    return out_e;
-  };
-
-  /* Could be written as:
-   *
-   * e.bind_e(function(v) { return one_e(f(v)); })
-   */
-  EventStream.prototype.lift_e = function(f) {
-    if (!(f instanceof Function)) {
-      throw ('lift_e : expected a function as the first argument; received ' + f);
-    };
-
-    return createNode([this],function(send,pulse) {
-      pulse.value = f(pulse.value);
-      send(pulse);
-    });
-  };
-  fxMethodSynonyms(EventStream,'lift_e',
-    'transform_e','map_e','apply_e','lift','transform','map','apply');
-
-  EventStream.prototype.not_e = function() { return this.lift_e(function(v) { return !v; }); };
-  fxMethodSynonyms(EventStream,'not_e','not');
-
-  var not_e = function(e) { return e.not_e(); };
-  fxExport(core,not_e,'not_e','not');
-
-  EventStream.prototype.filter_e = function(pred) {
-    if (!(pred instanceof Function)) {
-      throw ('filter_e : expected predicate; received ' + pred);
-    };
-
-    // Can be a bind_e
-    return createNode([this],
-      function(send,pulse) {
-        if (pred(pulse.value)) { send(pulse); }
-    });
-  };
-  fxMethodSynonyms(EventStream,'filter_e','filter');
-
-  var filter_e = function(e,p) { return e.filter_e(p); };
-  fxExport(core,filter_e,'filter_e');
-
-  // Fires just once.
-  EventStream.prototype.once_e = function() {
-    var done = false;
-    // Alternately: this.collect_e(0,\n v -> (n+1,v)).filter_e(\(n,v) -> n == 1).lift_e(fst)
-    return createNode([this],function(send,pulse) {
-      if (!done) { done = true; send(pulse); }
-    });
-  };
-  fxMethodSynonyms(EventStream,'once_e','once');
-
-  var once_e = function(e) { return e.once_e(); };
-  fxExport(core,once_e,'once_e');
-
-  EventStream.prototype.skipFirst_e = function() {
-    var skipped = false;
-    return createNode([this],function(send,pulse) {
-      if (skipped)
-        { send(pulse); }
-      else
-        { skipped = true; }
-    });
-  };
-  fxMethodSynonyms(EventStream,'skipFirst_e','skipFirst');
-
-  var skipFirst_e = function(e) { return e.skipFirst_e(); };
-  fxExport(core,skipFirst_e,'skipFirst_e');
-
-  EventStream.prototype.collect_e = function(init,fold) {
-    var acc = init;
-    return this.lift_e(
-      function (n) {
-        var next = fold(n, acc);
-        acc = next;
-        return next;
-      });
-  };
-  fxMethodSynonyms(EventStream,'collect_e','collect','transformWithMemory');
-
-  var collect_e = function(e,i,f) { return e.collect_e(i,f); };
-  fxExport(core,collect_e,'collect_e');
-
-  // a.k.a. join
-  EventStream.prototype.switch_e = function() {
-    return this.bind_e(function(v) { return v; });
-  };
-  fxMethodSynonyms(EventStream,'switch_e','forwardLatest');
-
-  var switch_e = function(e) { return e.switch_e(); };
-  fxExport(core,switch_e,'switch_e');
-
-  EventStream.prototype.if_e = function(thenE,elseE) {
-    var testStamp = -1;
-    var testValue = false;
-
-    createNode([this],function(_,pulse) { testStamp = pulse.stamp; testValue = pulse.value; });
-
-    return merge_e(
-      createNode([thenE],function(send,pulse) { if (testValue && (testStamp == pulse.stamp)) { send(pulse); } }),
-      createNode([elseE],function(send,pulse) { if (!testValue && (testStamp == pulse.stamp)) { send(pulse); } }));
-  };
-  fxMethodSynonyms(EventStream,'if_e','choose_e','choose');
-
-  var if_e = function(test,thenE,elseE) {
-    if (test instanceof EventStream)
-      { return test.if_e(thenE,elseE); }
-    else
-      { return test ? thenE : elseE; }
-  };
-  fxExport(core,if_e,'if_e','choose_e');
-
-  var cond_e = function (/*. predValArrays */) {
-    var predValArrays = slice(arguments, 0);
-    var acc = zero_e();
-    for (var i = predValArrays.length - 1; i > -1; i--) {
-      acc = if_e(predValArrays[i][0],predValArrays[i][1], acc);
-    }
-    return acc;
-  };
-  fxExport(core,cond_e,'cond_e');
-
-  var and_e = function (/* . nodes */) {
-    var nodes = slice(arguments, 0);
-
-    var acc = (nodes.length > 0)?
-    nodes[nodes.length - 1] : one_e(true);
-
-    for (var i = nodes.length - 2; i > -1; i--) {
-      acc = if_e(
-        nodes[i],
-        acc,
-        nodes[i].constant_e(false));
-    }
-    return acc;
-  };
-  fxExport(core,and_e,'and_e');
-
-  EventStream.prototype.and_e = function( /* others */ ) {
-    var deps = [this].concat(slice(arguments,0));
-    return and_e.apply(this,deps);
-  };
-  fxMethodSynonyms(EventStream,'and_e','and');
-
-  var or_e = function () {
-    var nodes = slice(arguments, 0);
-    var acc = (nodes.length > 2)?
-    nodes[nodes.length - 1] : one_e(false);
-    for (var i = nodes.length - 2; i > -1; i--) {
-      acc = if_e(
-        nodes[i],
-        nodes[i],
-        acc);
-    }
-    return acc;
-  };
-  fxExport(core,or_e,'or_e');
-
-  EventStream.prototype.or_e = function(/*others*/) {
-    var deps = [this].concat(slice(arguments,0));
-    return or_e.apply(this,deps);
-  };
-  fxMethodSynonyms(EventStream,'or_e','or');
-
-  var delayStatic_e = function (event, time) {
-
-    var resE = internal_e();
-
-    createNode(
-      [event],
-      function (s, p) {
-        setTimeout(
-          function () { sendEvent(resE, p.value);},
-      time ); });
-
-    return resE;
-  };
-
-  //delay_e: Event a * [Behaviour] Number ->  Event a
-  EventStream.prototype.delay_e = function (time) {
-    var event = this;
-
-    if (time instanceof Behaviour) {
-
-      var receiverEE = internal_e();
-      var link =
-      {
-        from: event,
-        towards: delayStatic_e(event, valueNow(time))
-      };
-
-			//TODO: Change semantics such that we are always guaranteed to get an event going out?
-      var switcherE =
-      createNode(
-        [changes(time)],
-        function (s, p) {
-          removeListener(link.from, link.towards);
-          link =
-          {
-            from: event,
-            towards: delayStatic_e(event, p.value)
-          };
-          sendEvent(receiverEE, link.towards);
-        });
-
-      var resE = receiverEE.switch_e();
-
-      sendEvent(switcherE, valueNow(time));
-      return resE;
-
-        } else { return delayStatic_e(event, time); }
-  };
-  fxMethodSynonyms(EventStream,'delay_e','delay');
-
-  var delay_e = function(sourceE,interval) {
-    return sourceE.delay_e(interval);
-  };
-  fxExport(core,delay_e,'delay_e');
-
-  //lift_e: ([Event] (. Array a -> b)) . Array [Event] a -> [Event] b
-  var lift_e = function (fn /*, [node0 | val0], ...*/) {
-    //      if (!(fn instanceof Function)) { throw 'lift_e: expected fn as second arg'; } //SAFETY
-
-    var valsOrNodes = slice(arguments, 0);
-    //selectors[i]() returns either the node or real val, optimize real vals
-    var selectors = [];
-    var selectI = 0;
-    var nodes = [];
-    for (var i = 0; i < valsOrNodes.length; i++) {
-      if (valsOrNodes[i] instanceof EventStream) {
-        nodes.push(valsOrNodes[i]);
-        selectors.push(
-          (function(ii) {
-              return function(realArgs) {
-                return realArgs[ii];
-              };
-          })(selectI));
-        selectI++;
-      } else {
-        selectors.push(
-          (function(aa) {
-              return function () {
-                return aa;
-              };
-          })(valsOrNodes[i]));
-      }
-    }
-
-    var context = this;
-		var nofnodes = slice(selectors,1);
-
-    if (nodes.length === 0) {
-      return one_e(fn.apply(context, valsOrNodes));
-    } else if ((nodes.length === 1) && (fn instanceof Function)) {
-      return nodes[0].lift_e(
-        function () {
-          var args = arguments;
-          return fn.apply(
-            context,
-            map(function (s) {return s(args);}, nofnodes));
-        });
-    } else if (nodes.length === 1) {
-      return fn.lift_e(
-        function (v) {
-          var args = arguments;
-          return v.apply(
-            context,
-            map(function (s) {return s(args);}, nofnodes));
-        });
-    } else if (fn instanceof Function) {
-      return createTimeSyncNode(nodes).lift_e(
-        function (arr) {
-          return fn.apply(
-            this,
-            map(function (s) { return s(arr); }, nofnodes));
-        });
-    } else if (fn instanceof EventStream) {
-      return createTimeSyncNode(nodes).lift_e(
-        function (arr) {
-          return arr[0].apply(
-            this,
-            map(function (s) {return s(arr); }, nofnodes));
-        });
-        } else {throw 'unknown lift_e case';}
-  };
-  fxExport(core,lift_e,'lift_e','transform_e','map_e','apply_e');
-
-  EventStream.prototype.snapshot_e = function (valueB) {
-    return createNode(
-      [this],
-      function (s, p) {
-        p.value = valueNow(valueB);
-        s(p);
-      }
-      );
-  };
-  fxMethodSynonyms(EventStream,'snapshot_e','snapshot','takeSnapshot');
-
-  var snapshot_e = function(triggerE,valueB) {
-    return triggerE.snapshot_e(valueB);
-  };
-  fxExport(core,snapshot_e,'snapshot_e');
-
-  EventStream.prototype.filterRepeats_e = function(optStart) {
-    var hadFirst = optStart === undefined ? false : true;
-    var prev = optStart;
-
-    return this.filter_e(function (v) {
-      if (!hadFirst || !(isEqual(prev,v))) {
-        hadFirst = true;
-        prev = v;
-        return true;
-      }
-      else {
-        return false;
-      }
-    });
-  };
-  fxMethodSynonyms(EventStream,'filterRepeats_e','filterRepeats');
-
-  var filterRepeats_e = function(sourceE,optStart) {
-    return sourceE.filterRepeats_e(optStart);
-  };
-  fxExport(core,filterRepeats_e,'filterRepeats_e','filterRepeats');
-
-  //credit Pete Hopkins
-  var calmStatic_e = function (triggerE, time) {
-		var out = internal_e();
-    createNode(
-      [triggerE],
-      function() {
-        var towards = null;
-        return function (s, p) {
-          if (towards !== null) { clearTimeout(towards); }
-          towards = setTimeout( function () { towards = null; sendEvent(out,p.value) }, time );
-        };
-      }());
-		return out;
-  };
-
-  //calm_e: Event a * [Behaviour] Number -> Event a
-  EventStream.prototype.calm_e = function(time) {
-    if (time instanceof Behaviour) {
-			var out = internal_e();
-      createNode(
-        [this],
-        function() {
-          var towards = null;
-          return function (s, p) {
-            if (towards !== null) { clearTimeout(towards); }
-             towards = setTimeout( function () { towards = null; sendEvent(out,p.value);}, valueNow(time));
-          };
-        }());
-			return out;
-    } else {
-      return calmStatic_e(this,time);
-    }
-  };
-  fxMethodSynonyms(EventStream,'calm_e','calm');
-
-  var calm_e = function(sourceE,interval) {
-    return sourceE.calm_e(interval);
-  };
-  fxExport(core,calm_e,'calm_e');
-
-  EventStream.prototype.blind_e = function (time) {
-    return createNode(
-      [this],
-      function () {
-        var intervalFn =
-        time instanceof Behaviour?
-        function () { return valueNow(time); }
-        : function () { return time; };
-        var lastSent = (new Date()).getTime() - intervalFn() - 1;
-        return function (s, p) {
-          var curTime = (new Date()).getTime();
-          if (curTime - lastSent > intervalFn()) {
-            lastSent = curTime;
-            s(p);
-          }
-        };
-      }());
-  };
-  fxMethodSynonyms(EventStream,'blind_e','blind');
-
-  var blind_e = function(sourceE,interval) {
-    return sourceE.blind_e(interval);
-  };
-  fxExport(core,blind_e,'blind_e');
-
-  EventStream.prototype.hold = function(init) {
-    return new Behaviour(this,init);
-  };
-  fxMethodSynonyms(EventStream,'hold','startsWith','toBehavior');
-
-  var hold = function(e,init) {
-    if (!(e instanceof EventStream)) {
-      throw 'hold: expected EventStream; received ' + e;
-    }
-    return e.hold(init);
-  };
-  fxExport(core,hold,'hold');
-
-  Behaviour.prototype.valueNow = function() {
-    return this.last;
-  };
-  var valueNow = function(behavior) { return behavior.valueNow(); };
-  fxExport(core,valueNow,'valueNow');
-
-  Behaviour.prototype.changes = function() {
-    return this.underlying;
-  };
-  fxMethodSynonyms(Behaviour,'changes','toEvent');
-
-   var changes = function (behave) { return behave.changes(); };
-  fxExport(core,changes,'changes');
-
-  Behaviour.prototype.switch_b = function() {
-    var behaviourCreatorsB = this;
-    var init = valueNow(behaviourCreatorsB);
-
-    var prevSourceE = null;
-
-    var receiverE = new internal_e();
-
-		//XXX could result in out-of-order propagation! Fix!
-    var makerE =
-    createNode(
-      [changes(behaviourCreatorsB)],
-      function (_, p) {
-        if (!(p.value instanceof Behaviour)) { throw 'switch_b: expected Behaviour as value of Behaviour of first argument'; } //SAFETY
-        if (prevSourceE != null) {
-          removeListener(prevSourceE, receiverE);
-        }
-
-        prevSourceE = changes(p.value);
-        attachListener(prevSourceE, receiverE);
-
-        sendEvent(receiverE, valueNow(p.value));
-      });
-
-    if (init instanceof Behaviour) {
-      sendEvent(makerE, init);
-    }
-
-    return hold(
-      receiverE,
-      init instanceof Behaviour? valueNow(init) : init);
-  };
-  fxMethodSynonyms(Behaviour,'switch_b','forwardLatest');
-
-  var switch_b = function (b) { return b.switch_b(); };
-  fxExport(core,switch_b,'switch_b');
-
-  //TODO test, signature
-  var timer_b = function(interval) {
-    return hold(timer_e(interval), (new Date()).getTime());
-  };
-  fxExport(dom,timer_b,'timer_b','asTimer_b');
-
-  //TODO test, signature
-  var delayStatic_b = function (triggerB, time, init) {
-    return hold(delayStatic_e(changes(triggerB), time), init);
-  };
-
-  //TODO test, signature
-  Behaviour.prototype.delay_b = function (time, init) {
-    var triggerB = this;
-    if (time instanceof Behaviour) {
-      return hold(
-        delay_e(
-          changes(triggerB),
-          time),
-        arguments.length > 3 ? init : valueNow(triggerB));
-    } else {
-      return delayStatic_b(
-        triggerB,
-        time,
-        arguments.length > 3 ? init : valueNow(triggerB));
-    }
-  };
-  fxMethodSynonyms(Behaviour,'delay_b','delay');
-
-  var delay_b = function(srcB, timeB, init) {
-    return srcB.delay_b(timeB,init);
-  };
-  fxExport(core,delay_b,'delay_b');
-
-  //artificially send a pulse to underlying event node of a behaviour
-  //note: in use, might want to use a receiver node as a proxy or an identity map
-  Behaviour.prototype.sendBehaviour = function(val) {
-    sendEvent(this.underlyingRaw,val);
-  };
-  Behaviour.prototype.sendBehavior = Behaviour.prototype.sendBehaviour;
-
-  var sendBehaviour = function (b,v) { b.sendBehaviour(v); };
-  fxExport(core,sendBehaviour,'sendBehaviour');
-  fxExport(core,sendBehaviour,'sendBehavior');
-
-  Behaviour.prototype.if_b = function(trueB,falseB) {
-    var testB = this;
-    //TODO auto conversion for behaviour funcs
-    if (!(trueB instanceof Behaviour)) { trueB = constant_b(trueB); }
-    if (!(falseB instanceof Behaviour)) { falseB = constant_b(falseB); }
-    return lift_b(function(te,t,f) { return te ? t : f; },testB,trueB,falseB);
-  };
-  fxMethodSynonyms(Behaviour,'if_b','choose_b','choose');
-
-  var if_b = function(test,cons,altr) {
-    if (!(test instanceof Behaviour)) { test = constant_b(test); };
-
-    return test.if_b(cons,altr);
-  };
-  fxExport(core,if_b,'if_b','choose_b');
-
-
-  //cond_b: . [Behaviour boolean, Behaviour a] -> Behaviour a
-  var cond_b = function (/* . pairs */ ) {
-    var pairs = slice(arguments, 0);
-	return lift_b.apply({},[function() {
-			for(var i=0;i<pairs.length;i++) {
-				if(arguments[i]) return arguments[pairs.length+i];
-			}
-			return undefined;
-		}].concat(map(function(pair) {return pair[0];},pairs).concat(map(function(pair) {return pair[1];},pairs))));
-  };
-  fxExport(core,cond_b,'cond_b');
-
-  //TODO optionally append to objects
-  //createConstantB: a -> Behaviour a
-  var constant_b = function (val) {
-    return new Behaviour(internal_e(), val);
-  };
-  fxExport(core,constant_b,'constant_b','receiver_b');
-
-	var lift_b = function (fn /* . behaves */) {
-    var args = slice(arguments, 1);
-
-    //dependencies
-    var constituentsE =
-      map(changes,
-      filter(function (v) { return v instanceof Behaviour; },
-        arguments));
-
-    //calculate new vals
-    var getCur = function (v) {
-      return v instanceof Behaviour ? v.last : v;
-    };
-
-    var ctx = this;
-    var getRes = function () {
-      return getCur(fn).apply(ctx, map(getCur, args));
-    };
-
-    if(constituentsE.length == 1) {
-      return new Behaviour(constituentsE[0],getRes(),getRes);
-    }
-
-    //gen/send vals @ appropriate time
-    var prevStamp = -1;
-    var mid = createNode(constituentsE, function (s, p) {
-        if (p.stamp != prevStamp) {
-          prevStamp = p.stamp;
-          s(p);
-    }});
-    return new Behaviour(mid,getRes(),getRes);
-  };
-  fxExport(core,lift_b,'lift_b','transform_b','apply_b');
-
-  Behaviour.prototype.lift_b = function(/* args */) {
-    var args= slice(arguments,0).concat([this]);
-    return lift_b.apply(this,args);
-  };
-  fxMethodSynonyms(Behaviour,'lift_b','transform_b','apply_b','transform',
-    'lift');
-
-  var and_b = function (/* . behaves */) {
-	return lift_b.apply({},[function() {
-			for(var i=0; i<arguments.length; i++) {if(!arguments[i]) return false;}
-			return true;
-	}].concat(slice(arguments,0)));
-  };
-  fxExport(core, and_b, 'and_b');
-
-  Behaviour.prototype.and_b = function() {
-    return and_b([this].concat(arguments));
-  };
-  fxMethodSynonyms(Behaviour,'and_b','and');
-
-  var or_b = function (/* . behaves */ ) {
-	return lift_b.apply({},[function() {
-			for(var i=0; i<arguments.length; i++) {if(arguments[i]) return true;}
-			return false;
-	}].concat(slice(arguments,0)));
-  };
-  fxExport(core,or_b,'or_b');
-
-  Behaviour.prototype.or_b = function () {
-    return or_b([this].concat(arguments));
-  };
-  fxMethodSynonyms(Behaviour, 'or_b', 'or');
-
-  Behaviour.prototype.not_b = function() {
-    return this.lift_b(function(v) { return !v; });
-  };
-  fxMethodSynonyms(Behaviour,'not_b','not');
-
-  var not_b = function(b) { return b.not_b(); };
-  fxExport(core,not_b,'not_b');
-
-  Behaviour.prototype.blind_b = function (intervalB) {
-    return changes(this).blind_e(intervalB).hold(this.valueNow());
-  };
-  fxMethodSynonyms(Behaviour,'blind_b','blind');
-
-  var blind_b = function(srcB,intervalB) {
-    return srcB.blind_b(intervalB);
-  };
-  fxExport(core,blind_b,'blind_b');
-
-  Behaviour.prototype.calm_b = function (intervalB) {
-    return this.changes().calm_e(intervalB).hold(this.valueNow());
-  };
-  fxMethodSynonyms(Behaviour,'calm_b','calm');
-
-  var calm_b = function (srcB,intervalB) {
-    return srcB.calm_b(intervalB);
-  };
-  fxExport(core,calm_b,'calm_b');
-
-  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-  // DOM Utilities
-
-	//credit Scott Andrew
-	//usage: addEvent(myDomObj, "mouseover", event->void )
-	//warning: do not use 'this' as meaning depends on browser (myDomObj vs window)
-	//addEvent: Dom * String DomEventEnum * (DomEvent -> a) -> Void
-	var addEvent = function (obj, evType, fn) {
-		//TODO encode mouseleave evt, formalize new evt interface
-
-		if (obj.addEventListener) {
-      obj.addEventListener(evType, fn, false); //TODO true fails on Opera
-      return true;
-		} else if (obj.attachEvent) {
-      //some reason original had code bloat here
-      return obj.attachEvent("on"+evType, fn);
-		} else {
-      return false;
-		}
-	};
-  fxExport(domMisc,addEvent,'addEvent');
-
-  // The Flapjax library does not use this function
-	//credit Dustin Diaz
-	//note: node/tag optional
-	//getElementsByClass: Regexp CSSSelector * Dom * String DomNodeEnum -> Array Dom
-	var getElementsByClass = function (searchClass, node, tag) {
-		var classElements = [];
-		if ( (node === null) || (node === undefined) ) { node = document; }
-		if ( (tag === null) || (tag === undefined) ) { tag = '*'; }
-		var els = node.getElementsByTagName(tag);
-		var elsLen = els.length;
-		var pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
-		for (var i = 0, j = 0; i < elsLen; i++) {
-      if ( pattern.test(els[i].className) ) {
-        classElements.push(els[i]);
-      }
-		}
-		return classElements;
-	};
-  fxExport(domMisc,getElementsByClass,'getElementsByClass','$$');
-
-	//assumes IDs already preserved
-	//swapDom: (Dom a U String) [* (Dom b U String)] -> Dom a
-	var swapDom = function(replaceMe, withMe) {
-		if ((replaceMe === null) || (replaceMe === undefined)) { throw ('swapDom: expected dom node or id, received: ' + replaceMe); } //SAFETY
-
-		var replaceMeD = getObj(replaceMe);
-		if (!(replaceMeD.nodeType > 0)) { throw ('swapDom expected a Dom node as first arg, received ' + replaceMeD); } //SAFETY
-
-		if (withMe) {
-      var withMeD = getObj(withMe);
-      if (!(withMeD.nodeType > 0)) { throw 'swapDom: can only swap with a DOM object'; } //SAFETY
-      try {
-        if(withMeD != replaceMeD) replaceMeD.parentNode.replaceChild(withMeD, replaceMeD);
-      } catch (e) {
-        throw('swapDom error in replace call: withMeD: ' + withMeD + ', replaceMe Parent: ' + replaceMeD + ', ' + e + ', parent: ' + replaceMeD.parentNode);
-      }
-		} else {
-      replaceMeD.parentNode.removeChild(replaceMeD); //TODO isolate child and set innerHTML to "" to avoid psuedo-leaks?
-		}
-		return replaceMeD;
-	};
-  fxExport(domMisc,swapDom,'swapDom');
-
-	//getObj: String U Dom -> Dom
-	//throws
-	//  'getObj: expects a Dom obj or Dom id as first arg'
-	//  'getObj: flapjax: cannot access object'
-	//  'getObj: no obj to get
-	//also known as '$'
-	//TODO Maybe alternative?
-	var getObj = function (name) {
-		if (typeof(name) == 'object') { return name; }
-		else if ((typeof(name) == 'null') || (typeof(name) == 'undefined')) {
-      throw 'getObj: expects a Dom obj or Dom id as first arg';
-		} else {
-
-      var res =
-			document.getElementById ? document.getElementById(name) :
-			document.all ? document.all[name] :
-			document.layers ? document.layers[name] :
-			(function(){ throw 'getObj: flapjax: cannot access object';})();
-      if ((res === null) || (res === undefined)) {
-        throw ('getObj: no obj to get: ' + name);
-      }
-      return res;
-		}
-	};
-  fxExport(domMisc,getObj,'getObj');
-  fxExport(domMisc,getObj,'$');
-
-  //helper to reduce obj look ups
-  //getDynObj: domNode . Array (id) -> domObj
-  //obj * [] ->  obj
-  //obj * ['position'] ->  obj
-  //obj * ['style', 'color'] ->  obj.style
-  var getMostDom = function (domObj, indices) {
-    var acc = getObj(domObj);
-    if ( (indices === null) || (indices === undefined) || (indices.length < 1)) {
-      return acc;
-    } else {
-      for (var i = 0; i < indices.length - 1; i++) {
-        acc = acc[indices[i]];
-      }
-      return acc;
-    }
-  };
-
-  var getDomVal = function (domObj, indices) {
-    var val = getMostDom(domObj, indices);
-    if (indices && indices.length > 0) {
-      val = val[indices[indices.length - 1]];
-    }
-    return val;
-  };
-
-  //TODO: manual timer management stinks.
-  // TODO: Name turn off or somethin
-  var ___timerID = 0;
-  var __getTimerId = function () { return ++___timerID; };
-  var timerDisablers = [];
-
-  var disableTimerNode = function (node) { timerDisablers[node.__timerId](); };
-
-  var disableTimer = function (v) {
-    if (v instanceof Behaviour) {
-      disableTimerNode(v.underlyingRaw);
-    } else if (v instanceof EventStream) {
-      disableTimerNode(v);
-    }
-  };
-  fxExport(dom,disableTimer,'disableTimer');
-
-  var createTimerNodeStatic = function (interval) {
-    var node = internal_e();
-    node.__timerId = __getTimerId();
-    var fn = function () { sendEvent(node, (new Date()).getTime());};
-    var timer = setInterval(fn, interval);
-    timerDisablers[node.__timerId] = function () {clearInterval(timer); };
-    return node;
-  };
-
-  var timer_e = function (interval) {
-    if (interval instanceof Behaviour) {
-      var receiverE = internal_e();
-
-      //the return
-      var res = receiverE.switch_e();
-
-      //keep track of previous timer to disable it
-      var prevE = createTimerNodeStatic(valueNow(interval));
-
-      //init
-      sendEvent(receiverE, prevE);
-
-      //interval changes: collect old timer
-      createNode(
-        [changes(interval)],
-        function (_, p) {
-          disableTimerNode(prevE);
-          prevE = createTimerNodeStatic(p.value);
-          sendEvent(receiverE, prevE);
-        });
-
-      res.__timerId = __getTimerId();
-      timerDisablers[res.__timerId] = function () {
-        disableTimerNode[prevE]();
-        return;
-      };
-
-      return res;
-    } else {
-      return createTimerNodeStatic(interval);
-    }
-  };
-  fxExport(dom,timer_e,'timer_e','asTimer_e');
-
-	d.TagB = function(tagName,args) {
-		this.resE = internal_e();
-		this.currentTag = document.createElement(tagName);
-		this.extractParameters(args);
-		this.insertChildrenNodes();
-		this.styleHooks = [];
-		this.styleChangedE = internal_e();
-		var ctx = this;
-    this.styleChangedE.lift_e(function(_) {
-				var oldTag = ctx.currentTag;
-				ctx.currentTag = document.createElement(tagName);
-        while (oldTag.firstChild) {
-          ctx.currentTag.appendChild(oldTag.removeChild(oldTag.firstChild));
-				}
-				while(ctx.styleHooks.length) removeListener(ctx.styleHooks.pop(),ctx.styleChangedE);
-        ctx.enstyle(ctx.currentTag,ctx.attribs);
-        sendEvent(ctx.resE, ctx.currentTag);
-		});
-		this.enstyle(this.currentTag,this.attribs);
-    this.resB = hold(this.resE, this.currentTag);
-	};
-	d.TagB.prototype = {
-    // Array [[Behaviour] Object *] [[Behaviour] Array] [Behaviour] Dom U String U undefined
-    //   --> {attribs: Array [Behaviour] Object, arrs: Array [Behaviour] Array [Behaviour] Dom }
-    // split an arguments array into:
-    //   1. arrs: (coalesced, and possibly time varying) arrays of dom objects
-    //   2. attribs: attribute objects
-		extractParameters: function(args) {
-			this.arrs = [];
-			var attribs = [];
-
-			var curarr = [];
-			this.arrs.push(curarr);
-			for (var i = 0; i < args.length; i++) {
-				if (args[i] instanceof Behaviour) {
-					var vn = valueNow(args[i]);
-					if (vn instanceof Array) {
-						this.arrs.push(args[i]);
-						curarr = [];
-						this.arrs.push(curarr);
-					} else {
-						if ( ((typeof(vn) == 'object') && (vn.nodeType == 1)) ||
-              (typeof(vn) == 'string') || (vn == undefined)) {
-            curarr.push(args[i]);
-              } else if (typeof(vn) == 'object') {
-                attribs.push(args[i]);
-              }
-              else { throw ('createParameterizedTagB: unknown behaviour argument argument ' + i); } //SAFETY
-					}
-				} else {
-					if (args[i] instanceof Array) {
-						var arr = args[i];
-						for (var j = 0; j < arr.length; j++) { curarr.push(arr[j]); }
-					} else {
-						var vn = args[i];
-						if ( ((typeof(vn) == 'object') && (vn.nodeType == 1)) ||
-              (typeof(vn) == 'string') || (vn == undefined)) {
-						curarr.push(args[i]);
-              } else if (typeof(vn) == 'object') {
-                attribs.push(args[i]);
-              }
-					}
-				}
-			};
-			if(attribs.length > 1) throw ('createParameterizedTagB ' + tagName + ': more than one attribute (' + attribs.length + ')');
-			this.attribs = attribs.length > 0 ? attribs[0] : {};
-		},
-		insertChildrenNodes: function() {
-			var ctx = this;
-
-			function quickNode(e) {
-        if ((typeof(e) == 'object') && (e.nodeType))
-					return e;
-        else if ( e == undefined )
-					return document.createTextNode('');
-        else
-          return document.createTextNode(e);
-      }
-
-			function unBehaviourize(arr) {
-				return map(function(n) {return (n instanceof Behaviour) ? valueNow(n) : n;},arr)
-			}
-
-			var lnodes = map(function() {return [];},this.arrs);
-			var arrLastVals = map(unBehaviourize,unBehaviourize(this.arrs));
-
-			var arrChangesE = internal_e();
-			var nodeChangesE = internal_e();
-
-			function attachNodes(i,arr) {
-				for(var j=0;j<arr.length;j++) {
-					var cnode = arr[j];
-					if(cnode instanceof Behaviour) {
-						var newnode = (function(jj) {
-                return changes(cnode).lift_e(function(n) {return {index:i,jdex:jj,val:n};});
-						})(j);
-						lnodes[i].push(newnode);
-						attachListener(newnode,nodeChangesE);
-						cnode = valueNow(cnode);
-					}
-				}
-			}
-
-			var childChangesE = merge_e(
-				// Behaviour arrays change
-				arrChangesE.lift_e(function(ai) {
-            var i = ai.index;
-            var newarr = ai.val;
-            while(lnodes[i].length) {
-              var ln = lnodes[i].pop();
-              removeListener(ln,nodeChangesE);
-            }
-            var newvals = map(function(n) {return quickNode(n);},unBehaviourize(newarr));
-            for(var j=0;j<arrLastVals[i].length;j++)
-				try {
-					ctx.currentTag.removeChild(arrLastVals[i][j]);
-				}
-				catch(e) {}
-            if(newvals.length) {
-              var nextNode = null;
-              for(ii = i+1; ii < arrLastVals.length && !(arrLastVals[ii].length) ; ii++);
-              if(ii < arrLastVals.length) nextNode = arrLastVals[ii][0];
-              for(var j=0; j<newvals.length; j++) ctx.currentTag.insertBefore(newvals[j],nextNode);
-            }
-            arrLastVals[i] = newvals;
-            attachNodes(i,newarr);
-            return ctx.currentTag;
-				}),
-				// Behaviour nodes change
-				nodeChangesE.lift_e(function(ni) {
-            var i = ni.index;
-            var j = ni.jdex;
-            var newnode = quickNode(ni.val);
-            swapDom(arrLastVals[i][j],newnode);
-            arrLastVals[i][j] = newnode;
-            return ctx.currentTag;
-				}));
-			childChangesE.lift_e(function(cc) {sendEvent(ctx.resE,cc);});
-
-			for(var i=0; i<this.arrs.length;i++) {
-				for(var j=0; j<arrLastVals[i].length; j++) {
-					arrLastVals[i][j] = quickNode(arrLastVals[i][j]);
-					this.currentTag.appendChild(arrLastVals[i][j]);
-				}
-				if(this.arrs[i] instanceof Behaviour) {
-					attachNodes(i,valueNow(this.arrs[i]));
-					var newnode = (function(ii) {return changes(ctx.arrs[ii]).lift_e(function(na) {return {index:ii,val:na};});})(i);
-					attachListener(newnode,arrChangesE);
-				}
-				else {
-					attachNodes(i,this.arrs[i]);
-				}
-			}
-		},
-		enstyle: function(obj,vals) {
-			//build & record hook if dynamic collection
-			if (vals instanceof Behaviour) {
-				if (!(typeof(valueNow(vals)) == 'object')) { throw 'enstyle: expected object literal as behaviour value'; } //SAFETY
-				this.styleHooks.push(changes(vals));
-				attachListener(changes(vals),this.styleChangedE);
-			}
-			var valsV = vals instanceof Behaviour ? valueNow(vals) : vals;
-			if (typeof(valsV) == 'object') {
-				for (var i in valsV) {
-					if (!(Object.prototype) || !(Object.prototype[i])) {
-						this.enstyleProperty(obj,valsV, i);
-					}
-				}
-			}
-			else { throw 'enstyle: expected object literals'; } //SAFETY
-		},
-    enstyleProperty: function (obj, vals, i) {
-      if (vals[i] instanceof Behaviour) {
-        if (typeof(valueNow(vals[i])) == 'object') {
-          this.enstyle(obj[i], vals[i]);
-        }
-				else {
-          obj[i] = valueNow(vals[i]);
-					changes(vals[i]).lift_e(function(v) {obj[i] = v;});
-				}
-			}
-			else {
-        if (typeof(vals[i]) == 'object') {
-          this.enstyle(obj[i], vals[i]);
-        } else {
-          obj[i] = vals[i];
-        }
-      }
-    }
-	};
-
-	d.createParameterizedTagB = function(tagName) {
-		return new d.TagB(tagName,slice(arguments,1)).resB;
-	};
-
-  d.enstyleStaticProperty = function (obj, props, index) {
-    if (typeof(props[index]) == 'object') {
-      for (var i in props[index]) {
-        if (!(Object.prototype) || !(Object.prototype[i])) {
-          d.enstyleStaticProperty(obj[index], props[index], i);
-        }
-      }
-    } else {
-      obj[index] = props[index];
-			if (index == 'checked')	obj['defaultChecked'] = props[index]; /* TODO: this should maybe be elsewhere? */
-			if (index == 'selected') obj['defaultSelected'] = props[index]; /* TODO: this should maybe be elsewhere? */
-    }
-  };
-
-  d.staticTagMaker = function (tagName) {
-
-    return function () {
-
-      var tagD = document.createElement(tagName);
-      if (!(tagD.nodeType > 0)) { throw (tagName + ': invalid tag name'); } //SAFETY
-
-      //partition input
-
-      //          if (arguments[1] === null || arguments[1] === undefined) { arguments[1] = {}; }
-      var attribs = [];
-      for (var i = 0; i < arguments.length; i++) {
-        if (arguments[i] instanceof Array) {
-          for (var j = 0; j < arguments[i].length; j++) {
-            if (arguments[i][j]) {
-              tagD.appendChild(
-                (typeof(arguments[i][j]) == 'object' &&
-                  arguments[i][j].nodeType > 0)?
-                arguments[i][j] :
-                document.createTextNode(arguments[i][j]));
-            }
-          }
-        } else if (!arguments[i]) {
-          //ignore
-        } else if ((typeof(arguments[i]) == 'object') &&
-          (arguments[i].nodeType > 0)) {
-        tagD.appendChild(arguments[i]);
-          } else if (typeof(arguments[i]) == 'object') {
-            attribs.push(arguments[i]);
-          } else {
-            tagD.appendChild(document.createTextNode(arguments[i]));
-          }
-      }
-
-      if (attribs.length == 1) {
-        for (var k in attribs[0]) {
-          if (!(Object.prototype) || !(Object.prototype[k])) {
-            d.enstyleStaticProperty(tagD, attribs[0], k);
-          }
-        }
-      } else if (attribs.length >  0) {
-        throw 'static enstyle: expected object literals'; //SAFETY
-      } /* else {
-      alert('no attribs on: ' + tagName);
-      } */
-
-
-      return tagD;
-    };
-  };
-
-
-
-  var generatedTags =
-  [ "a", "b", "blockquote", "br", "button", "canvas", "div", "fieldset",
-  "form", "font", "h1", "h2", "h3", "h4", "hr", "img", "iframe", "input",
-  "label", "legend", "li", "ol", "optgroup", "option",
-  "p", "pre", "select", "span", "strong", "table", "tbody",
-  "td", "textarea", "tfoot", "th", "thead", "tr", "tt", "ul" ];
-
-  map(
-    function (tagName) {
-
-      var upper = tagName.toUpperCase();
-
-      //d.<TAG>B
-      d[upper + 'B'] = function () {
-        return d.createParameterizedTagB.apply(this, [tagName].concat(slice(arguments,0)));
-      };
-			annotate(d[upper+'B'],[upper+'B']);
-
-      //d.<TAG>
-      d[upper] = d.staticTagMaker(tagName);  //faster, simple
-      /*              function () {
-      // 8/16/06 - leo - arguments bug
-      // more forgiving: allows reactive children (just doesn't propagate them)
-      var args = [b.maybeEmpty].concat(slice(arguments, 0));
-      return valueNow(d[upper + 'B'].apply(this, args));
-      }; */
-			annotate(d[upper],[upper]);
-    },
-    generatedTags);
-
-  //TEXTB: Behaviour a -> Behaviour Dom TextNode
-  d.TEXTB = function (strB) {
-    //      if (!(strB instanceof Behaviour || typeof(strB) == 'string')) { throw 'TEXTB: expected Behaviour as second arg'; } //SAFETY
-    if (!(strB instanceof Behaviour)) { strB = constant_b(strB); }
-
-    return hold(
-      changes(strB).lift_e(
-        function (txt) { return document.createTextNode(txt); }),
-      document.createTextNode(valueNow(strB)));
-  };
-	annotate(d.TEXTB,['TEXTB']);
-
-  var TEXT = function (str) {
-    return document.createTextNode(str);
-  };
-  fxExport(dom,TEXT,'TEXT');
-
-  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-  // Reactive DOM
-
-  //tagRec: Array (EventName a) *
-  //      ( .Array (Event a) * Array (Event a) -> Behaviour Dom) -> Behaviour Dom
-  var tagRec = function (eventNames, maker) {
-    if (!(eventNames instanceof Array)) { throw 'tagRec: expected array of event names as first arg'; } //SAFETY
-    if (!(maker instanceof Function)) { throw 'tagRec: expected function as second arg'; } //SAFETY
-
-    var numEvents = eventNames.length;
-    var internals = [ ];
-    var switches = [ ];
-    for (var i = 0; i < numEvents; i++) {
-      internals[i] = internal_e();
-      switches[i] = internals[i].switch_e();
-    };
-
-    var domB = maker.apply(this,switches);
-
-    var prevValue;
-
-    var interceptE = createNode([domB.changes()],function (_,p) {
-      if (isEqual(p.value,prevValue)) { return; }
-
-      prevValue = p.value;
-      for (var i = 0; i < numEvents; i++) {
-        sendEvent(internals[i],extractEvent_e(prevValue,eventNames[i]));
-      };
-    });
-
-    sendEvent(interceptE,domB.valueNow());
-
-    return domB;
-  };
-  fxExport(dom,tagRec,'tagRec','nodeWithOwnEvents');
-
-  //extractEventStatic_e: Dom * String -> Event
-  var extractEventStatic_e = function (domObj, eventName) {
-    if (!eventName) { throw 'extractEvent_e : no event name specified'; }
-    if (!domObj) { throw 'extractEvent_e : no DOM element specified'; }
-
-    domObj = getObj(domObj);
-
-    var primEventE = internal_e();
-    addEvent(domObj,eventName,function(evt) {
-       sendEvent(primEventE, evt || window.event);
-       // Important for IE; false would prevent things like a checkbox actually
-       // checking.
-       return true;
-    });
-
-    return primEventE;
-  };
-
-  //extractEvent_e: [Behaviour] Dom * String -> Event
-  var extractEvent_e = function (domB, eventName) {
-    if (!(domB instanceof Behaviour)) {
-      return extractEventStatic_e(domB,eventName);
-    }
-    else {
-      var domE = domB.changes();
-
-      var eventEE = domE.lift_e(function(dom) {
-        return extractEventStatic_e(dom,eventName);
-      });
-
-      var resultE = eventEE.switch_e();
-
-      sendEvent(domE,domB.valueNow());
-
-      return resultE;
-    };
-  };
-  fxExport(dom,extractEvent_e,'extractEvent_e','$EVT');
-
-  //extractEvents_e:
-  //      [Behaviour] Dom
-  //      . Array String
-  //      -> Event
-  // ex: extractEvents_e(m, 'body', 'mouseover', 'mouseout')
-  d.extractEvents_e = function (domObj /* . eventNames */) {
-    var eventNames = slice(arguments, 1);
-
-    var events = map(
-      function (eventName) {
-        return extractEvent_e(domObj, eventName);
-      },
-      eventNames.length === 0 ? [] : eventNames);
-
-    return merge_e.apply(this, events);
-  };
-  annotate(d.extractEvents_e,['extractEvents_e'],0,[Behaviour.prototype],['extractEvents_e']);
-
-  //value of dom form object during trigger
-  d.extractValueOnEvent_e = function (triggerE, domObj) {
-    if (!(triggerE instanceof EventStream)) { throw 'extractValueOnEvent_e: expected Event as first arg'; } //SAFETY
-
-    return changes(d.extractValueOnEvent_b.apply(this, arguments));
-
-  };
-  annotate(d.extractValueOnEvent_e,['extractValueOnEvent_e']);
-
-  //extractDomFieldOnEvent_e: Event * Dom U String . Array String -> Event a
-  d.extractDomFieldOnEvent_e = function (triggerE, domObj /* . indices */) {
-    if (!(triggerE instanceof EventStream)) { throw 'extractDomFieldOnEvent_e: expected Event as first arg'; } //SAFETY
-    var indices = slice(arguments, 2);
-    var res =
-    triggerE.lift_e(
-      function () { return getDomVal(domObj, indices); });
-    return res;
-  };
-
-  d.extractValue_e = function (domObj) {
-    return changes(d.extractValue_b.apply(this, arguments));
-  };
-  annotate(d.extractValue_e,['extractValue_e','$E']);
-
-  //extractValueOnEvent_b: Event * DOM -> Behaviour
-  // value of a dom form object, polled during trigger
-  d.extractValueOnEvent_b = function (triggerE, domObj) {
-    return d.extractValueStatic_b(domObj, triggerE);
-  };
-  annotate(d.extractValueOnEvent_b,['extractValueOnEvent_b']);
-
-  //extractValueStatic_b: DOM [ * Event ] -> Behaviour a
-  //If no trigger for extraction is specified, guess one
-  d.extractValueStatic_b = function (domObj, triggerE) {
-
-    var objD;
-    try {
-      objD = getObj(domObj);
-	    //This is for IE
-	    if(typeof(domObj) == 'string' && objD.id != domObj) {
-	    	throw 'Make a radio group';
-	    }
-    } catch (e) {
-      objD = {type: 'radio-group', name: domObj};
-    }
-
-    var getter; // get value at any current point in time
-
-
-    switch (objD.type)  {
-
-      //TODO: checkbox.value instead of status?
-    case 'checkbox':
-
-      return hold(
-        filterRepeats_e(
-          d.extractDomFieldOnEvent_e(
-            triggerE ? triggerE :
-            d.extractEvents_e(
-              objD,
-              'click', 'keyup', 'change'),
-            objD,
-            'checked'),objD.checked),
-        objD.checked);
-
-      case 'select-one':
-
-        getter = function (_) {
-          return objD.selectedIndex > -1 ?
-          (objD.options[objD.selectedIndex].value ?
-            objD.options[objD.selectedIndex].value :
-            objD.options[objD.selectedIndex].innerText)
-			    : undefined;
-        };
-
-        return hold(
-          filterRepeats_e(
-              (triggerE ? triggerE :
-              d.extractEvents_e(
-                objD,
-                'click', 'keyup', 'change')).lift_e(getter)),getter(),
-          getter());
-
-        case 'select-multiple':
-          //TODO ryan's cfilter adapted for equality check
-
-          getter = function (_) {
-            var res = [];
-            for (var i = 0; i < objD.options.length; i++) {
-              if (objD.options[i].selected) {
-                res.push(objD.options[i].value ? objD.options[i].value : objD.options[i].innerText);
-              }
-            }
-            return res;
-          };
-
-
-          return hold(
-              (triggerE ? triggerE :
-              d.extractEvents_e(
-                objD,
-                'click', 'keyup', 'change')).lift_e(getter),
-            getter());
-
-          case 'text':
-          case 'textarea':
-          case 'hidden':
-          case 'password':
-
-            return hold(
-              filterRepeats_e(
-                d.extractDomFieldOnEvent_e(
-                  triggerE ? triggerE :
-                  d.extractEvents_e(
-                    objD,
-                    'click', 'keyup', 'change'),
-                  objD,
-                  'value'),objD.value),
-              objD.value);
-
-            case 'button': //same as above, but don't filter repeats
-
-              return hold(
-                d.extractDomFieldOnEvent_e(
-                  triggerE ? triggerE :
-                  d.extractEvents_e(
-                    objD,
-                    'click', 'keyup', 'change'),
-                  objD,
-                  'value'),
-                objD.value);
-
-
-              case 'radio':
-              case 'radio-group':
-
-                //TODO returns value of selected button, but if none specified,
-                //      returns 'on', which is ambiguous. could return index,
-                //      but that is probably more annoying
-
-                var radiosAD = filter(
-                  function (elt) {
-                    return (elt.type == 'radio') &&
-                    (elt.getAttribute('name') == objD.name);
-                  },
-                  document.getElementsByTagName('input'));
-
-                getter =
-                objD.type == 'radio' ?
-
-                function (_) {
-                  return objD.checked;
-                } :
-
-                function (_) {
-                  for (var i = 0; i < radiosAD.length; i++) {
-                    if (radiosAD[i].checked) {
-                      return radiosAD[i].value;
-                    }
-                  }
-                  return undefined; //TODO throw exn?
-                };
-
-                var actualTriggerE = triggerE ? triggerE :
-                merge_e.apply(
-                  this,
-                  map(
-                    function (radio) {
-                      return d.extractEvents_e(
-                        radio,
-                    'click', 'keyup', 'change'); },
-                      radiosAD));
-
-                return hold(
-                  filterRepeats_e(
-                      actualTriggerE.lift_e(getter),getter()),
-                  getter());
-
-                default:
-
-                  throw ('extractValueStatic_b: unknown value type "' + objD.type + '"');
-    }
-  };
-
-  d.extractValue_b = function (domObj) {
-    if (domObj instanceof Behaviour) {
-      return lift_b(function (dom) { return d.extractValueStatic_b(dom); },
-                    domObj)
-             .switch_b();
-    } else {
-      return d.extractValueStatic_b(domObj);
-    }
-  };
-	annotate(d.extractValue_b,['extractValue_b','$B'],0,[Behaviour.prototype],['extractValue_b','$B']);
-
-  //into[index] = deepValueNow(from) via descending from object and mutating each field
-  d.deepStaticUpdate = function (into, from, index) {
-    var fV = (from instanceof Behaviour)? valueNow(from) : from;
-    if (typeof(fV) == 'object') {
-      for (var i in fV) {
-        if (!(Object.prototype) || !(Object.prototype[i])) {
-          d.deepStaticUpdate(index? into[index] : into, fV[i], i);
-        }
-      }
-    } else {
-      var old = into[index];
-      into[index] = fV;
-    }
-  };
-
-  //note: no object may be time varying, just the fields
-  //into[index] = from
-  //only updates on changes
-  d.deepDynamicUpdate = function (into, from, index) {
-    var fV = (from instanceof Behaviour)? valueNow(from) : from;
-    if (typeof(fV) == 'object') {
-      if (from instanceof Behaviour) {
-        throw 'deepDynamicUpdate: dynamic collections not supported';
-      }
-      for (var i in fV) {
-        if (!(Object.prototype) || !(Object.prototype[i])) {
-          d.deepDynamicUpdate(index? into[index] : into, fV[i], i);
-        }
-      }
-    } else {
-      if (from instanceof Behaviour) {
-        createNode(
-          [changes(from)],
-          function (s, p) {
-            if (index) {
-              var old = into[index];
-              into[index] = p.value;
-            }
-            else { into = p.value; } //TODO notify topE?
-          });
-      }
-    }
-  };
-
-
-  d.insertValue = function (val, domObj /* . indices */) {
-    var indices = slice(arguments, 2);
-    var parent = getMostDom(domObj, indices);
-    d.deepStaticUpdate(parent, val, indices ? indices[indices.length - 1] : undefined);
-  };
-  annotate(d.insertValue,['insertValue']);
-
-  //TODO convenience method (default to firstChild nodeValue)
-  d.insertValueE = function (triggerE, domObj /* . indices */) {
-    if (!(triggerE instanceof EventStream)) { throw 'insertValueE: expected Event as first arg'; } //SAFETY
-
-    var indices = slice(arguments, 2);
-    var parent = getMostDom(domObj, indices);
-
-      triggerE.lift_e(function (v) {
-        d.deepStaticUpdate(parent, v, indices? indices[indices.length - 1] : undefined);
-      });
-  };
-  annotate(d.insertValueE,['insertValueE'],0,[EventStream.prototype],['insertValueE']);
-
-  //insertValueB: Behaviour * domeNode . Array (id) -> void
-  //TODO notify adapter of initial state change?
-  d.insertValueB = function (triggerB, domObj /* . indices */) {
-
-    var indices = slice(arguments, 2);
-    var parent = getMostDom(domObj, indices);
-
-
-    //NOW
-    d.deepStaticUpdate(parent, triggerB, indices ? indices[indices.length - 1] : undefined);
-
-    //LATER
-    d.deepDynamicUpdate(parent, triggerB, indices? indices[indices.length -1] : undefined);
-
-  };
-  annotate(d.insertValueB,['insertValueB'],0,[Behaviour.prototype],['insertValueB']);
-
-  //TODO copy dom event call backs of original to new? i don't thinks so
-  //  complication though: registration of call backs should be scoped
-  d.insertDomE = function (triggerE, domObj) {
-
-    if (!(triggerE instanceof EventStream)) { throw 'insertDomE: expected Event as first arg'; } //SAFETY
-
-    var objD = getObj(domObj);
-
-    var res = triggerE.lift_e(
-      function (newObj) {
-        //TODO safer check
-        if (!((typeof(newObj) == 'object') && (newObj.nodeType == 1))) {
-          newObj = d.SPAN({}, newObj);
-        }
-        swapDom(objD, newObj);
-        objD = newObj;
-        return newObj; // newObj;
-      });
-
-    return res;
-  };
-  annotate(d.insertDomE,['insertDomE'],0,[EventStream.prototype],['insertDomE']);
-
-  //insertDom: dom
-  //          * dom
-  //          [* (null | undefined | 'over' | 'before' | 'after' | 'leftMost' | 'rightMost' | 'beginning' | 'end']
-  //          -> void
-  // TODO: for consistency, switch replaceWithD, hookD argument order
-  d.insertDom = function (hookD, replaceWithD, optPosition) {
-    switch (optPosition)
-    {
-    case undefined:
-    case null:
-    case 'over':
-      swapDom(hookD,replaceWithD);
-      break;
-    case 'before':
-      hookD.parentNode.insertBefore(replaceWithD, hookD);
-      break;
-    case 'after':
-      if (hookD.nextSibling) {
-        hookD.parentNode.insertBefore(replaceWithD, hookD.nextSibling);
-      } else {
-        hookD.parentNode.appendChild(replaceWithD);
-      }
-      break;
-    case 'leftMost':
-      if (hookD.parentNode.firstChild) {
-        hookD.parentNode.insertBefore(
-          replaceWithD,
-          hookD.parentNode.firstChild);
-                } else { hookD.parentNode.appendChild(replaceWithD); }
-                break;
-              case 'rightMost':
-                hookD.parentNode.appendChild(replaceWithD);
-                break;
-              case 'beginning':
-                if (hookD.firstChild) {
-                  hookD.insertBefore(
-                    replaceWithD,
-                    hookD.firstChild);
-                } else { hookD.appendChild(replaceWithD); }
-                break;
-              case 'end':
-                hookD.appendChild(replaceWithD);
-                break;
-              default:
-                throw ('domInsert: unknown position: ' + optPosition);
-    }
-  };
-
-  //insertDom: dom
-  //          * dom U String domID
-  //          [* (null | undefined | 'over' | 'before' | 'after' | 'leftMost' | 'rightMost' | 'beginning' | 'end']
-  //          -> void
-  d.insertDomClean = function (replaceWithD, hook, optPosition) {
-    //TODO span of textnode instead of textnode?
-    d.insertDom(
-	    getObj(hook),
-	    ((typeof(replaceWithD) == 'object') && (replaceWithD.nodeType > 0)) ? replaceWithD :
-      document.createTextNode(replaceWithD),
-	    optPosition);
-  };
-  annotate(d.insertDomClean,['insertDom']);
-
-  //TODO test
-  //insertDomB:
-  //      [Behaviour] String U Dom
-  //      [* ( id U null U undefined )
-  //          [* ('before' U 'after' U 'leftMost' U 'rightMost' U 'over' U 'beginning' U 'end')]]
-  //      -> Behaviour a
-  //if optID not specified, id must be set in init val of trigger
-  //if position is not specified, default to 'over'
-  //performs initial swap onload
-  d.insertDomB = function (initTriggerB, optID, optPosition) {
-
-    if (!(initTriggerB instanceof Behaviour)) {
-      initTriggerB = constant_b(initTriggerB);
-    }
-
-    var triggerB =
-    lift_b(
-      function (d) {
-        if ((typeof(d) == 'object') && (d.nodeType >  0)) {
-          return d;
-        } else {
-          var res = document.createElement('span'); //TODO createText instead
-          res.appendChild(document.createTextNode(d));
-          return res;
-        }
-      },
-      initTriggerB);
-
-    var initD = valueNow(triggerB);
-    if (!((typeof(initD) == 'object') && (initD.nodeType == 1))) { throw ('insertDomB: initial value conversion failed: ' + initD); } //SAFETY
-
-    d.insertDom(
-      optID === null || optID === undefined ? getObj(initD.getAttribute('id')) : getObj(optID),
-      initD,
-      optPosition);
-
-    var resB = hold(
-      d.insertDomE(
-        changes(triggerB),
-        initD),
-      initD);
-
-    return resB;
-  };
-  annotate(d.insertDomB,['insertDomB'],0,[Behaviour.prototype],['insertDomB']);
-
-
-  d.extractId_b = function (id, start)
-  {
-    return hold(
-      createNode( start instanceof Behaviour? [changes(start)] :
-        [],
-        function (s, p) {
-          p.value = getObj(id);
-          s(p);
-        }),
-      getObj(id));
-  };
-  annotate(d.insertDomB,['extractId_b'],1,[Behaviour.prototype],['extractId_b']);
-
-  var mouse_e = function(elem) {
-    return extractEvent_e(elem,'mousemove')
-    .lift_e(function(evt) {
-        if (evt.pageX | evt.pageY) {
-          return { left: evt.pageX, top: evt.pageY };
-        }
-        else if (evt.clientX || evt.clientY) {
-          return { left : evt.clientX + document.body.scrollLeft,
-                   top: evt.clientY + document.body.scrollTop };
-        }
-        else {
-          return { left: 0, top: 0 };
-        }
-    });
-  };
-
-  var mouse_b = function(elem) {
-    return mouse_e(elem).hold({ left: 0, top: 0 });
-  }
-  fxExport(dom,mouse_b,'mouse_b');
-
-  var mouseLeft_b = function(elem) {
-    return lift_b(function(v) { return v.left; },mouse_b(elem));
-  };
-  fxExport(dom,mouseLeft_b,'mouseLeft_b');
-
-  var mouseTop_b = function(elem) {
-    return mouse_b(elem).lift_b(function(v) { return v.top; });
-  };
-
-  fxExport(dom,mouseTop_b,'mouseTop_b');
-
-  var clicks_e = function(elem) {
-    return extractEvent_e(elem,'click');
-  };
-  fxExport(dom,clicks_e,'clicks_e');
-
-  //////////////////////////////////////////////////////////////////////////////
-  // Combinators for web services
-
-
-	//credit Matt White
-	var getURLParam = function (param) {
-    var lparam = param.toLowerCase();
-    var aReturn = [];
-    var strHref = window.location.href;
-    var endstr = (strHref.indexOf('#') > -1) ? strHref.indexOf('#') : strHref.length;
-    if ( strHref.indexOf("?") > -1 ) {
-      var strQueryString = strHref.substring(strHref.indexOf("?")+1,endstr);
-      map(function(qp) {
-          var eq = qp.indexOf('=');
-          var qname = qp.substr(0,eq+1).toLowerCase();
-          if(qname == lparam+"=") aReturn.push(decodeURIComponent(qp.substr(eq+1)));
-      },strQueryString.split("&"));
-    }
-    if (aReturn.length == 0) return undefined;
-    else if(aReturn.length == 1) return aReturn[0];
-    else return aReturn;
-	};
-  fxExport(domMisc,getURLParam,'getURLParam','$URL');
-
-	//credit Quirksmode
-	//readCookie: String -> String U Undefined
-	var readCookie = function (name) {
-    var nameEQ = name + "=";
-    var ca = document.cookie.split(';');
-    for (var i=0; i < ca.length; i++) {
-      var co = ca[i];
-      while (co.charAt(0) == ' ') { co = co.substring(1, co.length); }
-      if (co.indexOf(nameEQ) === 0) {
-        return co.substring(nameEQ.length, co.length);
-      }
-    }
-    return undefined;
-	};
-  fxExport(domMisc,readCookie,'readCookie');
-
-  //========== dynamic scripts ==========
-  var scriptCounter = 0;
-  var deleteScript = function (scriptID) {
-    var scriptD = getObj(scriptID);
-    scriptD.parentNode.removeChild(scriptD); //TODO isolate child and set innerHTML to "" to avoid psuedo-leaks?
-  };
-
-  // optional fn/param that gets polled until parm is defined
-  var runScript = function (url, fn, param) {
-    var script = document.createElement("script");
-    script.src = url;
-    var scriptID = 'scriptFnRPC' + scriptCounter++;
-    script.setAttribute('id', scriptID);
-    document.getElementsByTagName("head").item(0).appendChild(script);
-    var timer = {};
-    var check =
-    function () {
-      eval("try { if (" + param + "!== undefined) {var stat = " + param + ";}} catch (e) {}");
-      if (stat !== undefined) {
-        eval(param + " = undefined;");
-        clearInterval(timer.timer);
-        clearInterval(timer.timeout);
-        if (fn instanceof Function) {
-          fn(stat);
-        }
-        deleteScript(scriptID);
-      }
-    };
-    timer.timer = setInterval(check, 3500);
-    timer.timeout =
-    setTimeout(
-      function () {
-        try { clearInterval(timer.timer); }
-        catch (e) {}
-      },
-      5000); //TODO make parameter?
-  };
-
-  // Node {url, globalArg} -> Node a
-  //load script @ url and poll until param is set, then pass it along
-  var evalForeignScriptVal_e = function(urlArg_e) {
-    var result = receiver_e();
-    urlArg_e.lift_e(function(urlArg) {
-        runScript(urlArg.url,
-          function(val) { result.sendEvent(val); },
-          urlArg.globalArg);
-    });
-
-    return result;
-  };
-  fxExport(ws,evalForeignScriptVal_e,'evalForeignScriptVal_e');
-
-  var ajaxRequest = function(method,url,body,async,useFlash,callback) {
-    var xhr;
-    if (useFlash) {
-      xhr = new FlashXMLHttpRequest();
-      xhr.onload = function() { callback(xhr); };
-    }
-    else if (window.XMLHttpRequest && !(window.ActiveXObject)) {
-      xhr = new window.XMLHttpRequest();
-      xhr.onload = function() { callback(xhr); };
-    }
-    else if (window.ActiveXObject) {
-      try { xhr = new ActiveXObject("Msxml2.XMLHTTP"); }
-      catch(e) { xhr = new ActiveXObject("Microsoft.XMLHTTP"); }
-
-      xhr.onreadystatechange = function() {
-        if (xhr.readyState == 4) { callback(xhr); }
-      };
-    };
-
-    xhr.open(method,url,async);
-
-    if (method == 'POST') {
-      xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
-    }
-
-    xhr.send(body);
-    return xhr;
-  };
-
-  var encodeREST = function(obj) {
-    var str = "";
-    for (var field in obj) {
-      if (typeof(obj[field]) !== 'function') { // skips functions in the object
-        if (str != '') { str += '&'; }
-        str += field + '=' + encodeURIComponent(obj[field]);
-      }
-    }
-    return str;
-  };
-
-  // From json.org
-  var parseJSON = function(str) {
-    try {
-        return !(/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test(
-                str.replace(/"(\\.|[^"\\])*"/g, ''))) &&
-            eval('(' + str + ')');
-    } catch (e) {
-        throw 'cannot parse JSON string: ' + e;
-    }
-  };
-
-  var toJSONString = (function() {
-    var m = {
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        };
-    var s = {
-      array: function (x) {
-        var a = ['['], b, f, i, l = x.length, v;
-        for (i = 0; i < l; i += 1) {
-          v = x[i];
-          f = s[typeof v];
-          if (f) {
-            v = f(v);
-            if (typeof v == 'string') {
-              if (b) {
-                a[a.length] = ',';
-              }
-              a[a.length] = v;
-              b = true;
-            }
-          }
-        }
-        a[a.length] = ']';
-        return a.join('');
-      },
-      'boolean': function (x) {
-        return String(x);
-      },
-      'null': function (x) {
-        return "null";
-      },
-      number: function (x) {
-        return isFinite(x) ? String(x) : 'null';
-      },
-      object: function (x) {
-        if (x) {
-          if (x instanceof Array) {
-            return s.array(x);
-          }
-          var a = ['{'], b, f, i, v;
-          for (i in x) {
-            v = x[i];
-            f = s[typeof v];
-            if (f) {
-              v = f(v);
-              if (typeof v == 'string') {
-                if (b) {
-                  a[a.length] = ',';
-                }
-                a.push(s.string(i), ':', v);
-                b = true;
-              }
-            }
-          }
-          a[a.length] = '}';
-          return a.join('');
-        }
-        return 'null';
-      },
-      string: function (x) {
-        if (/["\\\x00-\x1f]/.test(x)) {
-          x = x.replace(/([\x00-\x1f\\"])/g, function(a, b) {
-              var c = m[b];
-              if (c) {
-                return c;
-              }
-              c = b.charCodeAt();
-              return '\\u00' +
-              Math.floor(c / 16).toString(16) +
-              (c % 16).toString(16);
-          });
-        }
-        return '"' + x + '"';
-      }
-    };
-    return function(val) {
-      var f = s[typeof val];
-      if (f) { return f(val); }
-      else { throw 'parseJSON: unknown object type: ' + (typeof val); }
-    };
-  })();
-
-  var serverRequest_e = function(useFlash,requestE) {
-    var response_e = receiver_e();
-
-    requestE.lift_e(function (obj) {
-        var body = '';
-        var method = 'GET';
-        var url = obj.url;
-
-        var reqType = obj.request ? obj.request : (obj.fields ? 'post' : 'get');
-        if (obj.request == 'get') {
-          url += "?" + encodeREST(obj.fields);
-          body = '';
-          method = 'GET';
-        } else if (obj.request == 'post') {
-          body = toJSONString(obj.fields);
-          method = 'POST';
-        } else if (obj.request == 'rawPost') {
-          body = obj.body;
-          method = 'POST';
-        }
-        else if (obj.request == 'rest') {
-          body = encodeREST(obj.fields);
-          method = 'POST';
-        }
-        else {
-          raise ("Invalid request type: " + obj.request);
-        }
-
-        var async = obj.async;
-
-        var xhr;
-
-        // Branch on the response type to determine how to parse it
-        if (obj.response == 'json') {
-          xhr = ajaxRequest(method,url,body,async,useFlash,
-            function(xhr) {
-              response_e.sendEvent(parseJSON(xhr.responseText));
-            });
-        }
-        else if (obj.response == 'xml') {
-          ajaxRequest(method,url,body,async,useFlash,
-            function(xhr) {
-              response_e.sendEvent(xhr.responseXML);
-            });
-        }
-        else if (obj.response == 'plain' || !obj.response) {
-          ajaxRequest(method,url,body,async,useFlash,
-            function(xhr) {
-              response_e.sendEvent(xhr.responseText);
-          });
-        }
-        else {
-          raise('Unknown response format: ' + obj.response);
-        }
-    });
-
-    return response_e;
-  };
-
-  var getWebServiceObject_e = function(requestE) {
-    return serverRequest_e(false,requestE);
-  };
-  fxExport(ws,getWebServiceObject_e,'getWebServiceObject_e');
-
-  var getForeignWebServiceObject_e = function(requestE) {
-    return serverRequest_e(true,requestE);
-  };
-  fxExport(ws,getForeignWebServiceObject_e,'getForeignWebServiceObject_e');
-
-  var cumulativeOffset = function(element) {
-    var valueT = 0, valueL = 0;
-    do {
-      valueT += element.offsetTop  || 0;
-      valueL += element.offsetLeft || 0;
-      element = element.offsetParent;
-    } while (element);
-    return { left: valueL, top: valueT };
- };
-fxExport(domMisc,cumulativeOffset,'cumulativeOffset');
-
-  ///////// OPTIONALLY EXPORT PUBLIC FUNCTIONS TO GLOBAL NAMESPACE
-  if (!options.hideAll) {
-    for (var zz in flapjax.pub) {
-      if (!options[zz]) {
-        window[zz] = flapjax.pub[zz]; // use eval instead so as to achieve Flash compatibility
-      }
-    }
-  }
-
-  for (var id in exports) {
-    if (window[id]) {
-      if (options.redefine === false) {
-        warn('window.' + id + ' is already defined; Flapjax will not ' +
-             'redefine it.');
-      }
-      else {
-        warn('Flapjax is redefining window.' + id + '.');
-      }
-    }
-
-    if (window[id] && options.redefine === false) {
-      // Don't redefine id
-    }
-    else if (options.show && options.hide && options.show[id] === true &&
-        options.hide[id] === true) {
-      throw('flapjaxInit: invalid options: ' + id + " specified as 'show' and 'hide'");
-    }
-    else if (options.show && options.show[id]) {
-      window[id] = exports[id];
-    }
-    else if (options.hide && options.hide[id]) {
-      // do nothing -- don't export
-    }
-    // Default behavior--check if it is a synonym first ...
-    else if (!synonyms[id] || options.includeSynonyms) {
-      // ... then export if its of the right category
-      if (options.exportMisc && exports[id].__flapjaxCategory === misc) {
-        window[id] = exports[id];
-      }
-      else if (options.exportDOMMisc && exports[id].__flapjaxCategory === domMisc) {
-        window[id] = exports[id];
-      }
-      else if (options.exportDOM && exports[id].__flapjaxCategory === dom) {
-        window[id] = exports[id];
-      }
-      else if (options.exportCore && exports[id].__flapjaxCategory === core) {
-        window[id] = exports[id];
-      }
-      else if (options.exportWS && exports[id].__flapjaxCategory === ws) {
-        window[id] = exports[id];
-      }
-    }
-  }
-
-  return exports;
-}
diff --git a/tests/parse-pretty/neg.js b/tests/parse-pretty/neg.js
deleted file mode 100644
--- a/tests/parse-pretty/neg.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var foo = -x;
-var bar = +y;
diff --git a/tests/parse-pretty/numbers.js b/tests/parse-pretty/numbers.js
deleted file mode 100644
--- a/tests/parse-pretty/numbers.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var x = 23;
-var y = 23.0;
diff --git a/tests/parse-pretty/object-lit-1.js b/tests/parse-pretty/object-lit-1.js
deleted file mode 100644
--- a/tests/parse-pretty/object-lit-1.js
+++ /dev/null
@@ -1,4 +0,0 @@
-var x = {  // avoid ambiguity with BlockStmt
-  foo : 23, 
-  bar : 50 
-}
diff --git a/tests/parse-pretty/object-lit-2.js b/tests/parse-pretty/object-lit-2.js
deleted file mode 100644
--- a/tests/parse-pretty/object-lit-2.js
+++ /dev/null
@@ -1,4 +0,0 @@
-var x = {  // avoid ambiguity with BlockStmt
-  foo : 23, 
-  bar : 50, 
-} 
diff --git a/tests/parse-pretty/prefix-chain.js b/tests/parse-pretty/prefix-chain.js
deleted file mode 100644
--- a/tests/parse-pretty/prefix-chain.js
+++ /dev/null
@@ -1,4 +0,0 @@
-var w = typeof typeof 34;
-var x = ! -- x; // derived from jQuery
-var y = ! !x; // derived from Prototype
-var z = !! -- x;
diff --git a/tests/parse-pretty/strings.js b/tests/parse-pretty/strings.js
deleted file mode 100644
--- a/tests/parse-pretty/strings.js
+++ /dev/null
@@ -1,6 +0,0 @@
-var x = '\b';
-var z = {
-  '\b': '\\b' // lhs is a PropString, not a StringLit, but must be printed
-              //  as a StringLit
-};
-var w = "\\s";
