language-ecmascript 0.14 → 0.15
raw patch · 38 files changed
+2909/−2706 lines, 38 filesdep +Diffdep +data-default-classdep +language-ecmascriptdep −charsetdep −data-defaultdep −sybdep ~QuickCheckdep ~containersPVP ok
version bump matches the API change (PVP)
Dependencies added: Diff, data-default-class, language-ecmascript, test-framework, test-framework-hunit, test-framework-quickcheck2
Dependencies removed: charset, data-default, syb
Dependency ranges changed: QuickCheck, containers
API changes (from Hackage documentation)
- Language.ECMAScript3.Parser: intLen :: (Integral a, Num a1) => a -> a1
- Language.ECMAScript3.Parser: mkDecimal :: Integer -> Integer -> Integer -> Integer -> Double
- Language.ECMAScript3.Parser.State: type ParserState = [String]
- Language.ECMAScript3.Parser.Type: type Parser s a = ParsecT s ParserState Identity a
+ Language.ECMAScript3.Parser: expression :: Stream s Identity Char => Parser s (Expression SourcePos)
+ Language.ECMAScript3.Parser: parseFromFile :: (Error e, MonadIO m, MonadError e m) => String -> m (JavaScript SourcePos)
+ Language.ECMAScript3.Parser: parseFromString :: String -> Either ParseError (JavaScript SourcePos)
+ Language.ECMAScript3.Parser: program :: Stream s Identity Char => Parser s (JavaScript SourcePos)
+ Language.ECMAScript3.Parser: statement :: Stream s Identity Char => Parser s (Statement SourcePos)
+ Language.ECMAScript3.SourceDiff: jsDiff :: JavaScript a -> JavaScript a -> String
+ Language.ECMAScript3.Syntax.Arbitrary: listOfN :: Arbitrary a => Int -> Gen a -> Gen [a]
Files
- CHANGELOG +10/−0
- language-ecmascript.cabal +29/−41
- src/Language/ECMAScript3/Analysis/Environment.hs +2/−0
- src/Language/ECMAScript3/Analysis/LabelSets.hs +5/−2
- src/Language/ECMAScript3/Parser.hs +62/−9
- src/Language/ECMAScript3/Parser/Type.hs +2/−0
- src/Language/ECMAScript3/PrettyPrint.hs +15/−6
- src/Language/ECMAScript3/SourceDiff.hs +24/−0
- src/Language/ECMAScript3/Syntax.hs +3/−2
- src/Language/ECMAScript3/Syntax/Arbitrary.hs +9/−3
- src/Language/ECMAScript3/Syntax/QuasiQuote.hs +3/−3
- src/PrettyTest.hs +0/−29
- src/UnitTest.hs +0/−33
- test/Test/Diff.hs +58/−0
- test/Test/Pretty.hs +43/−0
- test/Test/Unit.hs +49/−0
- test/TestMain.hs +12/−0
- test/diff/expects/t1.diff +2/−0
- test/diff/left/t1.js +2/−0
- test/diff/right/t1.js +1/−0
- test/parse-pretty/add.js +1/−0
- test/parse-pretty/do-while.js +3/−0
- test/parse-pretty/flapjax-fxinternal-22dec2008.js +2552/−0
- test/parse-pretty/neg.js +2/−0
- test/parse-pretty/numbers.js +2/−0
- test/parse-pretty/object-lit-1.js +4/−0
- test/parse-pretty/object-lit-2.js +4/−0
- test/parse-pretty/prefix-chain.js +4/−0
- test/parse-pretty/strings.js +6/−0
- tests/parse-pretty/add.js +0/−1
- tests/parse-pretty/do-while.js +0/−3
- tests/parse-pretty/flapjax-fxinternal-22dec2008.js +0/−2552
- tests/parse-pretty/neg.js +0/−2
- tests/parse-pretty/numbers.js +0/−2
- tests/parse-pretty/object-lit-1.js +0/−4
- tests/parse-pretty/object-lit-2.js +0/−4
- tests/parse-pretty/prefix-chain.js +0/−4
- tests/parse-pretty/strings.js +0/−6
CHANGELOG view
@@ -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
language-ecmascript.cabal view
@@ -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:
src/Language/ECMAScript3/Analysis/Environment.hs view
@@ -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 (..)
src/Language/ECMAScript3/Analysis/LabelSets.hs view
@@ -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
src/Language/ECMAScript3/Parser.hs view
@@ -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
src/Language/ECMAScript3/Parser/Type.hs view
@@ -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
src/Language/ECMAScript3/PrettyPrint.hs view
@@ -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
+ src/Language/ECMAScript3/SourceDiff.hs view
@@ -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
src/Language/ECMAScript3/Syntax.hs view
@@ -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.
src/Language/ECMAScript3/Syntax/Arbitrary.hs view
@@ -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
src/Language/ECMAScript3/Syntax/QuasiQuote.hs view
@@ -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
− src/PrettyTest.hs
@@ -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
− src/UnitTest.hs
@@ -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."
+ test/Test/Diff.hs view
@@ -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)
+ test/Test/Pretty.hs view
@@ -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
+ test/Test/Unit.hs view
@@ -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+
+ test/TestMain.hs view
@@ -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]
+ test/diff/expects/t1.diff view
@@ -0,0 +1,2 @@+ x = 1;+-y = 2;
+ test/diff/left/t1.js view
@@ -0,0 +1,2 @@+x = 1;+y = 2;
+ test/diff/right/t1.js view
@@ -0,0 +1,1 @@+x = 1;
+ test/parse-pretty/add.js view
@@ -0,0 +1,1 @@+1+2;;;;
+ test/parse-pretty/do-while.js view
@@ -0,0 +1,3 @@+do {+ x = y;+} while (element);
+ test/parse-pretty/flapjax-fxinternal-22dec2008.js view
@@ -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;+}
+ test/parse-pretty/neg.js view
@@ -0,0 +1,2 @@+var foo = -x;+var bar = +y;
+ test/parse-pretty/numbers.js view
@@ -0,0 +1,2 @@+var x = 23;+var y = 23.0;
+ test/parse-pretty/object-lit-1.js view
@@ -0,0 +1,4 @@+var x = { // avoid ambiguity with BlockStmt+ foo : 23, + bar : 50 +}
+ test/parse-pretty/object-lit-2.js view
@@ -0,0 +1,4 @@+var x = { // avoid ambiguity with BlockStmt+ foo : 23, + bar : 50, +}
+ test/parse-pretty/prefix-chain.js view
@@ -0,0 +1,4 @@+var w = typeof typeof 34;+var x = ! -- x; // derived from jQuery+var y = ! !x; // derived from Prototype+var z = !! -- x;
+ test/parse-pretty/strings.js view
@@ -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";
− tests/parse-pretty/add.js
@@ -1,1 +0,0 @@-1+2;;;;
− tests/parse-pretty/do-while.js
@@ -1,3 +0,0 @@-do {- x = y;-} while (element);
− tests/parse-pretty/flapjax-fxinternal-22dec2008.js
@@ -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;-}
− tests/parse-pretty/neg.js
@@ -1,2 +0,0 @@-var foo = -x;-var bar = +y;
− tests/parse-pretty/numbers.js
@@ -1,2 +0,0 @@-var x = 23;-var y = 23.0;
− tests/parse-pretty/object-lit-1.js
@@ -1,4 +0,0 @@-var x = { // avoid ambiguity with BlockStmt- foo : 23, - bar : 50 -}
− tests/parse-pretty/object-lit-2.js
@@ -1,4 +0,0 @@-var x = { // avoid ambiguity with BlockStmt- foo : 23, - bar : 50, -}
− tests/parse-pretty/prefix-chain.js
@@ -1,4 +0,0 @@-var w = typeof typeof 34;-var x = ! -- x; // derived from jQuery-var y = ! !x; // derived from Prototype-var z = !! -- x;
− tests/parse-pretty/strings.js
@@ -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";