packages feed

ghcjs-vdom (empty) → 0.2.0.0

raw patch · 30 files changed

+5281/−0 lines, 30 filesdep +basedep +containersdep +ghc-primsetup-changed

Dependencies added: base, containers, ghc-prim, ghcjs-base, ghcjs-ffiqq, ghcjs-prim, ghcjs-vdom, split, template-haskell

Files

+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2014 Luite Stegeman++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ README.markdown view
@@ -0,0 +1,4 @@+# virtual-dom bindings++[virtual-dom](https://github.com/Matt-Esch/virtual-dom) is a library for fast incremental+DOM updates by comparing immutable virtual DOM trees.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ data/example.css view
@@ -0,0 +1,28 @@+body {+    background-color: #ccc;+}++div.row {+    padding: 0;+    margin: 0;+    height: 5px;+    line-height: 5px;+    text-size: 10%;+}++div.pixel-red, div.pixel-white {+    vertical-align: top;+    display: inline-block;+    width: 5px;+    height: 5px;+    margin: 0;+    padding: 0;+}++div.pixel-red {+    background-color: #f00;+}++div.pixel-white {+    background-color: #fff;+}
+ examples/Components.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-+  ghcjs-vdom example, demonstrating components+ -}++module Main where++import           GHCJS.Foreign.QQ+import           GHCJS.Types++import qualified GHCJS.VDOM.Component    as C+import qualified GHCJS.VDOM.DOMComponent as D+import qualified GHCJS.VDOM.Attribute    as A+import qualified GHCJS.VDOM.Element      as E+import qualified GHCJS.VDOM.Event        as Ev+import           GHCJS.VDOM++import qualified Data.JSString.Int       as JSS++import           Data.IORef+import           Data.Monoid+import qualified Data.Map as M++import           Control.Concurrent+import           Control.Monad++import           System.IO++{-+  example virtual-dom component:+  a simple counter component that increments on click+ -}+data Counter = Counter { counterComp :: VComp+                       , counterKey  :: Int+                       , getCount    :: IO Int+                       }++mkCounter :: JSString -> Int -> IO Counter+mkCounter description startValue = do+  val <- newIORef startValue+  let description' = E.text (description <> ": ")+  c <- fixIO $ \c ->+      -- example only: diff and patch should really done through a Renderer+      let repaint   = C.render c >>= C.diff c >>= C.patch c+          increment = modifyIORef' val (+1) >> repaint >> return ()+      in  C.mkComponent $ do+            v <- readIORef val+            return $ E.div (A.class_ "counter", Ev.click (const increment))+                           [description', E.text (JSS.decimal v)]+  return $ Counter c startValue (readIORef val)++-- example DOM component+data Scroller = Scroller { scrollerComp :: DComp+                         , scrollerKey  :: Int+                         }++mkScroller :: JSString -> Int -> IO Scroller+mkScroller txt k = do+  mounts <- newIORef M.empty+  let mountScroller m = do+        (n::JSVal) <- [js| document.createElement('div') |]+        (t::JSVal) <- [js| document.createTextNode(`txt) |]+        [jsu_| `n.appendChild(`t); |]+        thr <- forkIO . forever $ do+           threadDelay 200000+           [jsu_| `t.data = `t.data.substr(1) + `t.data.substr(0,1); |]+        atomicModifyIORef mounts ((,()) . M.insert m thr)+        return n+      unmountScroller m _ = do+        Just thr <- M.lookup m <$> readIORef mounts+        killThread thr+        atomicModifyIORef mounts ((,()) . M.delete m)+        return ()+  c <- D.mkComponent mountScroller unmountScroller+  return (Scroller c k)++renderCounterList :: [Counter] -> VNode+renderCounterList counters =+  E.ul (A.class_ "counterList")+       (map (\c -> E.li (A.key (counterKey c)) (C.toNode (counterComp c)))+            counters)++renderScrollerList :: [Scroller] -> VNode+renderScrollerList scrollers =+  E.ul (A.class_ "scrollerList")+       (map (\s -> E.li (A.key (scrollerKey s)) (D.toNode (scrollerComp s)))+            scrollers)++render :: [Counter] -> [Scroller] -> VNode+render counters scrollers =+  E.div () [renderCounterList counters, renderScrollerList scrollers]++main :: IO ()+main = do+  Ev.initEventDelegation Ev.defaultEvents+  root <- [js| document.createElement('div') |]+  [js_| document.body.appendChild(`root); |]+  counters <- mapM (\i -> mkCounter ("counter " <> JSS.decimal i) i) [1..10]+  scrollers <- mapM (\i -> mkScroller ("scroller " <> JSS.decimal i) i) [1..10]+  m <- mount root (render counters scrollers)+  rotateComponents m 11 counters scrollers++rotateComponents :: VMount -> Int -> [Counter] -> [Scroller] -> IO ()+rotateComponents m n counters scrollers = do+  threadDelay 1000000+  newCounter <- mkCounter ("counter " <> JSS.decimal n) n+  newScroller <- mkScroller ("scroller " <> JSS.decimal n) n+  let scrollers' = tail scrollers ++ [newScroller]+      counters'  = tail counters ++ [newCounter]+  void $ diff m (render counters' scrollers') >>= patch m+  rotateComponents m (n+1) counters' scrollers'
+ examples/Render.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE QuasiQuotes, OverloadedStrings, BangPatterns #-}++{-+  ghcjs-vdom example, demonstrating the render queue+ -}++module Main where++import           Control.Concurrent+import           Control.Monad++import           Data.IORef+import           Data.Monoid++import           GHCJS.Foreign.QQ+import           GHCJS.Types++import qualified GHCJS.VDOM.Component    as C+import qualified GHCJS.VDOM.Attribute    as A+import qualified GHCJS.VDOM.Element      as E+import qualified GHCJS.VDOM.Render       as R+import           GHCJS.VDOM++import qualified Data.JSString.Int       as JSS+++-- a component with a slowed down rendering function+mkSlow :: JSString -> Int -> IO VComp+mkSlow descr delay = do+  count <- newIORef (0::Int)+  let descr' = E.text (descr <> " " <> JSS.decimal delay <> ": ")+  C.mkComponent $ do+    threadDelay delay+    c <- atomicModifyIORef count (\x -> let x' = x+1 in (x',x'))+    return $ E.div (A.class_ "slow") [descr', E.text (JSS.decimal c)]++main :: IO ()+main = do+  root <- [js| document.createElement('div') |]+  [js_| document.body.appendChild(`root); |]+  slows1 <- mapM (mkSlow "slow1") [10000, 50000, 100000, 300000]+  slows2 <- mapM (mkSlow "slow2") [10000, 50000, 100000]+  void $ mount root (E.div () (map C.toNode (slows1++slows2)))+  r1 <- R.mkRenderer+  r2 <- R.mkRenderer+  forever $ do+    mapM_ (R.render r1) slows1+    mapM_ (R.render r2) slows2+    threadDelay 10000+++
+ examples/Table.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE QuasiQuotes, OverloadedStrings, BangPatterns #-}++{-+  virtual-dom bindings demo, rendering a large pixel grid with a bouncing red+  square. the step and patch are calculated asynchronously, the update is+  batched in an animation frame+ -}++module Main where++import           Control.Monad++import           Data.IntMap (IntMap)+import qualified Data.IntMap as IM+import qualified Data.JSString as JSS++import           GHCJS.VDOM+import           GHCJS.VDOM.QQ+import qualified GHCJS.VDOM.Element as E+import qualified GHCJS.VDOM.Attribute as A++import           GHCJS.Foreign.Callback+import           GHCJS.Foreign.QQ+import           GHCJS.Types++import           JavaScript.Web.AnimationFrame (inAnimationFrame)++red :: JSString+red = "pixel-red"++white :: JSString+white = "pixel-white"++type Pixels = IntMap (IntMap JSString)++setPixel :: Int -> Int -> JSString -> Pixels -> Pixels+setPixel x y c p =+  let r  = p IM.! y+      r' = IM.insert x c r+  in  r' `seq` IM.insert y r' p++data State = State { x  :: !Int, y  :: !Int+                   , dx :: !Int, dy :: !Int+                   , w  :: !Int, h  :: !Int+                   , pixels :: !Pixels+                   }++mkState :: Int -> Int -> Int -> Int -> State+mkState w h x y = State x y 1 1 w h pix+  where+    pix     = IM.fromList $ map row [0..h-1]+    row n   = (n, IM.fromList (map (col n) [0..w-1]))+    col n m = (m, if (m,n)==(x,y) then red else white)++step :: State -> State+step (State x y dx dy w h p) =+  let dx' = if x==0 then 1 else if x==(w-1) then -1 else dx+      dy' = if y==0 then 1 else if y==(h-1) then -1 else dy+      x'  = x+dx'+      y'  = y+dy'+      p'  = setPixel x' y' red (setPixel x y white p)+   in State x' y' dx' dy' w h p'++cls :: JSString -> Attributes'+cls name = [att| className: name |]++render :: State -> VNode+render s = E.div (cls "state") [ch|pixelDiv,numDiv|]+    where+      xd       = textDiv (y s)+      yd       = textDiv (x s)+      numDiv   = E.div (cls "numeric") [ch|xd,yd|]+      pixelDiv = E.div (cls "pixels")+          (map (renderRowM (w s) . (pixels s IM.!)) [0..h s-1])++textDiv :: Show a => a -> VNode+textDiv x = E.div () [ch|c|]+  where+    c = E.text . JSS.pack . show $ x++renderRowM !w !r = memo renderRow w r++renderRow :: Int -> IntMap JSString -> VNode+renderRow w r =+  E.div (A.class_ "row", A.lang "EN") (map (renderPixelM r) [0..w-1])++renderPixelM !r !c = memo renderPixel r c++renderPixel :: IntMap JSString -> Int -> VNode+renderPixel r c = E.div (cls (r IM.! c)) ()++animate :: VMount -> State -> IO ()+animate m s =+  let s' = step s+      r' = render s'+  in do p <- diff m r'+        void $ inAnimationFrame ContinueAsync (\_ -> patch m p >> animate m s')++main :: IO ()+main = do+  root <- [js| document.createElement('div') |]+  [js_| document.body.appendChild(`root); |]+  let s = mkState 167 101 10 20+  m <- mount root (E.div () ())+  animate m s++
+ ghcjs-vdom.cabal view
@@ -0,0 +1,108 @@+name:                ghcjs-vdom+version:             0.2.0.0+synopsis:            Virtual-dom bindings for GHCJS+description:         Virtual-dom is a library for fast incremental DOM+                     updates by comparing virtual immutable DOM trees to+                     find a minimal number of changes to update the actual DOM.++                     The bindings support memoized nodes which are only+                     recomputed when the underlying data changes, using+                     referential equality for the function and arguments.++                     The diff procedure in the virtual-dom library has been+                     modified slightly to support computing a diff in an+                     asynchronous thread. Since computing a diff forces all data+                     around the virtual-dom tree, the computation, the computation+                     can be expensive.++                     An asynchronous diff computation can be safely aborted+                     with an async exception.++license:             MIT+license-file:        LICENSE+author:              Luite Stegeman+maintainer:          stegeman@gmail.com+category:            Web+build-type:          Simple+cabal-version:       >=1.10+extra-source-files:  virtual-dom/lib.require.js+                     virtual-dom/diff.js+                     virtual-dom/LICENSE+                     virtual-dom/handle-thunk.js+                     virtual-dom/README.md+                     virtual-dom/package.json+                     data/example.css+                     README.markdown++flag build-examples+  description: build the example programs+  default: False+  manual: True++library+  js-sources: jsbits/vdom.js+              virtual-dom/lib.js+  ghcjs-options: -Wall+  exposed-modules:     GHCJS.VDOM+                       GHCJS.VDOM.Attribute+                       GHCJS.VDOM.Component+                       GHCJS.VDOM.DOMComponent+                       GHCJS.VDOM.Element+                       GHCJS.VDOM.Event+                       GHCJS.VDOM.QQ+                       GHCJS.VDOM.Render+                       GHCJS.VDOM.Unsafe+  other-modules:       GHCJS.VDOM.Internal+                       GHCJS.VDOM.Internal.TH+                       GHCJS.VDOM.Internal.Thunk+                       GHCJS.VDOM.Internal.Types+                       GHCJS.VDOM.Element.Builtin+  build-depends:       base >=4.7 && < 5,+                       ghc-prim,+                       ghcjs-ffiqq,+                       ghcjs-base >= 0.2.0.0,+                       ghcjs-prim,+                       containers,+                       split,+                       template-haskell+  hs-source-dirs:      src+  default-language:    Haskell2010++executable ghcjs-vdom-example-table+  if !flag(build-examples)+      buildable: False+  Main-Is:        Table.hs+  Default-Language: Haskell2010+  hs-source-dirs: examples+  Build-Depends:  base                >= 4    &&  < 5,+                  ghcjs-ffiqq,+                  ghcjs-vdom,+                  containers,+                  ghcjs-base+  ghcjs-Options: -Wall++executable ghcjs-vdom-example-components+  if !flag(build-examples)+      buildable: False+  Main-Is:        Components.hs+  Default-Language: Haskell2010+  hs-source-dirs: examples+  Build-Depends:  base                >= 4    &&  < 5,+                  ghcjs-ffiqq,+                  ghcjs-vdom,+                  containers,+                  ghcjs-base+  ghcjs-Options: -Wall++executable ghcjs-vdom-example-render+  if !flag(build-examples)+      buildable: False+  Main-Is:        Render.hs+  Default-Language: Haskell2010+  hs-source-dirs: examples+  Build-Depends:  base                >= 4    &&  < 5,+                  ghcjs-ffiqq,+                  ghcjs-vdom,+                  containers,+                  ghcjs-base+  ghcjs-Options: -Wall
+ jsbits/vdom.js view
@@ -0,0 +1,23 @@+#include <ghcjs/rts.h>++/* + * global name for the things we need from the virtual-dom library+ */+var h$vdom;++function h$vdomEventCallback(async, action, ev) {+  var a = MK_AP1(action, MK_JSVAL(ev));+  if(async) {+    h$run(a);+  } else {+    h$runSync(a, true);+  }+}++function h$vdomMountComponentCallback(action, mnt, comp) {+  h$run(MK_AP2(action, MK_JSVAL(mnt), MK_JSVAL(comp)));+}++function h$vdomUnmountComponentCallback(action, mnt, node) {+  h$run(MK_AP2(action, MK_JSVAL(mnt), MK_JSVAL(node)));+}
+ src/GHCJS/VDOM.hs view
@@ -0,0 +1,106 @@+{-# LANGUAGE QuasiQuotes #-}++{-|+   Bindings for the virtual-dom library.++   The virtual-dom diff function has been changed slightly to allow it to work+   with full functionality in asynchronous threads.++   It's possible to implement the bindings without the modifications at the+   cost of tail-call optimization and preemptive threading in the diff, by+   recursively forcing the thunks in synchronous threads.+ -}++module GHCJS.VDOM ( Attributes, Children+                  , Attributes', Children'+                  , VMount, VNode, VComp, DComp, Patch, DOMNode+                  , mount, unmount+                  , diff, patch+                  , memo, memoKey+                  ) where++import GHCJS.Types+import GHCJS.Foreign.QQ+import GHCJS.Prim+import GHCJS.Marshal.Pure++import Control.Monad+import Data.Coerce++import System.IO.Unsafe++import           GHCJS.VDOM.Internal.Types+import           GHCJS.VDOM.Internal.Thunk+import           GHCJS.VDOM.Internal       (j,J)+import qualified GHCJS.VDOM.Internal       as I++class MemoNode a where memoNode :: (J, [JSIdent], a) -> a++instance MemoNode VNode+  where+    memoNode (_,[],a) = a+    memoNode (k,xs,v) =+      let vd     = I.unsafeExportValue v+          xs1    = unsafePerformIO (toJSArray $ coerce xs)+      in VNode [j| h$vdom.th(`vd, `xs1, `k, true) |]+    {-# INLINE memoNode #-}++instance MemoNode b => MemoNode (a -> b)+  where+    memoNode (k,xs,f) = \a -> memoNode (k, I.objectIdent a:xs, f a)+    {-# INLINE memoNode #-}++memoKey :: MemoNode a => JSString -> a -> a+memoKey k = memo' (pToJSVal k)+{-# NOINLINE memoKey #-}++memo :: MemoNode a => a -> a+memo = memo' [j| $r = null; |]+{-# NOINLINE memo #-}++memo' :: MemoNode a => J -> a -> a+memo' k f = memoNode (k,[I.objectIdent f],f)+{-# INLINE memo' #-}++{-|+   Mount a virtual-dom tree in the real DOM. The mount point can be updated+   with patch.+-}+mount :: DOMNode -> VNode -> IO VMount+mount n v = do+  m <- VMount <$> [js| h$vdom.mount(`n) |]+  void $ patch m =<< diff m v+  return m+{-# INLINE mount #-}++{-|+   Remove a virtual-dom tree from the document. It's important to use+   unmount rather than removing the mount point any other way since this+   releases all associated Haskell data structures.+ -}+unmount :: VMount -> IO ()+unmount (VMount m) = [jsu_| h$vdom.unmount(`m); |]+{-# INLINE unmount #-}++{-|+   Compute a patch to update the mounted tree to match the virtual-dom tree+ -}+diff :: VMount -> VNode -> IO Patch+diff (VMount m) (VNode v) = do+  thunks <- [jsu| [] |]+  patch  <- [jsu| `m.diff(`v, `thunks) |]+  forceThunks thunks+  forcePatch [j| `patch.patch |]+  return (Patch patch)+{-# INLINE diff #-}++{-|+   Apply a patch to a mounted virtual-dom tree. Fails if the tree has already+   been patched after the diff was computed.+ -}+patch :: VMount -> Patch -> IO Bool+patch (VMount m) (Patch p) = [jsu| `m.patch(`p); |]+{-# INLINE patch #-}+++
+ src/GHCJS/VDOM/Attribute.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}++module GHCJS.VDOM.Attribute ( Attribute+                            , Attributes+                              -- * some predefined attributes+                            , class_+                            , id+                            , href+                            , alt+                            , src+                            , name+                            , target+                            , value+                            , width+                            , height+                            , title+                            , lang+                            , type_+                            , key -- virtual-dom identifiers+                            ) where++import Prelude hiding (id)++import GHCJS.Types++import GHCJS.VDOM.Internal.Types+import GHCJS.VDOM.Internal++mkAttrs ''JSString [ "id", "href", "src", "alt", "title"+                   , "lang", "name", "target", "value"+                   ]++mkAttrs' ''JSString [ ("class_", "className")+                    , ("type_", "type")+                    ]++mkAttrs ''Int [ "key", "width", "height" ]
+ src/GHCJS/VDOM/Component.hs view
@@ -0,0 +1,60 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE GHCForeignImportPrim #-}+{-# LANGUAGE UnliftedFFITypes #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE MagicHash #-}++module GHCJS.VDOM.Component ( VComp+                            , toNode+                            , mkComponent+                            , render+                            , diff+                            , patch+                            ) where++import           Control.Monad++import           GHCJS.Foreign.QQ++import           GHCJS.VDOM.Internal.Types++import qualified GHCJS.VDOM.Internal       as I+import           GHCJS.VDOM.Internal       (j)+import qualified GHCJS.VDOM.Internal.Thunk as I++import           GHC.Exts+import           GHC.Types (IO(..))+import           Unsafe.Coerce++toNode :: VComp -> VNode+toNode (VComp v) = VNode v+{-# INLINE toNode #-}++mkComponent :: IO VNode -> IO VComp+mkComponent r = do+  let renderE = I.unsafeExportValue r+  c <- VComp <$> [jsu| h$vdom.c(`renderE, null, null, null) |]+  void $ patch c =<< diff c =<< render c+  return c++foreign import javascript unsafe "$r = $1.hsRender;"+  js_hsRender :: VComp -> State# RealWorld -> (# State# RealWorld, Any #)++render :: VComp -> IO VNode+render c = join $ IO (\s -> case js_hsRender c s of+  (# s', r #) -> (# s', unsafeCoerce r #))+{-# INLINE render #-}++diff :: VComp -> VNode -> IO Patch+diff (VComp c) (VNode v) = do+  thunks <- [jsu| [] |]+  patch  <- [jsu| `c.diff(`v, `thunks) |]+  I.forceThunks thunks+  I.forcePatch [j| `patch.patch |]+  return (Patch patch)+{-# INLINE diff #-}++patch :: VComp -> Patch -> IO Bool+patch (VComp c) (Patch p) = [jsu| `c.patch(`p) |]+{-# INLINE patch #-}+
+ src/GHCJS/VDOM/DOMComponent.hs view
@@ -0,0 +1,47 @@+{- |+   DOM components manage a normal DOM subtree inside a virtual-dom tree.++   The component has callbacks for mounting and unmounting. The mount+   callback returns a DOM tree that stays in the document until the+   unmount callback is called.++   A single component can be mounted multiple times. The mount callback+   is called for each mount, and is expected to return a fresh DOM+   tree every time.+ -}++{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI, QuasiQuotes #-}+module GHCJS.VDOM.DOMComponent ( DComp+                               , mkComponent+                               , toNode+                               ) where++import           GHCJS.Foreign.QQ+import           GHCJS.Marshal.Pure+import           GHCJS.Types++import           GHCJS.VDOM.Internal.Types++import qualified GHCJS.VDOM.Internal       as I+++toNode :: DComp -> VNode+toNode (DComp v) = VNode v+{-# INLINE toNode #-}++mkComponent :: (Int -> IO JSVal)     -- ^ mount action, return a DOM node+            -> (Int -> JSVal -> IO ()) -- ^ unmount action+            -> IO DComp+mkComponent mount unmount =+  let mountE   = I.unsafeExportValue (mountComponent mount)+      unmountE = I.unsafeExportValue (unmountComponent unmount)+  in  DComp <$> [jsu| h$vdom.c(null, `mountE, `unmountE, null) |]+++mountComponent :: (Int -> IO JSVal) -> JSVal -> JSVal -> IO ()+mountComponent f mnt c = do+  node <- f (pFromJSVal mnt)+  [jsu| `c.updateMount(`mnt, `node); |]++unmountComponent :: (Int -> JSVal -> IO ()) -> JSVal -> JSVal -> IO ()+unmountComponent f mnt node = f (pFromJSVal mnt) node
+ src/GHCJS/VDOM/Element.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}+module GHCJS.VDOM.Element ( custom+                          , text+                          , module GHCJS.VDOM.Element.Builtin+                          ) where++import           Data.JSString (JSString)++import qualified GHCJS.VDOM.Internal as I+import           GHCJS.VDOM.Internal.Types++import           GHCJS.VDOM.Element.Builtin++custom :: (Attributes a, Children c) => JSString -> a -> c -> VNode+custom tag a c = I.mkVNode tag a c+{-# INLINE custom #-}
+ src/GHCJS/VDOM/Element/Builtin.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE TemplateHaskell #-}++module GHCJS.VDOM.Element.Builtin where++import GHCJS.VDOM.Internal++mkElems [ "address", "article", "body", "footer", "header"+        , "h1", "h2", "h3", "h4", "h5", "h6"+        , "hgroup", "nav", "section"+        , "dd", "div", "dl", "dt", "figcaption", "figure", "hr", "li"+        , "main", "ol", "p", "pre", "ul"+        , "a", "abbr", "b", "bdi", "br", "cite", "code", "dfn"+        , "em", "i", "kbd", "mark", "q", "rp", "rt", "rtc", "ruby"+        , "s", "samp", "small", "span", "strong", "sub", "sup", "time"+        , "u", "var", "wbr"+        , "area", "audio", "img", "map", "track", "video"+        , "embed", "iframe", "object", "param", "source"+        , "canvas", "noscript", "script"+        , "del", "ins"+        , "caption", "col", "colgroup", "table", "tbody", "td", "tfoot"+        , "th", "thead", "tr"+        , "button", "datalist", "fieldset", "form", "input", "keygen"+        , "label", "legend", "meter", "optgroup", "option", "output"+        , "progress", "select", "textarea"+        , "details", "dialog", "menu", "menuitem", "summary"+        , "content", "element", "shadow", "template"+        ]++-- use 'data_' as the name for the data tag, data is a reserved word+mkElem "data_" "data"
+ src/GHCJS/VDOM/Event.hs view
@@ -0,0 +1,198 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE FlexibleContexts #-}++module GHCJS.VDOM.Event ( initEventDelegation+                        , defaultEvents+--                        , target+                        , stopPropagation+                        , stopImmediatePropagation+                        , preventDefault+                          +                          -- * mouse+                        , MouseEvent+                        , click+                        , dblclick+                        , mousedown+                        , mouseenter+                        , mouseleave+                        , mousemove+                        , mouseout+                        , mouseover+                        , mouseup+                          --+                        , button+                        , buttons+                        , clientX+                        , clientY+                           +                          -- * keyboard+                        , KeyboardEvent+                        , keydown+                        , keypress+                        , keyup+                          --+                        , key+                        , ctrlKey+                        , metaKey+                        , shiftKey+                          +                          -- * drag+                        , DragEvent+                        , drag+                        , dragend+                        , dragenter+                        , dragleave+                        , dragover+                        , dragstart+                          +                          -- * focus+                        , FocusEvent+                        , focus+                        , blur++                          -- * ui+                        , UIEvent+                        , resize+                        , scroll+                        , select+                        , unload+                          +                          -- * wheel+                        , WheelEvent+                        , wheel+                          --+                        , deltaX+                        , deltaY+                        , deltaZ+                        , deltaMode+                          +                          -- * generic+                        , Event+                        , submit+                        , change+                        ) where++import Data.Coerce++import Unsafe.Coerce++import GHCJS.Prim+import GHCJS.Types+import GHCJS.Foreign.QQ++import GHCJS.VDOM.Internal++-- | call this to initialize the virtual-dom event handling system+initEventDelegation :: [JSString] -> IO ()+initEventDelegation eventTypes = do+  a <- toJSArray (unsafeCoerce eventTypes)+  [jsu_| h$vdom.initDelegator(`a); |]++class Coercible a JSVal => Event_ a+class Event_ a          => KeyModEvent_ a+class Event_ a          => MouseEvent_ a+class Event_ a          => FocusEvent_ a++mkEventTypes ''Event_ [ ("MouseEvent",    [''MouseEvent_])+                      , ("KeyboardEvent", [''KeyModEvent_])+                      , ("FocusEvent",    [''FocusEvent_])+                      , ("DragEvent",     [])+                      , ("WheelEvent",    [])+                      , ("UIEvent",       [])+                      , ("Event",         [])+                      ]++mkEvents 'MouseEvent [ "click", "dblclick", "mousedown", "mouseenter"+                     , "mouseleave", "mousemove", "mouseout"+                     , "mouseover", "mouseup"+                     ]++mkEvents 'KeyboardEvent [ "keydown", "keypress", "keyup" ]++mkEvents 'DragEvent [ "drag", "dragend", "dragenter", "dragleave"+                    , "dragover", "dragstart" ]++mkEvents 'FocusEvent [ "focus", "blur" ]++mkEvents 'UIEvent [ "resize", "scroll", "select", "unload" ]++mkEvents 'WheelEvent [ "wheel" ]++mkEvents 'Event [ "submit", "change" ]++er :: Event_ a => (JSVal -> b) -> a -> b+er f x = f (coerce x)++-- -----------------------------------------------------------------------------++-- this contains all event types added with mkEvents+defaultEvents :: [JSString]+defaultEvents = $(mkDefaultEvents)+++-- target :: Event_ a => a -> VNode+-- target e = undefined++stopPropagation :: Event_ a => a -> IO ()+stopPropagation = er $ \e -> [jsu_| `e.stopPropagation(); |]+{-# INLINE stopPropagation #-}++stopImmediatePropagation :: Event_ a => a -> IO ()+stopImmediatePropagation = er $ \e -> [jsu_| `e.stopImmediatePropagation(); |]+{-# INLINE stopImmediatePropagation #-}++preventDefault :: Event_ a => a -> IO ()+preventDefault = er $ \e -> [jsu_| `e.preventDefault(); |]+{-# INLINE preventDefault #-}++ctrlKey :: KeyModEvent_ a => a -> Bool+ctrlKey = er $ \e -> [jsu'| `e.ctrlKey |]+{-# INLINE ctrlKey #-}++metaKey :: KeyModEvent_ a => a -> Bool+metaKey = er $ \e -> [jsu'| `e.ctrlKey |]+{-# INLINE metaKey #-}++shiftKey :: KeyModEvent_ a => a -> Bool+shiftKey = er $ \e -> [jsu'| `e.ctrlKey |]+{-# INLINE shiftKey #-}++key :: KeyboardEvent -> JSString+key = er $ \e -> [jsu'| `e.key |]+{-# INLINE key #-}++button :: MouseEvent_ a => a -> Int+button = er $ \e -> [jsu'| `e.button |]+{-# INLINE button #-}++buttons :: MouseEvent_ a => a -> Int+buttons = er $ \e -> [jsu'| `e.buttons |]+{-# INLINE buttons #-}++deltaX :: WheelEvent -> Double+deltaX = er $ \e -> [jsu'| `e.deltaX |]+{-# INLINE deltaX #-}++deltaY :: WheelEvent -> Double+deltaY = er $ \e -> [jsu'| `e.deltaY |]+{-# INLINE deltaY #-}++deltaZ :: WheelEvent -> Double+deltaZ = er $ \e -> [jsu'| `e.deltaZ |]+{-# INLINE deltaZ #-}++deltaMode :: WheelEvent -> Double+deltaMode = er $ \e -> [jsu'| `e.deltaMode |]+{-# INLINE deltaMode #-}++clientX :: MouseEvent -> Int+clientX = er $ \e -> [jsu'| `e.clientX|0 |]+{-# INLINE clientX #-}++clientY :: MouseEvent -> Int+clientY = er $ \e -> [jsu'| `e.clientY|0 |]+{-# INLINE clientY #-}
+ src/GHCJS/VDOM/Internal.hs view
@@ -0,0 +1,199 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE UnliftedFFITypes #-}+{-# LANGUAGE GHCForeignImportPrim #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE ScopedTypeVariables #-}++module GHCJS.VDOM.Internal where++import GHCJS.VDOM.Internal.Types++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Language.Haskell.TH.Syntax++import GHC.Prim (Any, State#, RealWorld)++import Control.Monad+import Unsafe.Coerce++import GHCJS.Foreign.QQ+import GHCJS.Types+import GHCJS.Marshal.Pure++import Data.List (foldl')+import Data.String (IsString(..))+import Data.Typeable++import GHC.IO           ( IO(..) )+import GHC.Base         ( StableName# )++type J = JSVal++j :: QuasiQuoter+j = jsu'++mkVNode :: (Attributes a, Children c) => JSString -> a -> c -> VNode+mkVNode tag atts children = js_vnode tag (mkAttributes atts) (mkChildren children)+{-# INLINE mkVNode #-}++mkElems :: [String] -> Q [Dec]+mkElems = fmap concat . mapM (join mkElem)++mkElem :: String -> String -> Q [Dec]+mkElem name tag = do+     let n = mkName name+     a <- newName "a"+     c <- newName "c"+     b <- [| mkVNode (fromString tag) |]+     typ <- [t|forall a c. (Attributes a, Children c) => a -> c -> VNode |]+     return [ SigD n typ+            , FunD n [Clause [VarP a, VarP c] (NormalB (AppE (AppE b (VarE a)) (VarE c))) []]+            , PragmaD (InlineP n Inline FunLike AllPhases)+            ]++mkAttrs :: Name -> [String] -> Q [Dec]+mkAttrs ty = fmap concat . mapM (join (mkAttr ty))++mkAttrs' :: Name -> [(String, String)] -> Q [Dec]+mkAttrs' ty = fmap concat . mapM (uncurry (mkAttr ty))++mkAttr :: Name -> String -> String -> Q [Dec]+mkAttr ty name attr = do+  let n = mkName name+  x <- newName "x"+  b <- [| \y -> Attribute attr (pToJSVal y) |]+  return [ SigD n (AppT (AppT ArrowT (ConT ty)) (ConT ''Attribute))+         , FunD n [Clause [VarP x] (NormalB (AppE b (VarE x))) []]+         , PragmaD (InlineP n Inline FunLike AllPhases)+         ]++mkEventTypes :: Name -> [(String, [Name])] -> Q [Dec]+mkEventTypes base = fmap concat . mapM mk+  where+    mk (n, cls) = do+      let nn     = mkName n+#if MIN_VERSION_template_haskell(2,11,0)+          mkI cn = InstanceD Nothing [] (AppT (ConT cn) (ConT nn)) []+#else+          mkI cn = InstanceD [] (AppT (ConT cn) (ConT nn)) []+#endif+          insts  = map mkI (base : cls)+      jsr <- [t| JSVal |]+      typ <- [t| Typeable |]+#if MIN_VERSION_template_haskell(2,11,0)+      return $ (NewtypeD []  nn [] Nothing (NormalC nn [(Bang NoSourceUnpackedness NoSourceStrictness, jsr)]) [ typ ]) : insts+#else+      return $ (NewtypeD [] nn [] (NormalC nn [(NotStrict, jsr)]) [''Typeable]) : insts+#endif++newtype CreatedEvents = CreatedEvents { unCreatedEvents :: [String] }+  deriving (Typeable)++addCreatedEvent :: String -> CreatedEvents -> CreatedEvents+addCreatedEvent ev (CreatedEvents es) = CreatedEvents (ev:es)++-- dcon must be a newtype constructor, not a data con+mkEvents :: Name -> [String] -> Q [Dec]+mkEvents dcon xs = fmap concat (mapM (\x -> mkEvent dcon x ("ev-"++x)) xs)++-- dcon must be a newtype constructor, not a data con+mkEvent :: Name -> String -> String -> Q [Dec]+mkEvent dcon name attr = do+  let n    = mkName name+      emsg = "GHCJS.VDOM.Internal.mkEvent: expected newtype constructor"+  i <- reify dcon+  dctyp <- case i of+#if MIN_VERSION_template_haskell(2,11,0)+    DataConI _ _ pn -> do+      pni <- reify pn+      case pni of+         TyConI (NewtypeD _ ctn _ _ _ _) -> return (ConT ctn)+         _                               -> error emsg+    _                 -> error emsg+#else+    DataConI _ _ pn _ -> do+      pni <- reify pn+      case pni of+         TyConI (NewtypeD _ ctn _ _ _) -> return (ConT ctn)+         _                             -> error emsg+    _                 -> error emsg+#endif+  iou <- [t| IO () |]+  h <- newName "h"+  b <- [| mkEventAttr (fromString attr) |]+  let ht = AppT (AppT ArrowT dctyp) iou+  -- typ <- [t| (dctyp -> IO ()) -> Attribute |]+  qPutQ . maybe (CreatedEvents [name]) (addCreatedEvent name) =<< qGetQ+  return [ SigD n (AppT (AppT ArrowT ht) (ConT ''Attribute))+         , FunD n [Clause [VarP h] (NormalB (AppE (AppE b (ConE dcon)) (VarE h))) []]+         , PragmaD (InlineP n Inline FunLike AllPhases)+         ]++-- a must be a newtype of JSVal!+mkEventAttr :: JSString -> (JSVal -> a) -> (a -> IO ()) -> Attribute+mkEventAttr attr _wrap h =+  +  let e  = unsafeExportValue h+      h' = [js'| h$vdom.makeHandler(`e, false) |]+  in  h' `seq` Attribute attr h'+{-# INLINE mkEventAttr #-}++{-+eventLogger :: JSVal ()+eventLogger = [js'| function(ev) { console.log("event caught"); } |]+-}++-- generate a list of all events stored in the persistent TH state, created with mkEvent+mkDefaultEvents :: Q Exp+mkDefaultEvents = do+  evs <- maybe [] unCreatedEvents <$> qGetQ+  nil  <- [| [] |]+  cons <- [| (:) |]+  return $ foldl' (\xs e -> AppE (AppE cons (LitE . stringL $ e)) xs) nil evs+  +js_vnode :: JSString -> Attributes' -> Children' -> VNode+js_vnode tag (Attributes' props) (Children' children) =+  VNode [jsu'| h$vdom.v(`tag, `props, `children) |]+  --VNode [jsu'| new h$vdom.VNode(`tag, `props, `children) |]++getThunk :: J -> IO J+getThunk x = IO (js_getThunk x)++foreign import javascript unsafe "$r = $1.hst;"+  js_getThunk :: J -> State# RealWorld -> (# State# RealWorld, J #)++-- -----------------------------------------------------------------------------+{-|+   Export an arbitrary Haskell value to JS.++   be careful with these JSVal values, losing track of them will result in+   incorrect memory management. As long as we keep the values directly in+   a Property or VNode, the ghcjs-vdom extensible retention system will know+   where to find them.+ -}+unsafeExportValue :: a -> JSVal+unsafeExportValue x = js_export (unsafeCoerce x)+{-# INLINE unsafeExportValue #-}++{-|+   make a unique identifier that can be easily compared in JS+   if(objectIdent(o1) === objectIdent(o2) or both are NaN, then o1 and o2 are+   are the same Haskell value+ -}+objectIdent :: a -> JSIdent+objectIdent x = x `seq` js_makeObjectIdent (unsafeExportValue x)+{-+  unsafePerformIO . IO $ \s ->+  case makeStableName# x s of (# s', sn #) -> (# s', js_convertSn sn #)+-}+{-# INLINE objectIdent #-}+                             +foreign import javascript unsafe "$r = $1;" js_export    :: Any -> JSVal+foreign import javascript unsafe "$r = $1;" js_convertSn :: StableName# a -> JSIdent++foreign import javascript unsafe "h$makeStableName($1)" js_makeObjectIdent :: JSVal -> JSIdent
+ src/GHCJS/VDOM/Internal/TH.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE CPP, TemplateHaskell, QuasiQuotes #-}++module GHCJS.VDOM.Internal.TH where++import Data.List (foldl')++import Language.Haskell.TH++import Unsafe.Coerce++mkTupleChildrenInstances :: Name -> Name -> Name -> Name -> Name -> [Int] -> Q [Dec]+mkTupleChildrenInstances cls method ty con wrapper xs =+  concat <$> mapM (mkTupleChildrenInstance cls method ty con wrapper) xs++{-++instance cls (ty, ty, ...) where+  method (con x1, con x2, ...) = wrapper (buildArrayIN x1 x2 ...)+  {-# INLINE method #-}++-}+mkTupleChildrenInstance :: Name -> Name -> Name -> Name -> Name -> Int -> Q [Dec]+mkTupleChildrenInstance cls method ty con wrapper n = do+  let xs    = map (mkName.('x':).show) [1..n]+      t     = AppT (ConT cls) (iterate (`AppT` ConT ty) (TupleT n) !! n)+      build = mkName ("GHCJS.Prim.Internal.Build.buildArrayI" ++ show n)+      pat   = [TupP (map (ConP con . (:[]) . VarP) xs)]+      body  = NormalB (AppE (ConE wrapper)+                           (foldl' (\e v -> AppE e (VarE v)) (VarE build) xs))+#if MIN_VERSION_template_haskell(2,11,0)+  return [InstanceD Nothing [] t [ FunD method [Clause pat body []]+                                 , PragmaD (InlineP method Inline FunLike AllPhases)+                                 ]+         ]+#else+  return [InstanceD [] t [ FunD method [Clause pat body []]+                         , PragmaD (InlineP method Inline FunLike AllPhases)+                         ]+         ]+#endif++mkTupleAttrInstances :: Name -> Name -> Name -> Name -> Name -> [Int] -> Q [Dec]+mkTupleAttrInstances cls method ty con wrapper xs =+  concat <$> mapM (mkTupleAttrInstance cls method ty con wrapper) xs++{-++instance cls (ty, ty, ...) where+  method (con k1 v1, con k2 v2, ...) =+    wrapper (buildObjectIN k1 k2 v1 v2 ...)+  {-# INLINE method #-}++ -}+mkTupleAttrInstance :: Name -> Name -> Name -> Name -> Name -> Int -> Q [Dec]+mkTupleAttrInstance cls method ty con wrapper n = do+  let xs    = map (\i -> let si = show i in [mkName ('k':si), mkName ('v':si)]) [1..n]+      t     = AppT (ConT cls) (iterate (`AppT` ConT ty) (TupleT n) !! n)+      build = mkName ("GHCJS.Prim.Internal.Build.buildObjectI" ++ show n)+      pat   = [TupP (map (ConP con . map VarP) xs)]+      app e k v = AppE (AppE e (AppE (VarE 'unsafeCoerce) (VarE k))) (VarE v)+      body  = NormalB (AppE (ConE wrapper)+                           (foldl' (\e [k,v] -> app e k v) (VarE build) xs))+#if MIN_VERSION_template_haskell(2,11,0)+  return [InstanceD Nothing [] t [ FunD method [Clause pat body []]+                                 , PragmaD (InlineP method Inline FunLike AllPhases)+                                 ]+         ]+#else+  return [InstanceD [] t [ FunD method [Clause pat body []]+                         , PragmaD (InlineP method Inline FunLike AllPhases)+                         ]+         ]+#endif
+ src/GHCJS/VDOM/Internal/Thunk.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes, LambdaCase, GHCForeignImportPrim #-}+{-|+   Code that deals with forcing thunks in virtual-dom trees. When+   computing a diff, the virtual-dom code returns a list of thunks+   found in the tree. The caller then forces the thunks and recurses+   into them to advance the diff computation, until all thunks have+   been evaluated.+ -}+module GHCJS.VDOM.Internal.Thunk where++import GHCJS.Foreign.QQ+import GHCJS.Prim++import Control.Exception+import Control.Monad++import GHC.Exts (Any)+import Unsafe.Coerce++import           GHCJS.VDOM.Internal       (j,J)+import qualified GHCJS.VDOM.Internal       as I+import           GHCJS.VDOM.Internal.Types++diff' :: J -> J -> IO J+diff' a b = do+  thunks <- [jsu| [] |]+  p <- [jsu| h$vdom.diff(`a, `b, `thunks) |]+  forceThunks thunks+  forcePatch p+  return p++forceThunks :: J -> IO ()+forceThunks thunks+  | [j| `thunks.length > 0 |] = fromJSArray thunks >>= mapM_ forceNode+  | otherwise                 = return ()+  where+    forceNode n = do+      forceThunkNode [j| `n.a |]+      forceThunkNode [j| `n.b |]+      patch <- diff' [j| `n.a.vnode |] [j| `n.b.vnode |]+      [jsu_| h$vdom.setThunkPatch(`n, `patch); |]++forceThunkNode :: J -> IO ()+forceThunkNode x =+  [jsu| `x && `x.hst && !`x.vnode |] >>= \case+    True -> do+      (VNode u) <- fmap unsafeCoerce . js_toHeapObject =<< I.getThunk x+      [jsu| `x.hst = null;+            `x.vnode = `u;+          |]+    _ -> return ()+++forcePatch :: J -> IO ()+forcePatch p = do+  thunks <- [jsu| h$vdom.forcePatch(`p) |]+  forceTree [thunks]++forceTree :: [J] -> IO ()+forceTree [] = return ()+forceTree (x:xs) = do+  x' <- fromJSArray x+  ys <- forM x' $ \t -> do+    forceThunkNode t+    newThunks <- [jsu| [] |]+    [jsu_| h$vdom.forceTree(`t.vnode, `newThunks) |]+    return newThunks+  forceTree (filter (\a -> [jsu'| `a.length !== 0 |]) ys ++ xs)++foreign import javascript unsafe+  "$r = $1;" js_toHeapObject :: JSVal -> IO Any
+ src/GHCJS/VDOM/Internal/Types.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE QuasiQuotes, TemplateHaskell, FlexibleInstances #-}++module GHCJS.VDOM.Internal.Types where++import qualified Data.JSString as JSS+import           Data.String (IsString(..))++import           GHCJS.Foreign.QQ+import           GHCJS.Types+import qualified GHCJS.Prim.Internal.Build+import qualified GHCJS.Prim.Internal.Build as IB++import           GHCJS.VDOM.Internal.TH++import           Unsafe.Coerce++-- do not export the constructors for these, this ensures that the objects are opaque+-- and cannot be mutated+newtype VNode      = VNode      { unVNode      :: JSVal }+newtype VComp      = VComp      { unVComp      :: JSVal }+newtype DComp      = DComp      { unDComp      :: JSVal }+newtype Patch      = Patch      { unPatch      :: JSVal }+newtype VMount     = VMount     { unVMount     :: JSVal }++-- fixme: make newtype?+-- newtype JSIdent = JSIdent JSVal+-- newtype DOMNode = DOMNode JSVal+type JSIdent = JSVal+type DOMNode = JSVal+++class Attributes a where+  mkAttributes :: a -> Attributes'+newtype Attributes' = Attributes' JSVal++data Attribute = Attribute JSString JSVal++class Children a where+  mkChildren :: a -> Children'+newtype Children' = Children' { unChildren :: JSVal }++instance Children Children' where+  mkChildren x = x+  {-# INLINE mkChildren #-}++instance Children () where+  mkChildren _ = Children' [jsu'| [] |]+  {-# INLINE mkChildren #-}++instance Children VNode where+  mkChildren (VNode v) =+    Children' [jsu'| [`v] |]+  {-# INLINE mkChildren #-}++mkTupleChildrenInstances ''Children 'mkChildren ''VNode 'VNode 'Children' [2..32]++instance Children [VNode] where+  mkChildren xs = Children' $ IB.buildArrayI (unsafeCoerce xs)+  {-# INLINE mkChildren #-}++instance Attributes Attributes' where+  mkAttributes x = x+  {-# INLINE mkAttributes #-}++instance Attributes () where+  mkAttributes _ = Attributes' [jsu'| {} |]+  {-# INLINE mkAttributes #-}++instance Attributes Attribute where+  mkAttributes (Attribute k v) =+    Attributes' (IB.buildObjectI1 (unsafeCoerce k) v)++instance Attributes [Attribute] where+  mkAttributes xs = Attributes' (IB.buildObjectI $+                                map (\(Attribute k v) -> (unsafeCoerce k,v)) xs)++mkTupleAttrInstances ''Attributes 'mkAttributes ''Attribute 'Attribute 'Attributes' [2..32]++instance IsString VNode where fromString xs = text (JSS.pack xs)++text :: JSString -> VNode+text xs = VNode [jsu'| h$vdom.t(`xs) |]+{-# INLINE text #-}
+ src/GHCJS/VDOM/QQ.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE QuasiQuotes, DeriveDataTypeable, TemplateHaskell #-}+{-+  More efficient JavaScript literals with QuasiQuoters++  mostly experimental, might not stay+ -}+module GHCJS.VDOM.QQ (ch, children, att, attributes) where++import           Language.Haskell.TH.Quote+import           Language.Haskell.TH.Syntax++import           GHCJS.VDOM.Internal.Types++import           GHCJS.Types+import           GHCJS.Marshal++import           Control.Applicative++import           Data.Char+import qualified Data.List as L+import           Data.List.Split+import           Data.Typeable++import           System.IO.Unsafe++att :: QuasiQuoter+att = attributes++ch :: QuasiQuoter+ch = children++-- example: [props|a:1, b: null, c: x, d: y |]+-- every value is either a literal or a variable referring to a convertible Haskell name+-- fixme, this does not have a proper parser+attributes :: QuasiQuoter+attributes = QuasiQuoter { quoteExp = quoteProps }++quoteProps :: String -> Q Exp+quoteProps pat = jsExpQQ ('{':ffiPat++"}") (map mkName names)+                 (\x -> AppE (VarE 'unsafePerformIO) (AppE (VarE 'toJSVal) x))+                 (AppE (ConE 'Attributes'))+  where+    (names, ffiPat) = genpat 1 $ map (break (==':') . trim) (linesBy (==',') pat)+    isName [] = False+    isName (x:xs) = isAlpha x && all isAlphaNum xs+    genpat :: Int -> [(String,String)] -> ([String], String)+    genpat _ [] = ([], "")+    genpat k ((x,':':n):xs)+      | isName n' = (n':ns, x ++ ": $" ++ (show k) ++ p)+      | otherwise = (ns, x ++ ':' : n ++ sep ++ p)+      where+        n'       = trim n+        ~(ns, p) = genpat (k+1) xs+        sep      = if null xs then "" else ","+    genpat _ _ = error "invalid pattern"+++-- example: [children|x,y,z|] for haskell names x,y,z :: VNode+children :: QuasiQuoter+children = QuasiQuoter { quoteExp = quoteChildren }++quoteChildren :: String -> Q Exp+quoteChildren pat = jsExpQQ ffiPat names (AppE (VarE 'unVNode)) (AppE (ConE 'Children'))+  where+    names  = map (mkName.trim) (linesBy (==',') pat)+    ffiPat = '[' : L.intercalate "," (map (('$':).show) (take (length names) [(1::Int)..])) ++ "]"++trim :: String -> String+trim = let f = reverse . dropWhile isSpace in f . f++newtype QQCounter = QQCounter { getCount :: Int } deriving (Typeable, Show)++jsExpQQ :: String -> [Name] -> (Exp -> Exp) -> (Exp -> Exp) -> Q Exp+jsExpQQ pat args unwrap wrap = do+  c <- maybe 0 getCount <$> qGetQ+  n <- newName ("__ghcjs_vdom_qq_spliced_" ++ show c)+  let ffiDecl      = ForeignD (ImportF CCall Unsafe pat' n (ty $ length args))+      ty :: Int -> Type+      ty 0         = ref+      ty n         = AppT (AppT ArrowT ref) (ty (n-1))+      ref          = ConT ''JSVal+      ffiCall []     = (VarE n)+      ffiCall (y:ys) = AppE (ffiCall ys) (unwrap (VarE y))+      pat'           = "__ghcjs_javascript_" ++ L.intercalate "_" (map (show . ord) pat)+  qAddTopDecls [ffiDecl]+  qPutQ (QQCounter (c+1))+  return $ wrap (ffiCall $ reverse args)+
+ src/GHCJS/VDOM/Render.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE TypeFamilies, QuasiQuotes, ScopedTypeVariables #-}+{-|+   Some utilities to manage a render queue for ghcjs-vdom components.++   Diffing is performed in an asynchronous background thread. When a+   patch is ready, an animationframe is requested to apply the update.++   Using the render queue is optional, directly calling diff and patch+   for the components is a lower level way to update the document.+ -}++module GHCJS.VDOM.Render ( Renderer+                         , render+                         , mkRenderer+                         ) where++import           GHCJS.Foreign.Callback+import           GHCJS.Foreign.QQ+import           GHCJS.Types++import           GHCJS.VDOM.Internal.Types+import qualified GHCJS.VDOM.Component as C+import qualified GHCJS.VDOM as V++import           JavaScript.Web.AnimationFrame++import           Control.Concurrent+import qualified Control.Exception as E+import           Control.Monad++import           Data.IORef+import           Data.Map (Map)+import qualified Data.Map as M+import           Data.Typeable++class Renderable a where+  type Render a+  doRender :: Renderer -> a -> Render a++instance Renderable VComp where+  type Render VComp = IO ()+  doRender r c@(VComp vcj) =+    enqueueRender r vcj (C.render c >>= C.diff c >>= addPatch vcj) (flushPatch vcj)++instance Renderable VMount where+  type Render VMount = (VNode -> IO ())+  doRender r vm@(VMount vmj) vn =+    enqueueRender r vmj (V.diff vm vn >>= addPatch vmj) (flushPatch vmj)++render :: Renderable a => Renderer -> a -> Render a+render r x = doRender r x++mkRenderer :: IO Renderer+mkRenderer = do+  r <- Renderer <$> newIORef M.empty+                <*> newIORef M.empty+                <*> newChan+  void (forkIO (renderThread r))+  return r++data Renderer = Renderer+  { renderPending      :: IORef (Map Double (IO (), IO ()))+  , renderFlushPending :: IORef (Map Double (IO ()))+  , renderQueue        :: Chan Double+  } deriving (Typeable)++renderThread :: Renderer -> IO ()+renderThread r = forever $ do+  k <- readChan (renderQueue r)+  Just (compute, flush) <-+    atomicModifyIORef (renderPending r) (\m -> (M.delete k m, M.lookup k m))+  let actions = do+        compute+        m <- atomicModifyIORef (renderFlushPending r)+                               (\m -> (M.insert k flush m, m))+        when (M.null m) (void $ inAnimationFrame ThrowWouldBlock+                                                 (\_ -> flushPatches r))+  actions`E.catch` \(_::E.SomeException) -> return ()++flushPatches :: Renderer -> IO ()+flushPatches r =+  mapM_ (\m -> m `E.catch` \(_::E.SomeException) -> return ()) =<<+    atomicModifyIORef (renderFlushPending r)+                      (\m -> (M.empty, M.elems m))++enqueueRender :: Renderer+              -> JSVal+              -> IO ()+              -> IO ()+              -> IO ()+enqueueRender r renderable compute flush = do+  k <- renderableKey renderable+  m <- atomicModifyIORef (renderPending r)+                         (\m -> (M.insert k (compute, flush) m, m))+  when (M.notMember k m) (writeChan (renderQueue r) k)++renderableKey :: JSVal -> IO Double+renderableKey r = [jsu| `r._key |]++addPatch :: JSVal -> Patch -> IO ()+addPatch r (Patch p) = [jsu_| `r.addPatch(`p); |]++flushPatch :: JSVal -> IO ()+flushPatch r = [jsu_| `r.patch(null); |]
+ src/GHCJS/VDOM/Unsafe.hs view
@@ -0,0 +1,31 @@+{-|+   Unsafe API for constructing Children' and Attributes', useful for+   writing new Children and Attributes instances+ -}+module GHCJS.VDOM.Unsafe ( Attributes(..),       Children(..)+                         , Attributes',          Children'+                         , unsafeToAttributes,   unsafeToChildren ) where++import GHCJS.Types++import GHCJS.VDOM.Internal.Types++{-|+   Convert a JSVal, which must be a JS object, to Attributes'. The object+   may not be mutated.++   FIXME what do we need to always be able to find Haskell callbacks?+ -}+unsafeToAttributes :: JSVal -> Attributes'+unsafeToAttributes = Attributes'++{-|+   Convert a JSVal, which must be an array of virtual-dom nodes+   to Children'. The array and the child nodes may not be mutated.++   note: All nodes must either be a virtual node or an instance of HSThunk,+   if you use other node types, the extensible retention (see scanTree in+   virtual-dom/lib.require.js) must be extended.+ -}+unsafeToChildren :: JSVal -> Children'+unsafeToChildren = Children'
+ virtual-dom/LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2014 Matt-Esch.++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ virtual-dom/README.md view
@@ -0,0 +1,13 @@+to initiate:++    $ npm install++to build++    $ grunt++(install grunt with: npm install -g grunt-cli)++to watch for changes and rebuild++    $ grunt watch
+ virtual-dom/diff.js view
@@ -0,0 +1,445 @@+/*+   vtree/diff module modified to defer rendering thunks. this makes it possible to+   implement thunks that cannot be called directly as a function, but have+   an asynchronous callback or require evaluation in some specific runtime+   environment+ */++var isArray = require("x-is-array")++var VPatch = require("virtual-dom/vnode/vpatch")+var isVNode = require("virtual-dom/vnode/is-vnode")+var isVText = require("virtual-dom/vnode/is-vtext")+var isWidget = require("virtual-dom/vnode/is-widget")+var isThunk = require("virtual-dom/vnode/is-thunk")+var handleThunk = require("./handle-thunk")++var diffProps = require("virtual-dom/vtree/diff-props")++module.exports = diff++// unevaluated thunks are added to the thunks argument (array)+function diff(a, b, thunks) {+    if(!a) throw new Error ("diff a: " + a);+    if(!a) throw new Error ("diff b: " + b);+    var patch = { a: a }+    walk(a, b, patch, thunks, 0)+    return patch+}++function walk(a, b, patch, thunks, index) {+    if (a === b) {+        return+    }++    var apply = patch[index]+    var applyClear = false++    if (isThunk(a) || isThunk(b)) {+        doThunks(a, b, patch, thunks, index)+    } else if (b == null) {++        // If a is a widget we will add a remove patch for it+        // Otherwise any child widgets/hooks must be destroyed.+        // This prevents adding two remove patches for a widget.+        if (!isWidget(a)) {+            clearState(a, patch, index)+            apply = patch[index]+        }++        apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b))+    } else if (isVNode(b)) {+        if (isVNode(a)) {+            if (a.tagName === b.tagName &&+                a.namespace === b.namespace &&+                a.key === b.key) {+                var propsPatch = diffProps(a.properties, b.properties)+                if (propsPatch) {+                    apply = appendPatch(apply,+                        new VPatch(VPatch.PROPS, a, propsPatch))+                }+                apply = diffChildren(a, b, patch, apply, thunks, index)+            } else {+                apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))+                applyClear = true+            }+        } else {+            apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))+            applyClear = true+        }+    } else if (isVText(b)) {+        if (!isVText(a)) {+            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))+            applyClear = true+        } else if (a.text !== b.text) {+            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))+        }+    } else if (isWidget(b)) {+        if (!isWidget(a)) {+            applyClear = true+        }++        apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b))+    }++    if (apply) {+        patch[index] = apply+    }++    if (applyClear) {+        clearState(a, patch, index)+    }+}++function diffChildren(a, b, patch, apply, thunks, index) {+    var aChildren = a.children+    var orderedSet = reorder(aChildren, b.children)+    var bChildren = orderedSet.children++    var aLen = aChildren.length+    var bLen = bChildren.length+    var len = aLen > bLen ? aLen : bLen++    for (var i = 0; i < len; i++) {+        var leftNode = aChildren[i]+        var rightNode = bChildren[i]+        index += 1++        if (!leftNode) {+            if (rightNode) {+                // Excess nodes in b need to be added+                apply = appendPatch(apply,+                    new VPatch(VPatch.INSERT, null, rightNode))+            }+        } else {+            walk(leftNode, rightNode, patch, thunks, index)+        }++        if (isVNode(leftNode) && leftNode.count) {+            index += leftNode.count+        }+    }++    if (orderedSet.moves) {+        // Reorder nodes last+        apply = appendPatch(apply, new VPatch(+            VPatch.ORDER,+            a,+            orderedSet.moves+        ))+    }++    return apply+}++function clearState(vNode, patch, index) {+    // TODO: Make this a single walk, not two+    unhook(vNode, patch, index)+    destroyWidgets(vNode, patch, index)+}++// Patch records for all destroyed widgets must be added because we need+// a DOM node reference for the destroy function+function destroyWidgets(vNode, patch, index) {+    if (isWidget(vNode)) {+        if (typeof vNode.destroy === "function") {+            patch[index] = appendPatch(+                patch[index],+                new VPatch(VPatch.REMOVE, vNode, null)+            )+        }+    } else if (isVNode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) {+        var children = vNode.children+        var len = children.length+        for (var i = 0; i < len; i++) {+            var child = children[i]+            index += 1++            destroyWidgets(child, patch, index)++            if (isVNode(child) && child.count) {+                index += child.count+            }+        }+    } else if (isThunk(vNode)) {+        doThunks(vNode, null, patch, thunks, index)+    }+}++// Create a sub-patch for thunks+function doThunks(a, b, patch, thunks, index) {+    var ts = handleThunk(a, b);+    if(ts.a || ts.b) {+        // defer rendering, caller is responsible for:+        //   - filling ts.a.vnode / ts.b.vnode with the result from the thunk+        //   - ts.p[t.sp] = new VPatch(VPatch.THUNK, null, diff(ts.a.vnode, ts.b.vnode))+        // before using it with patch+        thunks.push({ i: index, p: patch, a: a, b: b});+    } else {+        var thunkPatch = diff(a.vnode, b.vnode, thunks)+        if (hasPatches(thunkPatch)) {+            patch[index] = new VPatch(VPatch.THUNK, null, thunkPatch)+        }+    }+}++function hasPatches(patch) {+    for (var index in patch) {+        if (index !== "a") {+            return true+        }+    }++    return false+}++// Execute hooks when two nodes are identical+function unhook(vNode, patch, index) {+    if (isVNode(vNode)) {+        if (vNode.hooks) {+            patch[index] = appendPatch(+                patch[index],+                new VPatch(+                    VPatch.PROPS,+                    vNode,+                    undefinedKeys(vNode.hooks)+                )+            )+        }++        if (vNode.descendantHooks || vNode.hasThunks) {+            var children = vNode.children+            var len = children.length+            for (var i = 0; i < len; i++) {+                var child = children[i]+                index += 1++                unhook(child, patch, index)++                if (isVNode(child) && child.count) {+                    index += child.count+                }+            }+        }+    } else if (isThunk(vNode)) {+        doThunks(vNode, null, patch, thunks, index)+    }+}++function undefinedKeys(obj) {+    var result = {}++    for (var key in obj) {+        result[key] = undefined+    }++    return result+}++// List diff, naive left to right reordering+function reorder(aChildren, bChildren) {+    // O(M) time, O(M) memory+    var bChildIndex = keyIndex(bChildren)+    var bKeys = bChildIndex.keys+    var bFree = bChildIndex.free++    if (bFree.length === bChildren.length) {+        return {+            children: bChildren,+            moves: null+        }+    }++    // O(N) time, O(N) memory+    var aChildIndex = keyIndex(aChildren)+    var aKeys = aChildIndex.keys+    var aFree = aChildIndex.free++    if (aFree.length === aChildren.length) {+        return {+            children: bChildren,+            moves: null+        }+    }++    // O(MAX(N, M)) memory+    var newChildren = []++    var freeIndex = 0+    var freeCount = bFree.length+    var deletedItems = 0++    // Iterate through a and match a node in b+    // O(N) time,+    for (var i = 0 ; i < aChildren.length; i++) {+        var aItem = aChildren[i]+        var itemIndex++        if (aItem.key) {+            if (bKeys.hasOwnProperty(aItem.key)) {+                // Match up the old keys+                itemIndex = bKeys[aItem.key]+                newChildren.push(bChildren[itemIndex])++            } else {+                // Remove old keyed items+                itemIndex = i - deletedItems+++                newChildren.push(null)+            }+        } else {+            // Match the item in a with the next free item in b+            if (freeIndex < freeCount) {+                itemIndex = bFree[freeIndex++]+                newChildren.push(bChildren[itemIndex])+            } else {+                // There are no free items in b to match with+                // the free items in a, so the extra free nodes+                // are deleted.+                itemIndex = i - deletedItems+++                newChildren.push(null)+            }+        }+    }++    var lastFreeIndex = freeIndex >= bFree.length ?+        bChildren.length :+        bFree[freeIndex]++    // Iterate through b and append any new keys+    // O(M) time+    for (var j = 0; j < bChildren.length; j++) {+        var newItem = bChildren[j]++        if (newItem.key) {+            if (!aKeys.hasOwnProperty(newItem.key)) {+                // Add any new keyed items+                // We are adding new items to the end and then sorting them+                // in place. In future we should insert new items in place.+                newChildren.push(newItem)+            }+        } else if (j >= lastFreeIndex) {+            // Add any leftover non-keyed items+            newChildren.push(newItem)+        }+    }++    var simulate = newChildren.slice()+    var simulateIndex = 0+    var removes = []+    var inserts = []+    var simulateItem++    for (var k = 0; k < bChildren.length;) {+        var wantedItem = bChildren[k]+        simulateItem = simulate[simulateIndex]++        // remove items+        while (simulateItem === null && simulate.length) {+            removes.push(remove(simulate, simulateIndex, null))+            simulateItem = simulate[simulateIndex]+        }++        if (!simulateItem || simulateItem.key !== wantedItem.key) {+            // if we need a key in this position...+            if (wantedItem.key) {+                if (simulateItem && simulateItem.key) {+                    // if an insert doesn't put this key in place, it needs to move+                    if (bKeys[simulateItem.key] !== k + 1) {+                        removes.push(remove(simulate, simulateIndex, simulateItem.key))+                        simulateItem = simulate[simulateIndex]+                        // if the remove didn't put the wanted item in place, we need to insert it+                        if (!simulateItem || simulateItem.key !== wantedItem.key) {+                            inserts.push({key: wantedItem.key, to: k})+                        }+                        // items are matching, so skip ahead+                        else {+                            simulateIndex+++                        }+                    }+                    else {+                        inserts.push({key: wantedItem.key, to: k})+                    }+                }+                else {+                    inserts.push({key: wantedItem.key, to: k})+                }+                k+++            }+            // a key in simulate has no matching wanted key, remove it+            else if (simulateItem && simulateItem.key) {+                removes.push(remove(simulate, simulateIndex, simulateItem.key))+            }+        }+        else {+            simulateIndex+++            k+++        }+    }++    // remove all the remaining nodes from simulate+    while(simulateIndex < simulate.length) {+        simulateItem = simulate[simulateIndex]+        removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))+    }++    // If the only moves we have are deletes then we can just+    // let the delete patch remove these items.+    if (removes.length === deletedItems && !inserts.length) {+        return {+            children: newChildren,+            moves: null+        }+    }++    return {+        children: newChildren,+        moves: {+            removes: removes,+            inserts: inserts+        }+    }+}++function remove(arr, index, key) {+    arr.splice(index, 1)++    return {+        from: index,+        key: key+    }+}++function keyIndex(children) {+    var keys = {}+    var free = []+    var length = children.length++    for (var i = 0; i < length; i++) {+        var child = children[i]++        if (child.key) {+            keys[child.key] = i+        } else {+            free.push(i)+        }+    }++    return {+        keys: keys,     // A hash of key name to index+        free: free      // An array of unkeyed item indices+    }+}++function appendPatch(apply, patch) {+    if (apply) {+        if (isArray(apply)) {+            apply.push(patch)+        } else {+            apply = [apply, patch]+        }++        return apply+    } else {+        return patch+    }+}
+ virtual-dom/handle-thunk.js view
@@ -0,0 +1,18 @@+var isVNode = require("virtual-dom/vnode/is-vnode")+var isVText = require("virtual-dom/vnode/is-vtext")+var isWidget = require("virtual-dom/vnode/is-widget")+var isThunk = require("virtual-dom/vnode/is-thunk")++module.exports = handleThunk++function handleThunk(a, b) {+    return { a: isThunk(a) ? renderThunk(a, null) : null+           , b: isThunk(b) ? renderThunk(b, a) : null+           }+}++function renderThunk(thunk, previous) {+    if(thunk.vnode) return null;+    thunk.render(previous);+    return thunk.vnode ? null : thunk;+}
+ virtual-dom/lib.js view
@@ -0,0 +1,2710 @@+(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){+/*+   vtree/diff module modified to defer rendering thunks. this makes it possible to+   implement thunks that cannot be called directly as a function, but have+   an asynchronous callback or require evaluation in some specific runtime+   environment+ */++var isArray = require("x-is-array")++var VPatch = require("virtual-dom/vnode/vpatch")+var isVNode = require("virtual-dom/vnode/is-vnode")+var isVText = require("virtual-dom/vnode/is-vtext")+var isWidget = require("virtual-dom/vnode/is-widget")+var isThunk = require("virtual-dom/vnode/is-thunk")+var handleThunk = require("./handle-thunk")++var diffProps = require("virtual-dom/vtree/diff-props")++module.exports = diff++// unevaluated thunks are added to the thunks argument (array)+function diff(a, b, thunks) {+    if(!a) throw new Error ("diff a: " + a);+    if(!a) throw new Error ("diff b: " + b);+    var patch = { a: a }+    walk(a, b, patch, thunks, 0)+    return patch+}++function walk(a, b, patch, thunks, index) {+    if (a === b) {+        return+    }++    var apply = patch[index]+    var applyClear = false++    if (isThunk(a) || isThunk(b)) {+        doThunks(a, b, patch, thunks, index)+    } else if (b == null) {++        // If a is a widget we will add a remove patch for it+        // Otherwise any child widgets/hooks must be destroyed.+        // This prevents adding two remove patches for a widget.+        if (!isWidget(a)) {+            clearState(a, patch, index)+            apply = patch[index]+        }++        apply = appendPatch(apply, new VPatch(VPatch.REMOVE, a, b))+    } else if (isVNode(b)) {+        if (isVNode(a)) {+            if (a.tagName === b.tagName &&+                a.namespace === b.namespace &&+                a.key === b.key) {+                var propsPatch = diffProps(a.properties, b.properties)+                if (propsPatch) {+                    apply = appendPatch(apply,+                        new VPatch(VPatch.PROPS, a, propsPatch))+                }+                apply = diffChildren(a, b, patch, apply, thunks, index)+            } else {+                apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))+                applyClear = true+            }+        } else {+            apply = appendPatch(apply, new VPatch(VPatch.VNODE, a, b))+            applyClear = true+        }+    } else if (isVText(b)) {+        if (!isVText(a)) {+            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))+            applyClear = true+        } else if (a.text !== b.text) {+            apply = appendPatch(apply, new VPatch(VPatch.VTEXT, a, b))+        }+    } else if (isWidget(b)) {+        if (!isWidget(a)) {+            applyClear = true+        }++        apply = appendPatch(apply, new VPatch(VPatch.WIDGET, a, b))+    }++    if (apply) {+        patch[index] = apply+    }++    if (applyClear) {+        clearState(a, patch, index)+    }+}++function diffChildren(a, b, patch, apply, thunks, index) {+    var aChildren = a.children+    var orderedSet = reorder(aChildren, b.children)+    var bChildren = orderedSet.children++    var aLen = aChildren.length+    var bLen = bChildren.length+    var len = aLen > bLen ? aLen : bLen++    for (var i = 0; i < len; i++) {+        var leftNode = aChildren[i]+        var rightNode = bChildren[i]+        index += 1++        if (!leftNode) {+            if (rightNode) {+                // Excess nodes in b need to be added+                apply = appendPatch(apply,+                    new VPatch(VPatch.INSERT, null, rightNode))+            }+        } else {+            walk(leftNode, rightNode, patch, thunks, index)+        }++        if (isVNode(leftNode) && leftNode.count) {+            index += leftNode.count+        }+    }++    if (orderedSet.moves) {+        // Reorder nodes last+        apply = appendPatch(apply, new VPatch(+            VPatch.ORDER,+            a,+            orderedSet.moves+        ))+    }++    return apply+}++function clearState(vNode, patch, index) {+    // TODO: Make this a single walk, not two+    unhook(vNode, patch, index)+    destroyWidgets(vNode, patch, index)+}++// Patch records for all destroyed widgets must be added because we need+// a DOM node reference for the destroy function+function destroyWidgets(vNode, patch, index) {+    if (isWidget(vNode)) {+        if (typeof vNode.destroy === "function") {+            patch[index] = appendPatch(+                patch[index],+                new VPatch(VPatch.REMOVE, vNode, null)+            )+        }+    } else if (isVNode(vNode) && (vNode.hasWidgets || vNode.hasThunks)) {+        var children = vNode.children+        var len = children.length+        for (var i = 0; i < len; i++) {+            var child = children[i]+            index += 1++            destroyWidgets(child, patch, index)++            if (isVNode(child) && child.count) {+                index += child.count+            }+        }+    } else if (isThunk(vNode)) {+        doThunks(vNode, null, patch, thunks, index)+    }+}++// Create a sub-patch for thunks+function doThunks(a, b, patch, thunks, index) {+    var ts = handleThunk(a, b);+    if(ts.a || ts.b) {+        // defer rendering, caller is responsible for:+        //   - filling ts.a.vnode / ts.b.vnode with the result from the thunk+        //   - ts.p[t.sp] = new VPatch(VPatch.THUNK, null, diff(ts.a.vnode, ts.b.vnode))+        // before using it with patch+        thunks.push({ i: index, p: patch, a: a, b: b});+    } else {+        var thunkPatch = diff(a.vnode, b.vnode, thunks)+        if (hasPatches(thunkPatch)) {+            patch[index] = new VPatch(VPatch.THUNK, null, thunkPatch)+        }+    }+}++function hasPatches(patch) {+    for (var index in patch) {+        if (index !== "a") {+            return true+        }+    }++    return false+}++// Execute hooks when two nodes are identical+function unhook(vNode, patch, index) {+    if (isVNode(vNode)) {+        if (vNode.hooks) {+            patch[index] = appendPatch(+                patch[index],+                new VPatch(+                    VPatch.PROPS,+                    vNode,+                    undefinedKeys(vNode.hooks)+                )+            )+        }++        if (vNode.descendantHooks || vNode.hasThunks) {+            var children = vNode.children+            var len = children.length+            for (var i = 0; i < len; i++) {+                var child = children[i]+                index += 1++                unhook(child, patch, index)++                if (isVNode(child) && child.count) {+                    index += child.count+                }+            }+        }+    } else if (isThunk(vNode)) {+        doThunks(vNode, null, patch, thunks, index)+    }+}++function undefinedKeys(obj) {+    var result = {}++    for (var key in obj) {+        result[key] = undefined+    }++    return result+}++// List diff, naive left to right reordering+function reorder(aChildren, bChildren) {+    // O(M) time, O(M) memory+    var bChildIndex = keyIndex(bChildren)+    var bKeys = bChildIndex.keys+    var bFree = bChildIndex.free++    if (bFree.length === bChildren.length) {+        return {+            children: bChildren,+            moves: null+        }+    }++    // O(N) time, O(N) memory+    var aChildIndex = keyIndex(aChildren)+    var aKeys = aChildIndex.keys+    var aFree = aChildIndex.free++    if (aFree.length === aChildren.length) {+        return {+            children: bChildren,+            moves: null+        }+    }++    // O(MAX(N, M)) memory+    var newChildren = []++    var freeIndex = 0+    var freeCount = bFree.length+    var deletedItems = 0++    // Iterate through a and match a node in b+    // O(N) time,+    for (var i = 0 ; i < aChildren.length; i++) {+        var aItem = aChildren[i]+        var itemIndex++        if (aItem.key) {+            if (bKeys.hasOwnProperty(aItem.key)) {+                // Match up the old keys+                itemIndex = bKeys[aItem.key]+                newChildren.push(bChildren[itemIndex])++            } else {+                // Remove old keyed items+                itemIndex = i - deletedItems+++                newChildren.push(null)+            }+        } else {+            // Match the item in a with the next free item in b+            if (freeIndex < freeCount) {+                itemIndex = bFree[freeIndex++]+                newChildren.push(bChildren[itemIndex])+            } else {+                // There are no free items in b to match with+                // the free items in a, so the extra free nodes+                // are deleted.+                itemIndex = i - deletedItems+++                newChildren.push(null)+            }+        }+    }++    var lastFreeIndex = freeIndex >= bFree.length ?+        bChildren.length :+        bFree[freeIndex]++    // Iterate through b and append any new keys+    // O(M) time+    for (var j = 0; j < bChildren.length; j++) {+        var newItem = bChildren[j]++        if (newItem.key) {+            if (!aKeys.hasOwnProperty(newItem.key)) {+                // Add any new keyed items+                // We are adding new items to the end and then sorting them+                // in place. In future we should insert new items in place.+                newChildren.push(newItem)+            }+        } else if (j >= lastFreeIndex) {+            // Add any leftover non-keyed items+            newChildren.push(newItem)+        }+    }++    var simulate = newChildren.slice()+    var simulateIndex = 0+    var removes = []+    var inserts = []+    var simulateItem++    for (var k = 0; k < bChildren.length;) {+        var wantedItem = bChildren[k]+        simulateItem = simulate[simulateIndex]++        // remove items+        while (simulateItem === null && simulate.length) {+            removes.push(remove(simulate, simulateIndex, null))+            simulateItem = simulate[simulateIndex]+        }++        if (!simulateItem || simulateItem.key !== wantedItem.key) {+            // if we need a key in this position...+            if (wantedItem.key) {+                if (simulateItem && simulateItem.key) {+                    // if an insert doesn't put this key in place, it needs to move+                    if (bKeys[simulateItem.key] !== k + 1) {+                        removes.push(remove(simulate, simulateIndex, simulateItem.key))+                        simulateItem = simulate[simulateIndex]+                        // if the remove didn't put the wanted item in place, we need to insert it+                        if (!simulateItem || simulateItem.key !== wantedItem.key) {+                            inserts.push({key: wantedItem.key, to: k})+                        }+                        // items are matching, so skip ahead+                        else {+                            simulateIndex+++                        }+                    }+                    else {+                        inserts.push({key: wantedItem.key, to: k})+                    }+                }+                else {+                    inserts.push({key: wantedItem.key, to: k})+                }+                k+++            }+            // a key in simulate has no matching wanted key, remove it+            else if (simulateItem && simulateItem.key) {+                removes.push(remove(simulate, simulateIndex, simulateItem.key))+            }+        }+        else {+            simulateIndex+++            k+++        }+    }++    // remove all the remaining nodes from simulate+    while(simulateIndex < simulate.length) {+        simulateItem = simulate[simulateIndex]+        removes.push(remove(simulate, simulateIndex, simulateItem && simulateItem.key))+    }++    // If the only moves we have are deletes then we can just+    // let the delete patch remove these items.+    if (removes.length === deletedItems && !inserts.length) {+        return {+            children: newChildren,+            moves: null+        }+    }++    return {+        children: newChildren,+        moves: {+            removes: removes,+            inserts: inserts+        }+    }+}++function remove(arr, index, key) {+    arr.splice(index, 1)++    return {+        from: index,+        key: key+    }+}++function keyIndex(children) {+    var keys = {}+    var free = []+    var length = children.length++    for (var i = 0; i < length; i++) {+        var child = children[i]++        if (child.key) {+            keys[child.key] = i+        } else {+            free.push(i)+        }+    }++    return {+        keys: keys,     // A hash of key name to index+        free: free      // An array of unkeyed item indices+    }+}++function appendPatch(apply, patch) {+    if (apply) {+        if (isArray(apply)) {+            apply.push(patch)+        } else {+            apply = [apply, patch]+        }++        return apply+    } else {+        return patch+    }+}++},{"./handle-thunk":2,"virtual-dom/vnode/is-thunk":37,"virtual-dom/vnode/is-vnode":39,"virtual-dom/vnode/is-vtext":40,"virtual-dom/vnode/is-widget":41,"virtual-dom/vnode/vpatch":44,"virtual-dom/vtree/diff-props":46,"x-is-array":47}],2:[function(require,module,exports){+var isVNode = require("virtual-dom/vnode/is-vnode")+var isVText = require("virtual-dom/vnode/is-vtext")+var isWidget = require("virtual-dom/vnode/is-widget")+var isThunk = require("virtual-dom/vnode/is-thunk")++module.exports = handleThunk++function handleThunk(a, b) {+    return { a: isThunk(a) ? renderThunk(a, null) : null+           , b: isThunk(b) ? renderThunk(b, a) : null+           }+}++function renderThunk(thunk, previous) {+    if(thunk.vnode) return null;+    thunk.render(previous);+    return thunk.vnode ? null : thunk;+}++},{"virtual-dom/vnode/is-thunk":37,"virtual-dom/vnode/is-vnode":39,"virtual-dom/vnode/is-vtext":40,"virtual-dom/vnode/is-widget":41}],3:[function(require,module,exports){+/*+  to generate lib.js, install virtual-dom and process file:++     $ npm install+     $ grunt+   the ./diff module is vtree/diff with a few changes to+   allow diff to run in an asynchronous thread in the presence of+   memoized nodes.+ */++/*+   Note on memory management:++   To ensure accurate heap tracing for finalization and profiling purposes,+   GHCJS needs to know reachable all Haskell values. ghcjs-vdom stores some+   Haskell values inside JavaScript references and uses extensible retention+   to collect these values. It's crucial that all data structures that may+   contain Haskell values are stored directly in a JSVal and not inside other+   JS data structures.++   The recognized types are:++     - HSPatch:+         The patch object contains the new (target) virtual-dom tree and+         the original tree is reachable trough the parent. Since all handlers,+         components and thunks are reachable through these trees, the patch itself+         does not need to be traversed.++     - HSMount:+         All mounted HSMount points are scanned as roots. The current virtual tree+         (mount.vtree) is traversed for this.++     - HSComponent:+         Traversed when reachable through Haskell heap or virtual-dom tree. Contains+         current tree (component.vtree) and rendering action (component.hsRender)++     - HSThunk:+         Traversed when reachable through Haskell heap or virtual-dom tree. Contains+         Haskell suspension (thunk.hst) or rendered tree (thunk.vnode)++     - virtual node ( isVirtualNode(x) )+         Some node in a virtual-dom tree. Haskell event handlers are stored in 'ev-*'+         properties, which virtual-dom adds to the node's hooks (vnode.hooks). If+         a virtual node contains thunks, widgets or any of its descendants have hooks,+         the children of the node have to be traversed.++   forceThunks and forcePatch fill an array of thunks, which is not directly+   scannable; however, these functions are only used as part of a `diff` operation.+   The initial diff creates an HSPatch object, through which the original and target+   virtual-dom tree are completely reachable.+ */++var isVirtualNode = require('virtual-dom/vnode/is-vnode');+var isThunk       = require('virtual-dom/vnode/is-thunk');+var isWidget      = require("virtual-dom/vnode/is-widget");+var h             = require('virtual-dom/h');+var isArray       = require('x-is-array');+var VPatch        = require("virtual-dom/vnode/vpatch");+var VText         = require('virtual-dom/vnode/vtext');+var vdomPatch     = require('virtual-dom/vdom/patch');+var DomDelegator  = require('dom-delegator');++var diff = require('./diff');++var VRenderableN = 0;++/** @constructor */+function HSPatch(patch, old, vnode, parent) {+  this.patch   = patch;+  this.old     = old;+  this.vnode   = vnode;+  this.parent  = parent;+}++/** @constructor */+function HSThunk(t, ids, key) {+  this.hst        = t;   // haskell thunk+  this.ids        = ids; // array of haskell unique ids+  this.key        = key;+  this.vnode      = null;+  this._ghcjsMark = 0;+}++HSThunk.prototype.type = 'Thunk';++/* +  render returns the deferred rendering object+  null if the thunk has already been rendered, in which case the value is in this.vnode+ */+HSThunk.prototype.render = function(previous) {+  if(previous && !this.vnode && eqIds(this.ids, previous.ids)) {+    if(previous.hst) {+      this.hst = previous.hst;+    } else {+      this.hst   = null;+      this.vnode = previous.vnode;+    }+  }+  return this.vnode ? null : this;+}++/** @constructor */+function HSComponent(r, mnt, unmnt, key) {+  this._key      = ++VRenderableN;+  this.hsRender  = r;   // IO action that produces a vdom tree+  this.hsMount   = mnt;+  this.hsUnmount = unmnt;+  this.key       = key || this._key;+  this.vnode     = this.initialVnode = new VText("");+  this.pending   = [];+  this.mounts    = {};+  this.version   = 0;+  this.latest    = 0;+}++HSComponent.prototype.type = 'Widget';++HSComponent.prototype.init = function() {+  var n = document.createTextNode('');+  if(this.vnode !== this.initialVnode) {+    var thunks = [];+    var p = diff(this.initialVnode, this.vnode, thunks);+    if(thunks.length !== 0) {+      throw new Error("HSComponent vnode contains unevaluated thunks");+    }+    n = vdomPatch(n, p);+  }+  var m = new HSComponentMount(n);+  n._key = m._key;+  n._widget = this;+  this.mounts[m._key] = m;+  if(this.hsMount) {+    h$vdomMountComponentCallback(this.hsMount, m._key, this);+  }+  return n;+}++HSComponent.prototype.destroy = function(domNode) {+  delete this.mounts[domNode._key];+  if(this.hsUnmount) {+    h$vdomUnmountComponentCallback(this.hsUnmount, domNode._key, domNode);+  }+}++HSComponent.prototype.diff = function(v, thunks) {+  var vn = this.vnode;+  if(this.pending.length > 0) {+    vn = this.pending[this.pending.length-1].vnode;+  }+  return new HSPatch( diff(vn, v, thunks)+		    , vn+		    , v+		    , this);+}++HSComponent.prototype.addPatch = function(p) {+  var cur = this.pending.length > 0 ? this.pending[this.pending.length-1]+                                    : this.vnode;+  if(p.old === cur) this.pending.push(p);+}++HSComponent.prototype.patch = function(p) {+  if(this.pending.length > 0) {+    var pnd = this.pending;+    this.pending = [];+    for(var i = 0; i < pnd.length; i++) this.patch(pnd[i]);+  }+  if(!p) return;+  if(p.parent !== this || p.old !== this.vnode) {+    return false;+  }+  for(var k in this.mounts) {+    var m = this.mounts[k];+    m.node = vdomPatch(m.node, p.patch);+  }+  this.vnode = p.vnode;+  return true;+}++// only use this for manually updated components (i.e. no diff/patch)+HSComponent.prototype.updateMount = function(mnt, node) {+  var m = this.mounts[mnt];+  node._key = mnt;+  node._widget = this;+  m.node.parentNode.replaceChild(node, m.node);+  m.node = node;+}++HSComponent.prototype.update = function(leftVNode, node) {+  if(node._widget) {+    if(node._widget == this) return node;+    node._widget.destroy(node);+  }+  return this.init();+}++var HSComponentMountN = 0;+function HSComponentMount(domNode) {+  this._key = ++HSComponentMountN;+  this.node = domNode;+}++/** @constructor */+function HSMount(domNode) {+  this._key       = ++VRenderableN;+  // this.version    = 0;+  this.vnode      = new VText(""); // currently rendered vdom tree+  this.pending    = [];            // pending patches, not yet applied+  this.node       = document.createTextNode("");+  this.parentNode = domNode;+}++HSMount.prototype.diff = function(v, thunks) {+  var vn = this.vnode;+  if(this.pending.length > 0) {+    vn = this.pending[this.pending.length-1].vnode;+  }+  return new HSPatch( diff(vn, v, thunks)+		    , vn+		    , v+		    , this);+}++HSMount.prototype.addPatch = function(p) {+  var cur = this.pending.length > 0 ? this.pending[this.pending.length-1]+                                    : this.vnode;+  if(p.old === cur) this.pending.push(p);+}++// HSMount.patch(null) to flush pending list+HSMount.prototype.patch = function(p) {+  if(this.pending.length > 0) {+    var pnd = this.pending;+    this.pending = [];+    for(var i = 0; i < pnd.length; i++) this.patch(pnd[i]);+  }+  if(!p) return;+  if(p.parent !== this || p.old !== this.vnode) {+    return false;+  }+  this.node  = vdomPatch(this.node, p.patch);+  this.vnode = p.vnode;+  return true;+}+  +/* mount a vdom tree, making it visible to extensible retention */+function mount(domNode) {+  while(domNode.firstChild) domNode.removeChild(domNode.firstChild);+  var m = new HSMount(domNode);+  domNode.appendChild(m.node);+  vdomMounts.add(m);+  return m;+}++/* unmount a tree, removing all child nodes. */+function unmount(vmount) {+  var n = vmount.parentNode;+  while(n.firstChild) n.removeChild(n.firstChild);+  vdomMounts.remove(vmount);+}++/*+   Compare lists of object identifiers associated with a thunk node. If the lists are equal,+   the subtree does not have to be recomputed.+*/+function eqIds(ids1, ids2) {+  if(!ids1 || !ids2 || ids1.length != ids2.length) return false;+  for(var i=ids1.length-1;i>=0;i--) {+    var id1 = ids1[i], id2 = ids2[i];+    if(typeof id1 === 'number') {+      if(typeof id2 !== 'number') return false;+      if(id1 !== id2 && !((id1!=id1) && (id2!=id2))) return false;+    } else {+      if(id1 !== id2) return false;+    }+  }+  return true;+}++function forcePatch(p) {+  var thunks = [], i, j, pi;+  for(i in p) {+    var pi = p[i];+    if(isArray(pi)) {+      for(j=pi.length-1;j>=0;j--) {+	forceTree(pi[j].patch, thunks);+      }+    }+    else if(pi.patch) forceTree(pi.patch, thunks);+    else forceTree(pi, thunks);+  }+  return thunks;+}++function forceTree(n, t) {+  if(isThunk(n)) {+    if(n.vnode) forceTree(n.vnode, t);+    else t.push(n);+  } else if(isVirtualNode(n) && n.hasThunks) {+    for(var i=n.children.length-1;i>=0;i--) {+      forceTree(n.children[i], t);+    }+  }+}++/*+   scan all mounted virtual-dom trees+ */+function scanMounts(currentMark) {+  var i = vdomMounts.iter(), m, res = [];+  while((m = i.next()) !== null) {+    scanTreeRec(m.vnode, res, currentMark);+    if(m.pending.length > 0) {+      scanTreeRec(m.pending[m.pending.length-1].vnode, res, currentMark);+    }+  }+  return res;+}++/*+   scan a tree (extensible retention callback).++   returns:+     - an array of haskell items if any+     - true if no haskell items have been found+     - false if the object is not a ghcjs-vdom tree+         (fallthrough to other extensible retention scanners)+ */+var scanTreeRes = [];+function scanTree(o, currentMark) {+  if(isVirtualNode(o) || isThunk(o) || isWidget(o) ||+     o instanceof HSPatch || o instanceof HSComponent || o instanceof HSMount) {+    var r = scanTreeRes;+    scanTreeRec(o, r, currentMark);+    if(r.length > 0) {+      scanTreeRes = [];+      return r;+    } else {+      return true;+    }+  } else { // not a ghcjs-vdom object, fall through+    return false; +  }+}++function scanTreeRec(o, r, currentMark) {+  if(o instanceof HSPatch) {+    scanTreeRec(o.vnode, r, currentMark);+    scanTreeRec(o.parent);+  } else if(o instanceof HSThunk) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      if(o.hst) r.push(o.hst);+      if(o.vnode) scanTreeRec(o.vnode, r, currentMark);+    }+  } else if(o instanceof HSComponent) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      if(o.hsRender) r.push(o.hsRender);+      if(o.hsMount) r.push(o.hsMount);+      if(o.hsUnmount) r.push(o.hsUnmount);+      if(o.vnode) scanTreeRec(o.vnode, r, currentMark);+      if(o.pending.length > 0) {+	scanTreeRec(o.pending[o.pending.length-1].vnode, r, currentMark);+      }+    }+  } else if(isVirtualNode(o)) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      // collect event handlers+      var hooks = o.hooks;+      for(var p in hooks) {+	if(p.indexOf('ev-') === 0) {+	  var handler = hooks[p];+	  if(handler.value && handler.value.hsAction) {+	    r.push(handler.value.hsAction);+	  }+	}+      }+      // recurse if any of the children may have thunks, components or handlers+      if(o.hasWidgets || o.hasThunks || o.descendantHooks) {+	for(var i=o.children.length-1;i>=0;i--) {+          scanTreeRec(o.children[i], r, currentMark);+	}+      }+    }+  }+}++function setThunkPatch(n, p) {+  if(hasPatches(p)) n.p[n.i] = new VPatch(VPatch.THUNK, null, p);+}++function hasPatches(patch) {+  for (var index in patch) {+    if (index !== "a") {+      return true;+    }+  }+  return false;+}++function initDelegator(evTypes) {+  var d = DomDelegator();+  var l = evTypes.length;+  for(var i = 0; i < l; i++) {+    d.listenTo(evTypes[i]);+  }+}++function v(tag, props, children) {+  return h(tag, props, children);+}++function t(text) {+  return new VText(text);+}++function th(t, ids, key) {+  return new HSThunk(t, ids, key);+}++function c(r, m, u, key) {+  return new HSComponent(r, m, u, key);+}++function makeHandler(action, async) {+  var f = function(ev) {+    return h$vdomEventCallback(async, action, ev);+  }+  f.hsAction = action;+  return f;+}++var vdomMounts = new h$Set();++module.exports = { setThunkPatch: setThunkPatch+                 , forceTree:     forceTree+                 , forcePatch:    forcePatch+                 , diff:          diff+                 , mount:         mount+                 , unmount:       unmount+                 , initDelegator: initDelegator+                 , v:             v+                 , th:            th+                 , t:             t+                 , c:             c+                 , makeHandler:   makeHandler+                 };++// the global variable we're using in the bindings+h$vdom = module.exports;++h$registerExtensibleRetention(scanTree);+h$registerExtensibleRetentionRoot(scanMounts);++++},{"./diff":1,"dom-delegator":6,"virtual-dom/h":19,"virtual-dom/vdom/patch":30,"virtual-dom/vnode/is-thunk":37,"virtual-dom/vnode/is-vnode":39,"virtual-dom/vnode/is-widget":41,"virtual-dom/vnode/vpatch":44,"virtual-dom/vnode/vtext":45,"x-is-array":47}],4:[function(require,module,exports){+var EvStore = require("ev-store")++module.exports = addEvent++function addEvent(target, type, handler) {+    var events = EvStore(target)+    var event = events[type]++    if (!event) {+        events[type] = handler+    } else if (Array.isArray(event)) {+        if (event.indexOf(handler) === -1) {+            event.push(handler)+        }+    } else if (event !== handler) {+        events[type] = [event, handler]+    }+}++},{"ev-store":8}],5:[function(require,module,exports){+var globalDocument = require("global/document")+var EvStore = require("ev-store")+var createStore = require("weakmap-shim/create-store")++var addEvent = require("./add-event.js")+var removeEvent = require("./remove-event.js")+var ProxyEvent = require("./proxy-event.js")++var HANDLER_STORE = createStore()++module.exports = DOMDelegator++function DOMDelegator(document) {+    if (!(this instanceof DOMDelegator)) {+        return new DOMDelegator(document);+    }++    document = document || globalDocument++    this.target = document.documentElement+    this.events = {}+    this.rawEventListeners = {}+    this.globalListeners = {}+}++DOMDelegator.prototype.addEventListener = addEvent+DOMDelegator.prototype.removeEventListener = removeEvent++DOMDelegator.allocateHandle =+    function allocateHandle(func) {+        var handle = new Handle()++        HANDLER_STORE(handle).func = func;++        return handle+    }++DOMDelegator.transformHandle =+    function transformHandle(handle, broadcast) {+        var func = HANDLER_STORE(handle).func++        return this.allocateHandle(function (ev) {+            broadcast(ev, func);+        })+    }++DOMDelegator.prototype.addGlobalEventListener =+    function addGlobalEventListener(eventName, fn) {+        var listeners = this.globalListeners[eventName] || [];+        if (listeners.indexOf(fn) === -1) {+            listeners.push(fn)+        }++        this.globalListeners[eventName] = listeners;+    }++DOMDelegator.prototype.removeGlobalEventListener =+    function removeGlobalEventListener(eventName, fn) {+        var listeners = this.globalListeners[eventName] || [];++        var index = listeners.indexOf(fn)+        if (index !== -1) {+            listeners.splice(index, 1)+        }+    }++DOMDelegator.prototype.listenTo = function listenTo(eventName) {+    if (!(eventName in this.events)) {+        this.events[eventName] = 0;+    }++    this.events[eventName]++;++    if (this.events[eventName] !== 1) {+        return+    }++    var listener = this.rawEventListeners[eventName]+    if (!listener) {+        listener = this.rawEventListeners[eventName] =+            createHandler(eventName, this)+    }++    this.target.addEventListener(eventName, listener, true)+}++DOMDelegator.prototype.unlistenTo = function unlistenTo(eventName) {+    if (!(eventName in this.events)) {+        this.events[eventName] = 0;+    }++    if (this.events[eventName] === 0) {+        throw new Error("already unlistened to event.");+    }++    this.events[eventName]--;++    if (this.events[eventName] !== 0) {+        return+    }++    var listener = this.rawEventListeners[eventName]++    if (!listener) {+        throw new Error("dom-delegator#unlistenTo: cannot " ++            "unlisten to " + eventName)+    }++    this.target.removeEventListener(eventName, listener, true)+}++function createHandler(eventName, delegator) {+    var globalListeners = delegator.globalListeners;+    var delegatorTarget = delegator.target;++    return handler++    function handler(ev) {+        var globalHandlers = globalListeners[eventName] || []++        if (globalHandlers.length > 0) {+            var globalEvent = new ProxyEvent(ev);+            globalEvent.currentTarget = delegatorTarget;+            callListeners(globalHandlers, globalEvent)+        }++        findAndInvokeListeners(ev.target, ev, eventName)+    }+}++function findAndInvokeListeners(elem, ev, eventName) {+    var listener = getListener(elem, eventName)++    if (listener && listener.handlers.length > 0) {+        var listenerEvent = new ProxyEvent(ev);+        listenerEvent.currentTarget = listener.currentTarget+        callListeners(listener.handlers, listenerEvent)++        if (listenerEvent._bubbles) {+            var nextTarget = listener.currentTarget.parentNode+            findAndInvokeListeners(nextTarget, ev, eventName)+        }+    }+}++function getListener(target, type) {+    // terminate recursion if parent is `null`+    if (target === null || typeof target === "undefined") {+        return null+    }++    var events = EvStore(target)+    // fetch list of handler fns for this event+    var handler = events[type]+    var allHandler = events.event++    if (!handler && !allHandler) {+        return getListener(target.parentNode, type)+    }++    var handlers = [].concat(handler || [], allHandler || [])+    return new Listener(target, handlers)+}++function callListeners(handlers, ev) {+    handlers.forEach(function (handler) {+        if (typeof handler === "function") {+            handler(ev)+        } else if (typeof handler.handleEvent === "function") {+            handler.handleEvent(ev)+        } else if (handler.type === "dom-delegator-handle") {+            HANDLER_STORE(handler).func(ev)+        } else {+            throw new Error("dom-delegator: unknown handler " ++                "found: " + JSON.stringify(handlers));+        }+    })+}++function Listener(target, handlers) {+    this.currentTarget = target+    this.handlers = handlers+}++function Handle() {+    this.type = "dom-delegator-handle"+}++},{"./add-event.js":4,"./proxy-event.js":16,"./remove-event.js":17,"ev-store":8,"global/document":11,"weakmap-shim/create-store":14}],6:[function(require,module,exports){+var Individual = require("individual")+var cuid = require("cuid")+var globalDocument = require("global/document")++var DOMDelegator = require("./dom-delegator.js")++var versionKey = "13"+var cacheKey = "__DOM_DELEGATOR_CACHE@" + versionKey+var cacheTokenKey = "__DOM_DELEGATOR_CACHE_TOKEN@" + versionKey+var delegatorCache = Individual(cacheKey, {+    delegators: {}+})+var commonEvents = [+    "blur", "change", "click",  "contextmenu", "dblclick",+    "error","focus", "focusin", "focusout", "input", "keydown",+    "keypress", "keyup", "load", "mousedown", "mouseup",+    "resize", "select", "submit", "touchcancel",+    "touchend", "touchstart", "unload"+]++/*  Delegator is a thin wrapper around a singleton `DOMDelegator`+        instance.++    Only one DOMDelegator should exist because we do not want+        duplicate event listeners bound to the DOM.++    `Delegator` will also `listenTo()` all events unless+        every caller opts out of it+*/+module.exports = Delegator++function Delegator(opts) {+    opts = opts || {}+    var document = opts.document || globalDocument++    var cacheKey = document[cacheTokenKey]++    if (!cacheKey) {+        cacheKey =+            document[cacheTokenKey] = cuid()+    }++    var delegator = delegatorCache.delegators[cacheKey]++    if (!delegator) {+        delegator = delegatorCache.delegators[cacheKey] =+            new DOMDelegator(document)+    }++    if (opts.defaultEvents !== false) {+        for (var i = 0; i < commonEvents.length; i++) {+            delegator.listenTo(commonEvents[i])+        }+    }++    return delegator+}++Delegator.allocateHandle = DOMDelegator.allocateHandle;+Delegator.transformHandle = DOMDelegator.transformHandle;++},{"./dom-delegator.js":5,"cuid":7,"global/document":11,"individual":12}],7:[function(require,module,exports){+/**+ * cuid.js+ * Collision-resistant UID generator for browsers and node.+ * Sequential for fast db lookups and recency sorting.+ * Safe for element IDs and server-side lookups.+ *+ * Extracted from CLCTR+ *+ * Copyright (c) Eric Elliott 2012+ * MIT License+ */++/*global window, navigator, document, require, process, module */+(function (app) {+  'use strict';+  var namespace = 'cuid',+    c = 0,+    blockSize = 4,+    base = 36,+    discreteValues = Math.pow(base, blockSize),++    pad = function pad(num, size) {+      var s = "000000000" + num;+      return s.substr(s.length-size);+    },++    randomBlock = function randomBlock() {+      return pad((Math.random() *+            discreteValues << 0)+            .toString(base), blockSize);+    },++    safeCounter = function () {+      c = (c < discreteValues) ? c : 0;+      c++; // this is not subliminal+      return c - 1;+    },++    api = function cuid() {+      // Starting with a lowercase letter makes+      // it HTML element ID friendly.+      var letter = 'c', // hard-coded allows for sequential access++        // timestamp+        // warning: this exposes the exact date and time+        // that the uid was created.+        timestamp = (new Date().getTime()).toString(base),++        // Prevent same-machine collisions.+        counter,++        // A few chars to generate distinct ids for different+        // clients (so different computers are far less+        // likely to generate the same id)+        fingerprint = api.fingerprint(),++        // Grab some more chars from Math.random()+        random = randomBlock() + randomBlock();++        counter = pad(safeCounter().toString(base), blockSize);++      return  (letter + timestamp + counter + fingerprint + random);+    };++  api.slug = function slug() {+    var date = new Date().getTime().toString(36),+      counter,+      print = api.fingerprint().slice(0,1) ++        api.fingerprint().slice(-1),+      random = randomBlock().slice(-2);++      counter = safeCounter().toString(36).slice(-4);++    return date.slice(-2) ++      counter + print + random;+  };++  api.globalCount = function globalCount() {+    // We want to cache the results of this+    var cache = (function calc() {+        var i,+          count = 0;++        for (i in window) {+          count++;+        }++        return count;+      }());++    api.globalCount = function () { return cache; };+    return cache;+  };++  api.fingerprint = function browserPrint() {+    return pad((navigator.mimeTypes.length ++      navigator.userAgent.length).toString(36) ++      api.globalCount().toString(36), 4);+  };++  // don't change anything from here down.+  if (app.register) {+    app.register(namespace, api);+  } else if (typeof module !== 'undefined') {+    module.exports = api;+  } else {+    app[namespace] = api;+  }++}(this.applitude || this));++},{}],8:[function(require,module,exports){+'use strict';++var OneVersionConstraint = require('individual/one-version');++var MY_VERSION = '7';+OneVersionConstraint('ev-store', MY_VERSION);++var hashKey = '__EV_STORE_KEY@' + MY_VERSION;++module.exports = EvStore;++function EvStore(elem) {+    var hash = elem[hashKey];++    if (!hash) {+        hash = elem[hashKey] = {};+    }++    return hash;+}++},{"individual/one-version":10}],9:[function(require,module,exports){+(function (global){+'use strict';++/*global window, global*/++var root = typeof window !== 'undefined' ?+    window : typeof global !== 'undefined' ?+    global : {};++module.exports = Individual;++function Individual(key, value) {+    if (key in root) {+        return root[key];+    }++    root[key] = value;++    return value;+}++}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})+},{}],10:[function(require,module,exports){+'use strict';++var Individual = require('./index.js');++module.exports = OneVersion;++function OneVersion(moduleName, version, defaultValue) {+    var key = '__INDIVIDUAL_ONE_VERSION_' + moduleName;+    var enforceKey = key + '_ENFORCE_SINGLETON';++    var versionValue = Individual(enforceKey, version);++    if (versionValue !== version) {+        throw new Error('Can only have one copy of ' ++            moduleName + '.\n' ++            'You already have version ' + versionValue ++            ' installed.\n' ++            'This means you cannot install version ' + version);+    }++    return Individual(key, defaultValue);+}++},{"./index.js":9}],11:[function(require,module,exports){+(function (global){+var topLevel = typeof global !== 'undefined' ? global :+    typeof window !== 'undefined' ? window : {}+var minDoc = require('min-document');++if (typeof document !== 'undefined') {+    module.exports = document;+} else {+    var doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];++    if (!doccy) {+        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;+    }++    module.exports = doccy;+}++}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})+},{"min-document":18}],12:[function(require,module,exports){+(function (global){+var root = typeof window !== 'undefined' ?+    window : typeof global !== 'undefined' ?+    global : {};++module.exports = Individual++function Individual(key, value) {+    if (root[key]) {+        return root[key]+    }++    Object.defineProperty(root, key, {+        value: value+        , configurable: true+    })++    return value+}++}).call(this,typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})+},{}],13:[function(require,module,exports){+if (typeof Object.create === 'function') {+  // implementation from standard node.js 'util' module+  module.exports = function inherits(ctor, superCtor) {+    ctor.super_ = superCtor+    ctor.prototype = Object.create(superCtor.prototype, {+      constructor: {+        value: ctor,+        enumerable: false,+        writable: true,+        configurable: true+      }+    });+  };+} else {+  // old school shim for old browsers+  module.exports = function inherits(ctor, superCtor) {+    ctor.super_ = superCtor+    var TempCtor = function () {}+    TempCtor.prototype = superCtor.prototype+    ctor.prototype = new TempCtor()+    ctor.prototype.constructor = ctor+  }+}++},{}],14:[function(require,module,exports){+var hiddenStore = require('./hidden-store.js');++module.exports = createStore;++function createStore() {+    var key = {};++    return function (obj) {+        if ((typeof obj !== 'object' || obj === null) &&+            typeof obj !== 'function'+        ) {+            throw new Error('Weakmap-shim: Key must be object')+        }++        var store = obj.valueOf(key);+        return store && store.identity === key ?+            store : hiddenStore(obj, key);+    };+}++},{"./hidden-store.js":15}],15:[function(require,module,exports){+module.exports = hiddenStore;++function hiddenStore(obj, key) {+    var store = { identity: key };+    var valueOf = obj.valueOf;++    Object.defineProperty(obj, "valueOf", {+        value: function (value) {+            return value !== key ?+                valueOf.apply(this, arguments) : store;+        },+        writable: true+    });++    return store;+}++},{}],16:[function(require,module,exports){+var inherits = require("inherits")++var ALL_PROPS = [+    "altKey", "bubbles", "cancelable", "ctrlKey",+    "eventPhase", "metaKey", "relatedTarget", "shiftKey",+    "target", "timeStamp", "type", "view", "which"+]+var KEY_PROPS = ["char", "charCode", "key", "keyCode"]+var MOUSE_PROPS = [+    "button", "buttons", "clientX", "clientY", "layerX",+    "layerY", "offsetX", "offsetY", "pageX", "pageY",+    "screenX", "screenY", "toElement"+]++var rkeyEvent = /^key|input/+var rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/++module.exports = ProxyEvent++function ProxyEvent(ev) {+    if (!(this instanceof ProxyEvent)) {+        return new ProxyEvent(ev)+    }++    if (rkeyEvent.test(ev.type)) {+        return new KeyEvent(ev)+    } else if (rmouseEvent.test(ev.type)) {+        return new MouseEvent(ev)+    }++    for (var i = 0; i < ALL_PROPS.length; i++) {+        var propKey = ALL_PROPS[i]+        this[propKey] = ev[propKey]+    }++    this._rawEvent = ev+    this._bubbles = false;+}++ProxyEvent.prototype.preventDefault = function () {+    this._rawEvent.preventDefault()+}++ProxyEvent.prototype.startPropagation = function () {+    this._bubbles = true;+}++function MouseEvent(ev) {+    for (var i = 0; i < ALL_PROPS.length; i++) {+        var propKey = ALL_PROPS[i]+        this[propKey] = ev[propKey]+    }++    for (var j = 0; j < MOUSE_PROPS.length; j++) {+        var mousePropKey = MOUSE_PROPS[j]+        this[mousePropKey] = ev[mousePropKey]+    }++    this._rawEvent = ev+}++inherits(MouseEvent, ProxyEvent)++function KeyEvent(ev) {+    for (var i = 0; i < ALL_PROPS.length; i++) {+        var propKey = ALL_PROPS[i]+        this[propKey] = ev[propKey]+    }++    for (var j = 0; j < KEY_PROPS.length; j++) {+        var keyPropKey = KEY_PROPS[j]+        this[keyPropKey] = ev[keyPropKey]+    }++    this._rawEvent = ev+}++inherits(KeyEvent, ProxyEvent)++},{"inherits":13}],17:[function(require,module,exports){+var EvStore = require("ev-store")++module.exports = removeEvent++function removeEvent(target, type, handler) {+    var events = EvStore(target)+    var event = events[type]++    if (!event) {+        return+    } else if (Array.isArray(event)) {+        var index = event.indexOf(handler)+        if (index !== -1) {+            event.splice(index, 1)+        }+    } else if (event === handler) {+        events[type] = null+    }+}++},{"ev-store":8}],18:[function(require,module,exports){++},{}],19:[function(require,module,exports){+var h = require("./virtual-hyperscript/index.js")++module.exports = h++},{"./virtual-hyperscript/index.js":34}],20:[function(require,module,exports){+/*!+ * Cross-Browser Split 1.1.1+ * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>+ * Available under the MIT License+ * ECMAScript compliant, uniform cross-browser split method+ */++/**+ * Splits a string into an array of strings using a regex or string separator. Matches of the+ * separator are not included in the result array. However, if `separator` is a regex that contains+ * capturing groups, backreferences are spliced into the result each time `separator` is matched.+ * Fixes browser bugs compared to the native `String.prototype.split` and can be used reliably+ * cross-browser.+ * @param {String} str String to split.+ * @param {RegExp|String} separator Regex or string to use for separating the string.+ * @param {Number} [limit] Maximum number of items to include in the result array.+ * @returns {Array} Array of substrings.+ * @example+ *+ * // Basic use+ * split('a b c d', ' ');+ * // -> ['a', 'b', 'c', 'd']+ *+ * // With limit+ * split('a b c d', ' ', 2);+ * // -> ['a', 'b']+ *+ * // Backreferences in result array+ * split('..word1 word2..', /([a-z]+)(\d+)/i);+ * // -> ['..', 'word', '1', ' ', 'word', '2', '..']+ */+module.exports = (function split(undef) {++  var nativeSplit = String.prototype.split,+    compliantExecNpcg = /()??/.exec("")[1] === undef,+    // NPCG: nonparticipating capturing group+    self;++  self = function(str, separator, limit) {+    // If `separator` is not a regex, use `nativeSplit`+    if (Object.prototype.toString.call(separator) !== "[object RegExp]") {+      return nativeSplit.call(str, separator, limit);+    }+    var output = [],+      flags = (separator.ignoreCase ? "i" : "") + (separator.multiline ? "m" : "") + (separator.extended ? "x" : "") + // Proposed for ES6+      (separator.sticky ? "y" : ""),+      // Firefox 3++      lastLastIndex = 0,+      // Make `global` and avoid `lastIndex` issues by working with a copy+      separator = new RegExp(separator.source, flags + "g"),+      separator2, match, lastIndex, lastLength;+    str += ""; // Type-convert+    if (!compliantExecNpcg) {+      // Doesn't need flags gy, but they don't hurt+      separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);+    }+    /* Values for `limit`, per the spec:+     * If undefined: 4294967295 // Math.pow(2, 32) - 1+     * If 0, Infinity, or NaN: 0+     * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;+     * If negative number: 4294967296 - Math.floor(Math.abs(limit))+     * If other: Type-convert, then use the above rules+     */+    limit = limit === undef ? -1 >>> 0 : // Math.pow(2, 32) - 1+    limit >>> 0; // ToUint32(limit)+    while (match = separator.exec(str)) {+      // `separator.lastIndex` is not reliable cross-browser+      lastIndex = match.index + match[0].length;+      if (lastIndex > lastLastIndex) {+        output.push(str.slice(lastLastIndex, match.index));+        // Fix browsers whose `exec` methods don't consistently return `undefined` for+        // nonparticipating capturing groups+        if (!compliantExecNpcg && match.length > 1) {+          match[0].replace(separator2, function() {+            for (var i = 1; i < arguments.length - 2; i++) {+              if (arguments[i] === undef) {+                match[i] = undef;+              }+            }+          });+        }+        if (match.length > 1 && match.index < str.length) {+          Array.prototype.push.apply(output, match.slice(1));+        }+        lastLength = match[0].length;+        lastLastIndex = lastIndex;+        if (output.length >= limit) {+          break;+        }+      }+      if (separator.lastIndex === match.index) {+        separator.lastIndex++; // Avoid an infinite loop+      }+    }+    if (lastLastIndex === str.length) {+      if (lastLength || !separator.test("")) {+        output.push("");+      }+    } else {+      output.push(str.slice(lastLastIndex));+    }+    return output.length > limit ? output.slice(0, limit) : output;+  };++  return self;+})();++},{}],21:[function(require,module,exports){+module.exports=require(8)+},{"individual/one-version":23}],22:[function(require,module,exports){+module.exports=require(9)+},{}],23:[function(require,module,exports){+module.exports=require(10)+},{"./index.js":22}],24:[function(require,module,exports){+module.exports=require(11)+},{"min-document":18}],25:[function(require,module,exports){+"use strict";++module.exports = function isObject(x) {+	return typeof x === "object" && x !== null;+};++},{}],26:[function(require,module,exports){+var isObject = require("is-object")+var isHook = require("../vnode/is-vhook.js")++module.exports = applyProperties++function applyProperties(node, props, previous) {+    for (var propName in props) {+        var propValue = props[propName]++        if (propValue === undefined) {+            removeProperty(node, propName, propValue, previous);+        } else if (isHook(propValue)) {+            removeProperty(node, propName, propValue, previous)+            if (propValue.hook) {+                propValue.hook(node,+                    propName,+                    previous ? previous[propName] : undefined)+            }+        } else {+            if (isObject(propValue)) {+                patchObject(node, props, previous, propName, propValue);+            } else {+                node[propName] = propValue+            }+        }+    }+}++function removeProperty(node, propName, propValue, previous) {+    if (previous) {+        var previousValue = previous[propName]++        if (!isHook(previousValue)) {+            if (propName === "attributes") {+                for (var attrName in previousValue) {+                    node.removeAttribute(attrName)+                }+            } else if (propName === "style") {+                for (var i in previousValue) {+                    node.style[i] = ""+                }+            } else if (typeof previousValue === "string") {+                node[propName] = ""+            } else {+                node[propName] = null+            }+        } else if (previousValue.unhook) {+            previousValue.unhook(node, propName, propValue)+        }+    }+}++function patchObject(node, props, previous, propName, propValue) {+    var previousValue = previous ? previous[propName] : undefined++    // Set attributes+    if (propName === "attributes") {+        for (var attrName in propValue) {+            var attrValue = propValue[attrName]++            if (attrValue === undefined) {+                node.removeAttribute(attrName)+            } else {+                node.setAttribute(attrName, attrValue)+            }+        }++        return+    }++    if(previousValue && isObject(previousValue) &&+        getPrototype(previousValue) !== getPrototype(propValue)) {+        node[propName] = propValue+        return+    }++    if (!isObject(node[propName])) {+        node[propName] = {}+    }++    var replacer = propName === "style" ? "" : undefined++    for (var k in propValue) {+        var value = propValue[k]+        node[propName][k] = (value === undefined) ? replacer : value+    }+}++function getPrototype(value) {+    if (Object.getPrototypeOf) {+        return Object.getPrototypeOf(value)+    } else if (value.__proto__) {+        return value.__proto__+    } else if (value.constructor) {+        return value.constructor.prototype+    }+}++},{"../vnode/is-vhook.js":38,"is-object":25}],27:[function(require,module,exports){+var document = require("global/document")++var applyProperties = require("./apply-properties")++var isVNode = require("../vnode/is-vnode.js")+var isVText = require("../vnode/is-vtext.js")+var isWidget = require("../vnode/is-widget.js")+var handleThunk = require("../vnode/handle-thunk.js")++module.exports = createElement++function createElement(vnode, opts) {+    var doc = opts ? opts.document || document : document+    var warn = opts ? opts.warn : null++    vnode = handleThunk(vnode).a++    if (isWidget(vnode)) {+        return vnode.init()+    } else if (isVText(vnode)) {+        return doc.createTextNode(vnode.text)+    } else if (!isVNode(vnode)) {+        if (warn) {+            warn("Item is not a valid virtual dom node", vnode)+        }+        return null+    }++    var node = (vnode.namespace === null) ?+        doc.createElement(vnode.tagName) :+        doc.createElementNS(vnode.namespace, vnode.tagName)++    var props = vnode.properties+    applyProperties(node, props)++    var children = vnode.children++    for (var i = 0; i < children.length; i++) {+        var childNode = createElement(children[i], opts)+        if (childNode) {+            node.appendChild(childNode)+        }+    }++    return node+}++},{"../vnode/handle-thunk.js":36,"../vnode/is-vnode.js":39,"../vnode/is-vtext.js":40,"../vnode/is-widget.js":41,"./apply-properties":26,"global/document":24}],28:[function(require,module,exports){+// Maps a virtual DOM tree onto a real DOM tree in an efficient manner.+// We don't want to read all of the DOM nodes in the tree so we use+// the in-order tree indexing to eliminate recursion down certain branches.+// We only recurse into a DOM node if we know that it contains a child of+// interest.++var noChild = {}++module.exports = domIndex++function domIndex(rootNode, tree, indices, nodes) {+    if (!indices || indices.length === 0) {+        return {}+    } else {+        indices.sort(ascending)+        return recurse(rootNode, tree, indices, nodes, 0)+    }+}++function recurse(rootNode, tree, indices, nodes, rootIndex) {+    nodes = nodes || {}+++    if (rootNode) {+        if (indexInRange(indices, rootIndex, rootIndex)) {+            nodes[rootIndex] = rootNode+        }++        var vChildren = tree.children++        if (vChildren) {++            var childNodes = rootNode.childNodes++            for (var i = 0; i < tree.children.length; i++) {+                rootIndex += 1++                var vChild = vChildren[i] || noChild+                var nextIndex = rootIndex + (vChild.count || 0)++                // skip recursion down the tree if there are no nodes down here+                if (indexInRange(indices, rootIndex, nextIndex)) {+                    recurse(childNodes[i], vChild, indices, nodes, rootIndex)+                }++                rootIndex = nextIndex+            }+        }+    }++    return nodes+}++// Binary search for an index in the interval [left, right]+function indexInRange(indices, left, right) {+    if (indices.length === 0) {+        return false+    }++    var minIndex = 0+    var maxIndex = indices.length - 1+    var currentIndex+    var currentItem++    while (minIndex <= maxIndex) {+        currentIndex = ((maxIndex + minIndex) / 2) >> 0+        currentItem = indices[currentIndex]++        if (minIndex === maxIndex) {+            return currentItem >= left && currentItem <= right+        } else if (currentItem < left) {+            minIndex = currentIndex + 1+        } else  if (currentItem > right) {+            maxIndex = currentIndex - 1+        } else {+            return true+        }+    }++    return false;+}++function ascending(a, b) {+    return a > b ? 1 : -1+}++},{}],29:[function(require,module,exports){+var applyProperties = require("./apply-properties")++var isWidget = require("../vnode/is-widget.js")+var VPatch = require("../vnode/vpatch.js")++var updateWidget = require("./update-widget")++module.exports = applyPatch++function applyPatch(vpatch, domNode, renderOptions) {+    var type = vpatch.type+    var vNode = vpatch.vNode+    var patch = vpatch.patch++    switch (type) {+        case VPatch.REMOVE:+            return removeNode(domNode, vNode)+        case VPatch.INSERT:+            return insertNode(domNode, patch, renderOptions)+        case VPatch.VTEXT:+            return stringPatch(domNode, vNode, patch, renderOptions)+        case VPatch.WIDGET:+            return widgetPatch(domNode, vNode, patch, renderOptions)+        case VPatch.VNODE:+            return vNodePatch(domNode, vNode, patch, renderOptions)+        case VPatch.ORDER:+            reorderChildren(domNode, patch)+            return domNode+        case VPatch.PROPS:+            applyProperties(domNode, patch, vNode.properties)+            return domNode+        case VPatch.THUNK:+            return replaceRoot(domNode,+                renderOptions.patch(domNode, patch, renderOptions))+        default:+            return domNode+    }+}++function removeNode(domNode, vNode) {+    var parentNode = domNode.parentNode++    if (parentNode) {+        parentNode.removeChild(domNode)+    }++    destroyWidget(domNode, vNode);++    return null+}++function insertNode(parentNode, vNode, renderOptions) {+    var newNode = renderOptions.render(vNode, renderOptions)++    if (parentNode) {+        parentNode.appendChild(newNode)+    }++    return parentNode+}++function stringPatch(domNode, leftVNode, vText, renderOptions) {+    var newNode++    if (domNode.nodeType === 3) {+        domNode.replaceData(0, domNode.length, vText.text)+        newNode = domNode+    } else {+        var parentNode = domNode.parentNode+        newNode = renderOptions.render(vText, renderOptions)++        if (parentNode && newNode !== domNode) {+            parentNode.replaceChild(newNode, domNode)+        }+    }++    return newNode+}++function widgetPatch(domNode, leftVNode, widget, renderOptions) {+    var updating = updateWidget(leftVNode, widget)+    var newNode++    if (updating) {+        newNode = widget.update(leftVNode, domNode) || domNode+    } else {+        newNode = renderOptions.render(widget, renderOptions)+    }++    var parentNode = domNode.parentNode++    if (parentNode && newNode !== domNode) {+        parentNode.replaceChild(newNode, domNode)+    }++    if (!updating) {+        destroyWidget(domNode, leftVNode)+    }++    return newNode+}++function vNodePatch(domNode, leftVNode, vNode, renderOptions) {+    var parentNode = domNode.parentNode+    var newNode = renderOptions.render(vNode, renderOptions)++    if (parentNode && newNode !== domNode) {+        parentNode.replaceChild(newNode, domNode)+    }++    return newNode+}++function destroyWidget(domNode, w) {+    if (typeof w.destroy === "function" && isWidget(w)) {+        w.destroy(domNode)+    }+}++function reorderChildren(domNode, moves) {+    var childNodes = domNode.childNodes+    var keyMap = {}+    var node+    var remove+    var insert++    for (var i = 0; i < moves.removes.length; i++) {+        remove = moves.removes[i]+        node = childNodes[remove.from]+        if (remove.key) {+            keyMap[remove.key] = node+        }+        domNode.removeChild(node)+    }++    var length = childNodes.length+    for (var j = 0; j < moves.inserts.length; j++) {+        insert = moves.inserts[j]+        node = keyMap[insert.key]+        // this is the weirdest bug i've ever seen in webkit+        domNode.insertBefore(node, insert.to >= length++ ? null : childNodes[insert.to])+    }+}++function replaceRoot(oldRoot, newRoot) {+    if (oldRoot && newRoot && oldRoot !== newRoot && oldRoot.parentNode) {+        oldRoot.parentNode.replaceChild(newRoot, oldRoot)+    }++    return newRoot;+}++},{"../vnode/is-widget.js":41,"../vnode/vpatch.js":44,"./apply-properties":26,"./update-widget":31}],30:[function(require,module,exports){+var document = require("global/document")+var isArray = require("x-is-array")++var render = require("./create-element")+var domIndex = require("./dom-index")+var patchOp = require("./patch-op")+module.exports = patch++function patch(rootNode, patches, renderOptions) {+    renderOptions = renderOptions || {}+    renderOptions.patch = renderOptions.patch && renderOptions.patch !== patch+        ? renderOptions.patch+        : patchRecursive+    renderOptions.render = renderOptions.render || render++    return renderOptions.patch(rootNode, patches, renderOptions)+}++function patchRecursive(rootNode, patches, renderOptions) {+    var indices = patchIndices(patches)++    if (indices.length === 0) {+        return rootNode+    }++    var index = domIndex(rootNode, patches.a, indices)+    var ownerDocument = rootNode.ownerDocument++    if (!renderOptions.document && ownerDocument !== document) {+        renderOptions.document = ownerDocument+    }++    for (var i = 0; i < indices.length; i++) {+        var nodeIndex = indices[i]+        rootNode = applyPatch(rootNode,+            index[nodeIndex],+            patches[nodeIndex],+            renderOptions)+    }++    return rootNode+}++function applyPatch(rootNode, domNode, patchList, renderOptions) {+    if (!domNode) {+        return rootNode+    }++    var newNode++    if (isArray(patchList)) {+        for (var i = 0; i < patchList.length; i++) {+            newNode = patchOp(patchList[i], domNode, renderOptions)++            if (domNode === rootNode) {+                rootNode = newNode+            }+        }+    } else {+        newNode = patchOp(patchList, domNode, renderOptions)++        if (domNode === rootNode) {+            rootNode = newNode+        }+    }++    return rootNode+}++function patchIndices(patches) {+    var indices = []++    for (var key in patches) {+        if (key !== "a") {+            indices.push(Number(key))+        }+    }++    return indices+}++},{"./create-element":27,"./dom-index":28,"./patch-op":29,"global/document":24,"x-is-array":47}],31:[function(require,module,exports){+var isWidget = require("../vnode/is-widget.js")++module.exports = updateWidget++function updateWidget(a, b) {+    if (isWidget(a) && isWidget(b)) {+        if ("name" in a && "name" in b) {+            return a.id === b.id+        } else {+            return a.init === b.init+        }+    }++    return false+}++},{"../vnode/is-widget.js":41}],32:[function(require,module,exports){+'use strict';++var EvStore = require('ev-store');++module.exports = EvHook;++function EvHook(value) {+    if (!(this instanceof EvHook)) {+        return new EvHook(value);+    }++    this.value = value;+}++EvHook.prototype.hook = function (node, propertyName) {+    var es = EvStore(node);+    var propName = propertyName.substr(3);++    es[propName] = this.value;+};++EvHook.prototype.unhook = function(node, propertyName) {+    var es = EvStore(node);+    var propName = propertyName.substr(3);++    es[propName] = undefined;+};++},{"ev-store":21}],33:[function(require,module,exports){+'use strict';++module.exports = SoftSetHook;++function SoftSetHook(value) {+    if (!(this instanceof SoftSetHook)) {+        return new SoftSetHook(value);+    }++    this.value = value;+}++SoftSetHook.prototype.hook = function (node, propertyName) {+    if (node[propertyName] !== this.value) {+        node[propertyName] = this.value;+    }+};++},{}],34:[function(require,module,exports){+'use strict';++var isArray = require('x-is-array');++var VNode = require('../vnode/vnode.js');+var VText = require('../vnode/vtext.js');+var isVNode = require('../vnode/is-vnode');+var isVText = require('../vnode/is-vtext');+var isWidget = require('../vnode/is-widget');+var isHook = require('../vnode/is-vhook');+var isVThunk = require('../vnode/is-thunk');++var parseTag = require('./parse-tag.js');+var softSetHook = require('./hooks/soft-set-hook.js');+var evHook = require('./hooks/ev-hook.js');++module.exports = h;++function h(tagName, properties, children) {+    var childNodes = [];+    var tag, props, key, namespace;++    if (!children && isChildren(properties)) {+        children = properties;+        props = {};+    }++    props = props || properties || {};+    tag = parseTag(tagName, props);++    // support keys+    if (props.hasOwnProperty('key')) {+        key = props.key;+        props.key = undefined;+    }++    // support namespace+    if (props.hasOwnProperty('namespace')) {+        namespace = props.namespace;+        props.namespace = undefined;+    }++    // fix cursor bug+    if (tag === 'INPUT' &&+        !namespace &&+        props.hasOwnProperty('value') &&+        props.value !== undefined &&+        !isHook(props.value)+    ) {+        props.value = softSetHook(props.value);+    }++    transformProperties(props);++    if (children !== undefined && children !== null) {+        addChild(children, childNodes, tag, props);+    }+++    return new VNode(tag, props, childNodes, key, namespace);+}++function addChild(c, childNodes, tag, props) {+    if (typeof c === 'string') {+        childNodes.push(new VText(c));+    } else if (typeof c === 'number') {+        childNodes.push(new VText(String(c)));+    } else if (isChild(c)) {+        childNodes.push(c);+    } else if (isArray(c)) {+        for (var i = 0; i < c.length; i++) {+            addChild(c[i], childNodes, tag, props);+        }+    } else if (c === null || c === undefined) {+        return;+    } else {+        throw UnexpectedVirtualElement({+            foreignObject: c,+            parentVnode: {+                tagName: tag,+                properties: props+            }+        });+    }+}++function transformProperties(props) {+    for (var propName in props) {+        if (props.hasOwnProperty(propName)) {+            var value = props[propName];++            if (isHook(value)) {+                continue;+            }++            if (propName.substr(0, 3) === 'ev-') {+                // add ev-foo support+                props[propName] = evHook(value);+            }+        }+    }+}++function isChild(x) {+    return isVNode(x) || isVText(x) || isWidget(x) || isVThunk(x);+}++function isChildren(x) {+    return typeof x === 'string' || isArray(x) || isChild(x);+}++function UnexpectedVirtualElement(data) {+    var err = new Error();++    err.type = 'virtual-hyperscript.unexpected.virtual-element';+    err.message = 'Unexpected virtual child passed to h().\n' ++        'Expected a VNode / Vthunk / VWidget / string but:\n' ++        'got:\n' ++        errorString(data.foreignObject) ++        '.\n' ++        'The parent vnode is:\n' ++        errorString(data.parentVnode)+        '\n' ++        'Suggested fix: change your `h(..., [ ... ])` callsite.';+    err.foreignObject = data.foreignObject;+    err.parentVnode = data.parentVnode;++    return err;+}++function errorString(obj) {+    try {+        return JSON.stringify(obj, null, '    ');+    } catch (e) {+        return String(obj);+    }+}++},{"../vnode/is-thunk":37,"../vnode/is-vhook":38,"../vnode/is-vnode":39,"../vnode/is-vtext":40,"../vnode/is-widget":41,"../vnode/vnode.js":43,"../vnode/vtext.js":45,"./hooks/ev-hook.js":32,"./hooks/soft-set-hook.js":33,"./parse-tag.js":35,"x-is-array":47}],35:[function(require,module,exports){+'use strict';++var split = require('browser-split');++var classIdSplit = /([\.#]?[a-zA-Z0-9\u007F-\uFFFF_:-]+)/;+var notClassId = /^\.|#/;++module.exports = parseTag;++function parseTag(tag, props) {+    if (!tag) {+        return 'DIV';+    }++    var noId = !(props.hasOwnProperty('id'));++    var tagParts = split(tag, classIdSplit);+    var tagName = null;++    if (notClassId.test(tagParts[1])) {+        tagName = 'DIV';+    }++    var classes, part, type, i;++    for (i = 0; i < tagParts.length; i++) {+        part = tagParts[i];++        if (!part) {+            continue;+        }++        type = part.charAt(0);++        if (!tagName) {+            tagName = part;+        } else if (type === '.') {+            classes = classes || [];+            classes.push(part.substring(1, part.length));+        } else if (type === '#' && noId) {+            props.id = part.substring(1, part.length);+        }+    }++    if (classes) {+        if (props.className) {+            classes.push(props.className);+        }++        props.className = classes.join(' ');+    }++    return props.namespace ? tagName : tagName.toUpperCase();+}++},{"browser-split":20}],36:[function(require,module,exports){+var isVNode = require("./is-vnode")+var isVText = require("./is-vtext")+var isWidget = require("./is-widget")+var isThunk = require("./is-thunk")++module.exports = handleThunk++function handleThunk(a, b) {+    var renderedA = a+    var renderedB = b++    if (isThunk(b)) {+        renderedB = renderThunk(b, a)+    }++    if (isThunk(a)) {+        renderedA = renderThunk(a, null)+    }++    return {+        a: renderedA,+        b: renderedB+    }+}++function renderThunk(thunk, previous) {+    var renderedThunk = thunk.vnode++    if (!renderedThunk) {+        renderedThunk = thunk.vnode = thunk.render(previous)+    }++    if (!(isVNode(renderedThunk) ||+            isVText(renderedThunk) ||+            isWidget(renderedThunk))) {+        throw new Error("thunk did not return a valid node");+    }++    return renderedThunk+}++},{"./is-thunk":37,"./is-vnode":39,"./is-vtext":40,"./is-widget":41}],37:[function(require,module,exports){+module.exports = isThunk++function isThunk(t) {+    return t && t.type === "Thunk"+}++},{}],38:[function(require,module,exports){+module.exports = isHook++function isHook(hook) {+    return hook &&+      (typeof hook.hook === "function" && !hook.hasOwnProperty("hook") ||+       typeof hook.unhook === "function" && !hook.hasOwnProperty("unhook"))+}++},{}],39:[function(require,module,exports){+var version = require("./version")++module.exports = isVirtualNode++function isVirtualNode(x) {+    return x && x.type === "VirtualNode" && x.version === version+}++},{"./version":42}],40:[function(require,module,exports){+var version = require("./version")++module.exports = isVirtualText++function isVirtualText(x) {+    return x && x.type === "VirtualText" && x.version === version+}++},{"./version":42}],41:[function(require,module,exports){+module.exports = isWidget++function isWidget(w) {+    return w && w.type === "Widget"+}++},{}],42:[function(require,module,exports){+module.exports = "2"++},{}],43:[function(require,module,exports){+var version = require("./version")+var isVNode = require("./is-vnode")+var isWidget = require("./is-widget")+var isThunk = require("./is-thunk")+var isVHook = require("./is-vhook")++module.exports = VirtualNode++var noProperties = {}+var noChildren = []++function VirtualNode(tagName, properties, children, key, namespace) {+    this.tagName = tagName+    this.properties = properties || noProperties+    this.children = children || noChildren+    this.key = key != null ? String(key) : undefined+    this.namespace = (typeof namespace === "string") ? namespace : null++    var count = (children && children.length) || 0+    var descendants = 0+    var hasWidgets = false+    var hasThunks = false+    var descendantHooks = false+    var hooks++    for (var propName in properties) {+        if (properties.hasOwnProperty(propName)) {+            var property = properties[propName]+            if (isVHook(property) && property.unhook) {+                if (!hooks) {+                    hooks = {}+                }++                hooks[propName] = property+            }+        }+    }++    for (var i = 0; i < count; i++) {+        var child = children[i]+        if (isVNode(child)) {+            descendants += child.count || 0++            if (!hasWidgets && child.hasWidgets) {+                hasWidgets = true+            }++            if (!hasThunks && child.hasThunks) {+                hasThunks = true+            }++            if (!descendantHooks && (child.hooks || child.descendantHooks)) {+                descendantHooks = true+            }+        } else if (!hasWidgets && isWidget(child)) {+            if (typeof child.destroy === "function") {+                hasWidgets = true+            }+        } else if (!hasThunks && isThunk(child)) {+            hasThunks = true;+        }+    }++    this.count = count + descendants+    this.hasWidgets = hasWidgets+    this.hasThunks = hasThunks+    this.hooks = hooks+    this.descendantHooks = descendantHooks+}++VirtualNode.prototype.version = version+VirtualNode.prototype.type = "VirtualNode"++},{"./is-thunk":37,"./is-vhook":38,"./is-vnode":39,"./is-widget":41,"./version":42}],44:[function(require,module,exports){+var version = require("./version")++VirtualPatch.NONE = 0+VirtualPatch.VTEXT = 1+VirtualPatch.VNODE = 2+VirtualPatch.WIDGET = 3+VirtualPatch.PROPS = 4+VirtualPatch.ORDER = 5+VirtualPatch.INSERT = 6+VirtualPatch.REMOVE = 7+VirtualPatch.THUNK = 8++module.exports = VirtualPatch++function VirtualPatch(type, vNode, patch) {+    this.type = Number(type)+    this.vNode = vNode+    this.patch = patch+}++VirtualPatch.prototype.version = version+VirtualPatch.prototype.type = "VirtualPatch"++},{"./version":42}],45:[function(require,module,exports){+var version = require("./version")++module.exports = VirtualText++function VirtualText(text) {+    this.text = String(text)+}++VirtualText.prototype.version = version+VirtualText.prototype.type = "VirtualText"++},{"./version":42}],46:[function(require,module,exports){+var isObject = require("is-object")+var isHook = require("../vnode/is-vhook")++module.exports = diffProps++function diffProps(a, b) {+    var diff++    for (var aKey in a) {+        if (!(aKey in b)) {+            diff = diff || {}+            diff[aKey] = undefined+        }++        var aValue = a[aKey]+        var bValue = b[aKey]++        if (aValue === bValue) {+            continue+        } else if (isObject(aValue) && isObject(bValue)) {+            if (getPrototype(bValue) !== getPrototype(aValue)) {+                diff = diff || {}+                diff[aKey] = bValue+            } else if (isHook(bValue)) {+                 diff = diff || {}+                 diff[aKey] = bValue+            } else {+                var objectDiff = diffProps(aValue, bValue)+                if (objectDiff) {+                    diff = diff || {}+                    diff[aKey] = objectDiff+                }+            }+        } else {+            diff = diff || {}+            diff[aKey] = bValue+        }+    }++    for (var bKey in b) {+        if (!(bKey in a)) {+            diff = diff || {}+            diff[bKey] = b[bKey]+        }+    }++    return diff+}++function getPrototype(value) {+  if (Object.getPrototypeOf) {+    return Object.getPrototypeOf(value)+  } else if (value.__proto__) {+    return value.__proto__+  } else if (value.constructor) {+    return value.constructor.prototype+  }+}++},{"../vnode/is-vhook":38,"is-object":25}],47:[function(require,module,exports){+var nativeIsArray = Array.isArray+var toString = Object.prototype.toString++module.exports = nativeIsArray || isArray++function isArray(obj) {+    return toString.call(obj) === "[object Array]"+}++},{}]},{},[3]);
+ virtual-dom/lib.require.js view
@@ -0,0 +1,457 @@+/*+  to generate lib.js, install virtual-dom and process file:++     $ npm install+     $ grunt+   the ./diff module is vtree/diff with a few changes to+   allow diff to run in an asynchronous thread in the presence of+   memoized nodes.+ */++/*+   Note on memory management:++   To ensure accurate heap tracing for finalization and profiling purposes,+   GHCJS needs to know reachable all Haskell values. ghcjs-vdom stores some+   Haskell values inside JavaScript references and uses extensible retention+   to collect these values. It's crucial that all data structures that may+   contain Haskell values are stored directly in a JSVal and not inside other+   JS data structures.++   The recognized types are:++     - HSPatch:+         The patch object contains the new (target) virtual-dom tree and+         the original tree is reachable trough the parent. Since all handlers,+         components and thunks are reachable through these trees, the patch itself+         does not need to be traversed.++     - HSMount:+         All mounted HSMount points are scanned as roots. The current virtual tree+         (mount.vtree) is traversed for this.++     - HSComponent:+         Traversed when reachable through Haskell heap or virtual-dom tree. Contains+         current tree (component.vtree) and rendering action (component.hsRender)++     - HSThunk:+         Traversed when reachable through Haskell heap or virtual-dom tree. Contains+         Haskell suspension (thunk.hst) or rendered tree (thunk.vnode)++     - virtual node ( isVirtualNode(x) )+         Some node in a virtual-dom tree. Haskell event handlers are stored in 'ev-*'+         properties, which virtual-dom adds to the node's hooks (vnode.hooks). If+         a virtual node contains thunks, widgets or any of its descendants have hooks,+         the children of the node have to be traversed.++   forceThunks and forcePatch fill an array of thunks, which is not directly+   scannable; however, these functions are only used as part of a `diff` operation.+   The initial diff creates an HSPatch object, through which the original and target+   virtual-dom tree are completely reachable.+ */++var isVirtualNode = require('virtual-dom/vnode/is-vnode');+var isThunk       = require('virtual-dom/vnode/is-thunk');+var isWidget      = require("virtual-dom/vnode/is-widget");+var h             = require('virtual-dom/h');+var isArray       = require('x-is-array');+var VPatch        = require("virtual-dom/vnode/vpatch");+var VText         = require('virtual-dom/vnode/vtext');+var vdomPatch     = require('virtual-dom/vdom/patch');+var DomDelegator  = require('dom-delegator');++var diff = require('./diff');++var VRenderableN = 0;++/** @constructor */+function HSPatch(patch, old, vnode, parent) {+  this.patch   = patch;+  this.old     = old;+  this.vnode   = vnode;+  this.parent  = parent;+}++/** @constructor */+function HSThunk(t, ids, key) {+  this.hst        = t;   // haskell thunk+  this.ids        = ids; // array of haskell unique ids+  this.key        = key;+  this.vnode      = null;+  this._ghcjsMark = 0;+}++HSThunk.prototype.type = 'Thunk';++/* +  render returns the deferred rendering object+  null if the thunk has already been rendered, in which case the value is in this.vnode+ */+HSThunk.prototype.render = function(previous) {+  if(previous && !this.vnode && eqIds(this.ids, previous.ids)) {+    if(previous.hst) {+      this.hst = previous.hst;+    } else {+      this.hst   = null;+      this.vnode = previous.vnode;+    }+  }+  return this.vnode ? null : this;+}++/** @constructor */+function HSComponent(r, mnt, unmnt, key) {+  this._key      = ++VRenderableN;+  this.hsRender  = r;   // IO action that produces a vdom tree+  this.hsMount   = mnt;+  this.hsUnmount = unmnt;+  this.key       = key || this._key;+  this.vnode     = this.initialVnode = new VText("");+  this.pending   = [];+  this.mounts    = {};+  this.version   = 0;+  this.latest    = 0;+}++HSComponent.prototype.type = 'Widget';++HSComponent.prototype.init = function() {+  var n = document.createTextNode('');+  if(this.vnode !== this.initialVnode) {+    var thunks = [];+    var p = diff(this.initialVnode, this.vnode, thunks);+    if(thunks.length !== 0) {+      throw new Error("HSComponent vnode contains unevaluated thunks");+    }+    n = vdomPatch(n, p);+  }+  var m = new HSComponentMount(n);+  n._key = m._key;+  n._widget = this;+  this.mounts[m._key] = m;+  if(this.hsMount) {+    h$vdomMountComponentCallback(this.hsMount, m._key, this);+  }+  return n;+}++HSComponent.prototype.destroy = function(domNode) {+  delete this.mounts[domNode._key];+  if(this.hsUnmount) {+    h$vdomUnmountComponentCallback(this.hsUnmount, domNode._key, domNode);+  }+}++HSComponent.prototype.diff = function(v, thunks) {+  var vn = this.vnode;+  if(this.pending.length > 0) {+    vn = this.pending[this.pending.length-1].vnode;+  }+  return new HSPatch( diff(vn, v, thunks)+		    , vn+		    , v+		    , this);+}++HSComponent.prototype.addPatch = function(p) {+  var cur = this.pending.length > 0 ? this.pending[this.pending.length-1]+                                    : this.vnode;+  if(p.old === cur) this.pending.push(p);+}++HSComponent.prototype.patch = function(p) {+  if(this.pending.length > 0) {+    var pnd = this.pending;+    this.pending = [];+    for(var i = 0; i < pnd.length; i++) this.patch(pnd[i]);+  }+  if(!p) return;+  if(p.parent !== this || p.old !== this.vnode) {+    return false;+  }+  for(var k in this.mounts) {+    var m = this.mounts[k];+    m.node = vdomPatch(m.node, p.patch);+  }+  this.vnode = p.vnode;+  return true;+}++// only use this for manually updated components (i.e. no diff/patch)+HSComponent.prototype.updateMount = function(mnt, node) {+  var m = this.mounts[mnt];+  node._key = mnt;+  node._widget = this;+  m.node.parentNode.replaceChild(node, m.node);+  m.node = node;+}++HSComponent.prototype.update = function(leftVNode, node) {+  if(node._widget) {+    if(node._widget == this) return node;+    node._widget.destroy(node);+  }+  return this.init();+}++var HSComponentMountN = 0;+function HSComponentMount(domNode) {+  this._key = ++HSComponentMountN;+  this.node = domNode;+}++/** @constructor */+function HSMount(domNode) {+  this._key       = ++VRenderableN;+  // this.version    = 0;+  this.vnode      = new VText(""); // currently rendered vdom tree+  this.pending    = [];            // pending patches, not yet applied+  this.node       = document.createTextNode("");+  this.parentNode = domNode;+}++HSMount.prototype.diff = function(v, thunks) {+  var vn = this.vnode;+  if(this.pending.length > 0) {+    vn = this.pending[this.pending.length-1].vnode;+  }+  return new HSPatch( diff(vn, v, thunks)+		    , vn+		    , v+		    , this);+}++HSMount.prototype.addPatch = function(p) {+  var cur = this.pending.length > 0 ? this.pending[this.pending.length-1]+                                    : this.vnode;+  if(p.old === cur) this.pending.push(p);+}++// HSMount.patch(null) to flush pending list+HSMount.prototype.patch = function(p) {+  if(this.pending.length > 0) {+    var pnd = this.pending;+    this.pending = [];+    for(var i = 0; i < pnd.length; i++) this.patch(pnd[i]);+  }+  if(!p) return;+  if(p.parent !== this || p.old !== this.vnode) {+    return false;+  }+  this.node  = vdomPatch(this.node, p.patch);+  this.vnode = p.vnode;+  return true;+}+  +/* mount a vdom tree, making it visible to extensible retention */+function mount(domNode) {+  while(domNode.firstChild) domNode.removeChild(domNode.firstChild);+  var m = new HSMount(domNode);+  domNode.appendChild(m.node);+  vdomMounts.add(m);+  return m;+}++/* unmount a tree, removing all child nodes. */+function unmount(vmount) {+  var n = vmount.parentNode;+  while(n.firstChild) n.removeChild(n.firstChild);+  vdomMounts.remove(vmount);+}++/*+   Compare lists of object identifiers associated with a thunk node. If the lists are equal,+   the subtree does not have to be recomputed.+*/+function eqIds(ids1, ids2) {+  if(!ids1 || !ids2 || ids1.length != ids2.length) return false;+  for(var i=ids1.length-1;i>=0;i--) {+    var id1 = ids1[i], id2 = ids2[i];+    if(typeof id1 === 'number') {+      if(typeof id2 !== 'number') return false;+      if(id1 !== id2 && !((id1!=id1) && (id2!=id2))) return false;+    } else {+      if(id1 !== id2) return false;+    }+  }+  return true;+}++function forcePatch(p) {+  var thunks = [], i, j, pi;+  for(i in p) {+    var pi = p[i];+    if(isArray(pi)) {+      for(j=pi.length-1;j>=0;j--) {+	forceTree(pi[j].patch, thunks);+      }+    }+    else if(pi.patch) forceTree(pi.patch, thunks);+    else forceTree(pi, thunks);+  }+  return thunks;+}++function forceTree(n, t) {+  if(isThunk(n)) {+    if(n.vnode) forceTree(n.vnode, t);+    else t.push(n);+  } else if(isVirtualNode(n) && n.hasThunks) {+    for(var i=n.children.length-1;i>=0;i--) {+      forceTree(n.children[i], t);+    }+  }+}++/*+   scan all mounted virtual-dom trees+ */+function scanMounts(currentMark) {+  var i = vdomMounts.iter(), m, res = [];+  while((m = i.next()) !== null) {+    scanTreeRec(m.vnode, res, currentMark);+    if(m.pending.length > 0) {+      scanTreeRec(m.pending[m.pending.length-1].vnode, res, currentMark);+    }+  }+  return res;+}++/*+   scan a tree (extensible retention callback).++   returns:+     - an array of haskell items if any+     - true if no haskell items have been found+     - false if the object is not a ghcjs-vdom tree+         (fallthrough to other extensible retention scanners)+ */+var scanTreeRes = [];+function scanTree(o, currentMark) {+  if(isVirtualNode(o) || isThunk(o) || isWidget(o) ||+     o instanceof HSPatch || o instanceof HSComponent || o instanceof HSMount) {+    var r = scanTreeRes;+    scanTreeRec(o, r, currentMark);+    if(r.length > 0) {+      scanTreeRes = [];+      return r;+    } else {+      return true;+    }+  } else { // not a ghcjs-vdom object, fall through+    return false; +  }+}++function scanTreeRec(o, r, currentMark) {+  if(o instanceof HSPatch) {+    scanTreeRec(o.vnode, r, currentMark);+    scanTreeRec(o.parent);+  } else if(o instanceof HSThunk) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      if(o.hst) r.push(o.hst);+      if(o.vnode) scanTreeRec(o.vnode, r, currentMark);+    }+  } else if(o instanceof HSComponent) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      if(o.hsRender) r.push(o.hsRender);+      if(o.hsMount) r.push(o.hsMount);+      if(o.hsUnmount) r.push(o.hsUnmount);+      if(o.vnode) scanTreeRec(o.vnode, r, currentMark);+      if(o.pending.length > 0) {+	scanTreeRec(o.pending[o.pending.length-1].vnode, r, currentMark);+      }+    }+  } else if(isVirtualNode(o)) {+    if(o._ghcjsMark !== currentMark) {+      o._ghcjsMark = currentMark;+      // collect event handlers+      var hooks = o.hooks;+      for(var p in hooks) {+	if(p.indexOf('ev-') === 0) {+	  var handler = hooks[p];+	  if(handler.value && handler.value.hsAction) {+	    r.push(handler.value.hsAction);+	  }+	}+      }+      // recurse if any of the children may have thunks, components or handlers+      if(o.hasWidgets || o.hasThunks || o.descendantHooks) {+	for(var i=o.children.length-1;i>=0;i--) {+          scanTreeRec(o.children[i], r, currentMark);+	}+      }+    }+  }+}++function setThunkPatch(n, p) {+  if(hasPatches(p)) n.p[n.i] = new VPatch(VPatch.THUNK, null, p);+}++function hasPatches(patch) {+  for (var index in patch) {+    if (index !== "a") {+      return true;+    }+  }+  return false;+}++function initDelegator(evTypes) {+  var d = DomDelegator();+  var l = evTypes.length;+  for(var i = 0; i < l; i++) {+    d.listenTo(evTypes[i]);+  }+}++function v(tag, props, children) {+  return h(tag, props, children);+}++function t(text) {+  return new VText(text);+}++function th(t, ids, key) {+  return new HSThunk(t, ids, key);+}++function c(r, m, u, key) {+  return new HSComponent(r, m, u, key);+}++function makeHandler(action, async) {+  var f = function(ev) {+    return h$vdomEventCallback(async, action, ev);+  }+  f.hsAction = action;+  return f;+}++var vdomMounts = new h$Set();++module.exports = { setThunkPatch: setThunkPatch+                 , forceTree:     forceTree+                 , forcePatch:    forcePatch+                 , diff:          diff+                 , mount:         mount+                 , unmount:       unmount+                 , initDelegator: initDelegator+                 , v:             v+                 , th:            th+                 , t:             t+                 , c:             c+                 , makeHandler:   makeHandler+                 };++// the global variable we're using in the bindings+h$vdom = module.exports;++h$registerExtensibleRetention(scanTree);+h$registerExtensibleRetentionRoot(scanMounts);++
+ virtual-dom/package.json view
@@ -0,0 +1,16 @@+{+  "name": "ghcjs-vdom-support",+  "version": "0.0.0",+  "description": "Support lib for ghcjs-vdom",+  "devDependencies": {+    "grunt": "^0.4.5",+    "grunt-browserify": "^2.1.4",+    "grunt-contrib-watch": "^0.6.1"+  },+  "dependencies": {+    "is-object": "^0.1.2",+    "virtual-dom": "^2.0.1",+    "x-is-array": "^0.1.0",+    "dom-delegator": "^13.1.0"+  }+}