├── .ghci
├── Setup.hs
├── .gitignore
├── examples
├── index.html
├── console-polyfill.js
├── Main.hs
├── ace-helpers.js
├── es5-sham.min.js
├── React
│ └── Ace.hs
├── es5-shim.min.js
└── react.min.js
├── .dir-locals.el
├── README.md
├── LICENSE
├── src
├── GHCJS
│ └── Compat.hs
├── React
│ ├── Lucid.hs
│ ├── Builder.hs
│ ├── Component.hs
│ ├── Event.hs
│ ├── Monad.hs
│ └── Internal.hs
└── React.hs
└── ghcjs-react.cabal
/.ghci:
--------------------------------------------------------------------------------
1 | :set -isrc
2 |
--------------------------------------------------------------------------------
/Setup.hs:
--------------------------------------------------------------------------------
1 | import Distribution.Simple
2 | main = defaultMain
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | cabal-dev
3 | *.o
4 | *.hi
5 | *.chi
6 | *.chs.h
7 | .virtualenv
8 | .hsenv
9 | .cabal-sandbox/
10 | cabal.sandbox.config
11 | cabal.config
12 | *.js_o
13 | *.js_hi
14 | *.jsexe
15 | .docker-sandbox
16 | .shake.database
17 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Example app
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.dir-locals.el:
--------------------------------------------------------------------------------
1 | ((haskell-mode . ((haskell-indent-spaces . 4)
2 | (haskell-process-type . ghci)
3 | (haskell-process-path-ghci . "ghci")
4 | (haskell-process-wrapper-function
5 | . (lambda (args)
6 | (append
7 | (list "fpbuild"
8 | "--docker-run-args=[\"--interactive=true\",\"--tty=false\"]"
9 | "exec"
10 | "--")
11 | args)))
12 | (haskell-process-use-ghci . t)))
13 | (hamlet-mode . ((hamlet/basic-offset . 4)
14 | (haskell-process-use-ghci . t))))
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | react-ghcjs
2 | ===========
3 |
4 | React bindings for GHCJS
5 |
6 | ## Compile examples
7 |
8 | ghcjs -iexamples/ -isrc/ examples/Main.hs
9 | cp examples/index.html examples/Main.jsexe/
10 |
11 | ## Haddock
12 |
13 | $ haddock React --optghc=-isrc -h
14 |
15 | ## Alternate means of building
16 |
17 | If you have trouble with `cabal install --ghcjs`, you may instead try:
18 |
19 | $ rm -r dist/
20 | $ ghcjs Setup.hs
21 | $ node Setup.jsexe/all.js clean
22 | $ node Setup.jsexe/all.js configure --user --ghcjs
23 | $ node Setup.jsexe/all.js build
24 | $ node Setup.jsexe/all.js copy
25 | $ node Setup.jsexe/all.js register
26 | $ node Setup.jsexe/all.js haddock
27 |
--------------------------------------------------------------------------------
/examples/console-polyfill.js:
--------------------------------------------------------------------------------
1 | // Console-polyfill. MIT license.
2 | // https://github.com/paulmillr/console-polyfill
3 | // Make it safe to do console.log() always.
4 | (function(con) {
5 | 'use strict';
6 | var prop, method;
7 | var empty = {};
8 | var dummy = function() {};
9 | var properties = 'memory'.split(',');
10 | var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' +
11 | 'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
12 | 'table,time,timeEnd,timeStamp,trace,warn').split(',');
13 | while (prop = properties.pop()) con[prop] = con[prop] || empty;
14 | while (method = methods.pop()) con[method] = con[method] || dummy;
15 | })(this.console = this.console || {}); // Using `this` for web workers.
16 |
--------------------------------------------------------------------------------
/examples/Main.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE TemplateHaskell #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE CPP #-}
4 | {-# LANGUAGE RankNTypes #-}
5 | {-# LANGUAGE FlexibleContexts #-}
6 |
7 | -- | Example application.
8 |
9 | module Main where
10 |
11 | import Data.Maybe
12 | import React
13 | import React.Ace as Ace
14 | import React.Builder
15 | import React.Internal
16 | import React.Lucid
17 | import Control.Lens
18 | import Control.Lens.TH
19 | import Control.Monad.IO.Class
20 |
21 | -- | Application state.
22 | data State
23 | = Start
24 | | MainState Main
25 | deriving (Show,Eq)
26 |
27 | data Main =
28 | Main {_mainAce :: Ace}
29 | deriving (Show,Eq)
30 |
31 | $(makePrisms ''State)
32 | $(makeLenses ''Main)
33 |
34 | -- | Main entry point.
35 | main :: IO ()
36 | main =
37 | do app <- getApp
38 | ace <- Ace.new app
39 | container <- getElementById "container"
40 | (react app (render ace) container)
41 |
42 | -- | Make the application.
43 | getApp :: IO (App State IO)
44 | getApp = makeApp Start id
45 |
46 | -- | Our main view.
47 | render :: MonadIO m
48 | => Component State Ace m -> State -> ReactT State m ()
49 | render ace state =
50 | do build "div" (text "Hello, world!")
51 | buildComponent
52 | ace
53 | (_MainState . mainAce)
54 | (do code_ "main = putStrLn \"Woot\""
55 | Ace.startline_ 0
56 | Ace.startcol_ 0
57 | Ace.endline_ 0
58 | Ace.endcol_ 0)
59 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, FP Complete
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of FP Complete nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/src/GHCJS/Compat.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE CPP #-}
2 | {-# LANGUAGE FlexibleInstances #-}
3 | {-# OPTIONS_GHC -fno-warn-missing-signatures #-}
4 |
5 | -- | Compatibility for both GHCi and GHCJS.
6 |
7 | module GHCJS.Compat where
8 |
9 | import Data.String
10 | import Data.Text (Text)
11 |
12 | #ifdef __GHCJS__
13 | import JavaScript.JQuery (JQuery)
14 | import GHCJS.Types
15 | import GHCJS.Marshal
16 | import GHCJS.DOM
17 | import GHCJS.Foreign
18 | #endif
19 |
20 | -- So I can type check this in GHCi.
21 | #ifndef __GHCJS__
22 | data Event
23 | data JQuery
24 | type JSObject a = JSRef (JSObject_ a)
25 | data JSObject_ a
26 | data JSRef a
27 | -- | A DOM element.
28 | data Element
29 | castRef = undefined
30 | toJSString = undefined
31 | data JSString
32 | instance IsString JSString where fromString = undefined
33 | newObj = undefined
34 | setProp = undefined
35 | getProp = undefined
36 | toJSRef_aeson = undefined
37 | fromJSString = undefined
38 | syncCallback1 = undefined
39 | syncCallback2 = undefined
40 | syncCallback = undefined
41 | data U = AlwaysRetain
42 | #endif
43 |
44 | #ifndef __GHCJS__
45 | class ToJSRef a where
46 | toJSRef :: a -> IO (JSRef a)
47 | toJSRef = undefined
48 | toJSRefListOf :: [a] -> IO (JSRef [a])
49 | toJSRefListOf = undefined
50 | class FromJSRef a where
51 | fromJSRef :: JSRef a -> IO (Maybe a)
52 | fromJSRef = undefined
53 | fromJSRefListOf :: JSRef [a] -> IO (Maybe [a])
54 | fromJSRefListOf = undefined
55 | instance FromJSRef JQuery
56 | instance FromJSRef Int
57 | instance FromJSRef Text
58 | instance FromJSRef JSString
59 | instance FromJSRef x => FromJSRef [x]
60 | instance ToJSRef x => ToJSRef [x]
61 | instance ToJSRef x => ToJSRef (JSRef x)
62 | instance ToJSRef Int
63 | #endif
64 |
--------------------------------------------------------------------------------
/examples/ace-helpers.js:
--------------------------------------------------------------------------------
1 | // FIXME: Replace this with pure GHCJS. It's just more laborious to do
2 | // so.
3 | var editor;
4 | var AceRange = ace.require('ace/range').Range;
5 | function makeEditor(e,onClick,onDblClick){
6 | editor = ace.edit(e.get(0));
7 | editor.setTheme("ace/theme/tomorrow");
8 | editor.getSession().setMode("ace/mode/haskell");
9 | editor.setReadOnly(true);
10 | editor.on("click", debounce(function(e) {
11 | var pos = e.getDocumentPosition();
12 | var s = editor.getSelection();
13 | var anchor = s.getSelectionAnchor();
14 | var lead = s.getSelectionLead();
15 | // Swap them to have the right order
16 | if ((anchor.row > lead.row) || (anchor.row == lead.row && anchor.column > lead.column)){
17 | var tmp = lead;
18 | lead = anchor;
19 | anchor = tmp;
20 | }
21 | if (onClick) onClick(
22 | { clientX: e.clientX,clientY: e.clientY,
23 | "startLine": anchor.row + 1,
24 | "startCol": anchor.column + 1,
25 | "endLine": lead.row + 1,
26 | "endCol": lead.column + 1
27 | });
28 | }));
29 | editor.on("dblclick", debounce(function(e) {
30 | var pos = e.getDocumentPosition();
31 | if (onDblClick) onDblClick({ clientX: e.clientX,clientY: e.clientY });
32 | }));
33 | return editor;
34 | }
35 |
36 | // Debounce the given function
37 | function debounce(f){
38 | var t;
39 | return function(x){
40 | clearTimeout(t);
41 | t = setTimeout(function(){
42 | f(x);
43 | },100);
44 | };
45 | }
46 |
47 | function getSelectionRange(e){
48 | var r = e.getSelectionRange();
49 | return {"start-col": r.start.column +1 ,
50 | "start-line": r.start.row +1,
51 | "end-col": r.end.column +1,
52 | "end-line": r.start.row+1};
53 | }
54 |
--------------------------------------------------------------------------------
/ghcjs-react.cabal:
--------------------------------------------------------------------------------
1 | name: ghcjs-react
2 | version: 0.0.0
3 | synopsis: Om-inspired React.js support for GHCJS.
4 | description:
5 | license: BSD3
6 | license-file: LICENSE
7 | author: FP Complete
8 | maintainer: chrisdone@fpcomplete.com
9 | copyright:
10 | category: Development
11 | build-type: Simple
12 | extra-source-files: README.md
13 | cabal-version: >=1.10
14 |
15 | library
16 | exposed-modules: React
17 | React.Internal
18 | React.Event
19 | React.Component
20 | React.Builder
21 | React.Lucid
22 | GHCJS.Compat
23 | other-extensions: GADTs
24 | ExistentialQuantification
25 | MultiParamTypeClasses
26 | StandaloneDeriving
27 | RankNTypes
28 | FlexibleContexts
29 | ScopedTypeVariables
30 | CPP
31 | ConstraintKinds
32 | TemplateHaskell
33 | TypeFamilies
34 | BangPatterns
35 | OverloadedStrings
36 | OverloadedLists
37 | TypeSynonymInstances
38 | FlexibleInstances
39 | DeriveGeneric
40 | build-depends: base >=4.7 && <5,
41 | transformers,
42 | containers,
43 | mtl,
44 | vector,
45 | unordered-containers,
46 | stm,
47 | text, ghcjs-dom, ghcjs-base, ghcjs-jquery,
48 | lens
49 | hs-source-dirs: src
50 | default-language: Haskell2010
51 |
--------------------------------------------------------------------------------
/src/React/Lucid.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | -- | Lucid-like elements and attributes.
4 |
5 | module React.Lucid where
6 |
7 | import Data.Text (Text)
8 | import React.Builder
9 | import React.Internal
10 |
11 | div_ :: Monad m
12 | => ReactT state m a -> ReactT state m a
13 | div_ = build "div"
14 |
15 | img_ :: Monad m
16 | => ReactT state m a -> ReactT state m a
17 | img_ = build "img"
18 |
19 | p_ :: Monad m
20 | => ReactT state m a -> ReactT state m a
21 | p_ = build "p"
22 |
23 | h1_ :: Monad m
24 | => ReactT state m a -> ReactT state m a
25 | h1_ = build "h1"
26 |
27 | h2_ :: Monad m
28 | => ReactT state m a -> ReactT state m a
29 | h2_ = build "h2"
30 |
31 | h3_ :: Monad m
32 | => ReactT state m a -> ReactT state m a
33 | h3_ = build "h3"
34 |
35 | h4_ :: Monad m
36 | => ReactT state m a -> ReactT state m a
37 | h4_ = build "h4"
38 |
39 | h5_ :: Monad m
40 | => ReactT state m a -> ReactT state m a
41 | h5_ = build "h5"
42 |
43 | h6_ :: Monad m
44 | => ReactT state m a -> ReactT state m a
45 | h6_ = build "h6"
46 |
47 | ul_ :: Monad m
48 | => ReactT state m a -> ReactT state m a
49 | ul_ = build "ul"
50 |
51 | li_ :: Monad m
52 | => ReactT state m a -> ReactT state m a
53 | li_ = build "li"
54 |
55 | pre_ :: Monad m
56 | => ReactT state m a -> ReactT state m a
57 | pre_ = build "pre"
58 |
59 | span_ :: Monad m
60 | => ReactT state m a -> ReactT state m a
61 | span_ = build "span"
62 |
63 | strong_ :: Monad m
64 | => ReactT state m a -> ReactT state m a
65 | strong_ = build "strong"
66 |
67 | td_ :: Monad m
68 | => ReactT state m a -> ReactT state m a
69 | td_ = build "td"
70 |
71 | th_ :: Monad m
72 | => ReactT state m a -> ReactT state m a
73 | th_ = build "th"
74 |
75 | tr_ :: Monad m
76 | => ReactT state m a -> ReactT state m a
77 | tr_ = build "tr"
78 |
79 | table_ :: Monad m
80 | => ReactT state m a -> ReactT state m a
81 | table_ = build "table"
82 |
83 | tbody_ :: Monad m
84 | => ReactT state m a -> ReactT state m a
85 | tbody_ = build "tbody"
86 |
87 | thead_ :: Monad m
88 | => ReactT state m a -> ReactT state m a
89 | thead_ = build "thead"
90 |
91 |
92 | class_ :: Monad m
93 | => Text -> ReactT state m ()
94 | class_ = attr "className"
95 |
96 | src_ :: Monad m
97 | => Text -> ReactT state m ()
98 | src_ = attr "src"
99 |
100 | title_ :: Monad m
101 | => Text -> ReactT state m ()
102 | title_ = attr "title"
103 |
104 | code_ :: Monad m
105 | => Text -> ReactT state m ()
106 | code_ = attr "code"
107 |
--------------------------------------------------------------------------------
/src/React/Builder.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE KindSignatures #-}
2 | {-# LANGUAGE CPP #-}
3 | {-# LANGUAGE FlexibleInstances #-}
4 | {-# LANGUAGE GADTs #-}
5 | {-# LANGUAGE OverloadedStrings #-}
6 | {-# LANGUAGE RankNTypes #-}
7 | {-# OPTIONS_GHC -fno-warn-orphans #-}
8 |
9 | -- | Element builders.
10 |
11 | module React.Builder where
12 |
13 | import Control.Lens
14 | import Control.Monad.Reader
15 | import Control.Monad.State.Strict
16 | import qualified Data.Map as Map
17 | import Data.Monoid
18 | import Data.String (IsString (..))
19 | import Data.Text (Text)
20 | import qualified Data.Text as T
21 | import qualified Data.Vector as V
22 | import React.Internal
23 |
24 | #ifdef __GHCJS__
25 | import JavaScript.JQuery (JQuery)
26 | import GHCJS.Types
27 | import GHCJS.Marshal
28 | import GHCJS.DOM.Types (Element (..), Event (..))
29 | import GHCJS.Foreign
30 | #endif
31 |
32 | -- | Build an element.
33 | build :: Monad m
34 | => Text -- ^ Name of the element.
35 | -> ReactT state m a -- ^ Inner content.
36 | -> ReactT state m a
37 | build name m =
38 | do var <- ask
39 | (a,child) <- ReactT (ReaderT (const (StateT (\s ->
40 | do r <- runReactT name var m
41 | return (r,s)))))
42 | modifyEl (\e ->
43 | e {elemChildren = elemChildren e <> V.singleton child})
44 | return a
45 |
46 | -- | Build a component.
47 | buildComponent :: (Monad m,MonadIO m)
48 | => Component s a m -- ^ The component.
49 | -> Traversal' s a -- ^ A cursor into the state for this instance.
50 | -> ReactT s m x -- ^ Set attributes for the
51 | -- component. Ignores content (for
52 | -- now).
53 | -> ReactT s m x
54 | buildComponent (Component cls) cursor m =
55 | do app <- ask
56 | (a,child) <-
57 | ReactT (ReaderT (const (StateT (\s ->
58 | do r <-
59 | runReactT "tmp" app m
60 | return (r,s)))))
61 | -- The above is just used for running attributes. ^
62 | cursorId <- liftIO (genCursor app (traversalToCursor cursor))
63 | modifyEl
64 | (\e ->
65 | e {elemChildren =
66 | elemChildren e <>
67 | V.singleton
68 | (RNComponent
69 | (ReactComponent cls
70 | (getProps child)
71 | cursorId))})
72 | return a
73 | where getProps (RNElement (ReactElement "tmp" props _)) = props
74 | getProps x =
75 | error ("getProps: unexpected case: " ++ show x)
76 |
77 | -- | Add some text to the current node's children.
78 | text :: Monad m
79 | => Text -- ^ Text content.
80 | -> ReactT state m ()
81 | text t =
82 | modifyEl (\e -> e {elemChildren = elemChildren e <> V.singleton (RNText t)})
83 |
84 | -- | Add a style.
85 | style :: Monad m => Text
86 | -- ^ CSS property name in JavaScript format; @fontSize@, etc.
87 | -> Text -- ^ Value.
88 | -> ReactT state m ()
89 | style name val = styles [(name,val)]
90 |
91 | -- | Add styles. Does not overwrite existing keys.
92 | styles :: Monad m => [(Text,Text)] -> ReactT state m ()
93 | styles vs =
94 | modifyProps
95 | (\ep ->
96 | ep {epStyle = epStyle ep `Map.union` Map.fromList vs})
97 |
98 | -- | Add attributes. Does not overwrite existing keys.
99 | attrs :: Monad m => [(Text,Text)] -> ReactT state m ()
100 | attrs vs =
101 | modifyProps
102 | (\ep ->
103 | ep {epOtherProps = epOtherProps ep `Map.union` Map.fromList vs})
104 |
105 | -- | Add attributes. Does not overwrite existing keys.
106 | attr :: Monad m
107 | => Text -- ^ Name.
108 | -> Text -- ^ Value.
109 | -> ReactT state m ()
110 | attr name prop =
111 | modifyProps
112 | (\ep ->
113 | ep {epOtherProps = epOtherProps ep `Map.union` Map.fromList [(name,prop)]})
114 |
115 | instance (a ~ (),Monad m) => IsString (ReactT state m a) where
116 | fromString = text . T.pack
117 |
--------------------------------------------------------------------------------
/src/React/Component.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ViewPatterns #-}
2 | {-# LANGUAGE KindSignatures #-}
3 | {-# LANGUAGE RankNTypes #-}
4 | {-# LANGUAGE CPP #-}
5 | {-# LANGUAGE OverloadedStrings #-}
6 | {-# LANGUAGE ScopedTypeVariables #-}
7 | {-# LANGUAGE FlexibleInstances #-}
8 |
9 | -- |
10 |
11 | module React.Component where
12 |
13 |
14 | import Control.Concurrent.STM
15 | import Control.Lens
16 | import Control.Monad
17 | import qualified Data.HashMap.Strict as M
18 | import GHCJS.Compat
19 | import React.Builder
20 | import React.Internal
21 | import Unsafe.Coerce
22 |
23 | #ifdef __GHCJS__
24 | import JavaScript.JQuery (JQuery)
25 | import GHCJS.Types
26 | import GHCJS.Marshal
27 | import GHCJS.DOM.Types (Element (..), Event (..))
28 | import GHCJS.Foreign
29 | import GHCJS.DOM
30 | import GHCJS.DOM.Element
31 | import GHCJS.DOM.Event
32 | #endif
33 |
34 | -- | Make a new class spec.
35 | newClass :: App state m -- ^ Application.
36 | -> (ReactT state m ()) -- ^ Rendering function.
37 | -> (forall props. Traversal' state cursor -> JQuery -> JSRef props -> IO ()) -- ^ Did mount handler.
38 | -> (forall props. Traversal' state cursor -> JSRef props -> IO ()) -- ^ Did update.
39 | -> (forall props. Traversal' state cursor -> JSRef props -> IO Bool) -- ^ Should update?
40 | -> (forall props. Traversal' state cursor -> JSRef props -> IO ()) -- ^ Receiving new props.
41 | -> Class state cursor m
42 | newClass app render didMount didUpdate shouldUpdate recProps =
43 | Class app
44 | render
45 | (\t q p -> didMount t q p)
46 | (\t p -> didUpdate t p)
47 | (\t p -> shouldUpdate t p)
48 | (\t p -> recProps t p)
49 |
50 | -- | Get the app of the class.
51 | classApp :: Class state cursor m -> App state m
52 | classApp = _classApp
53 |
54 | -- | Create a component class.
55 | createComponent :: (Monad m)
56 | => Class state cursor m
57 | -> IO (Component state cursor m)
58 | createComponent cls =
59 | do renderFun <-
60 | syncCallback1
61 | AlwaysRetain
62 | True
63 | (\elRef ->
64 | do el <-
65 | appRun (_classApp cls)
66 | (liftM snd
67 | (runReactT "div"
68 | (_classApp cls)
69 | (do attr "data-component" "true"
70 | (_classRender cls))))
71 | el' <-
72 | toReactElem (_classApp cls)
73 | el
74 | setProp ("r" :: String) el' elRef)
75 | didMountFun <-
76 | syncCallback2
77 | AlwaysRetain
78 | True
79 | (\jq ref ->
80 | do el :: Maybe JQuery <- fromJSRef jq
81 | cursor <- js_React_props_cursor ref
82 | cs <-
83 | atomically (readTVar (appCursors (_classApp cls)))
84 | case M.lookup cursor cs of
85 | Nothing ->
86 | error ("Couldn't find cursor: " ++ show cursor)
87 | Just (Cursor cursorc :: Cursor) ->
88 | do
89 | _classDidMount cls
90 | (cursorToTraversal (unsafeCoerce cursorc))
91 | (maybe (error "didMount: Couldn't get jquery element...") id el)
92 | ref
93 | return ())
94 | didUpdateFun <-
95 | syncCallback1
96 | AlwaysRetain
97 | True
98 | (\ref ->
99 | do cursor <- js_React_props_cursor ref
100 | cs <-
101 | atomically (readTVar (appCursors (_classApp cls)))
102 | case M.lookup cursor cs of
103 | Nothing ->
104 | error ("Couldn't find cursor: " ++ show cursor)
105 | Just (Cursor cursorc :: Cursor) ->
106 | do
107 | _classDidUpdate cls
108 | (cursorToTraversal (unsafeCoerce cursorc))
109 | ref
110 | return ())
111 | shouldUpdateFun <-
112 | syncCallback1
113 | AlwaysRetain
114 | True
115 | (\ref ->
116 | do cursor <- js_React_props_cursor ref
117 | cs <-
118 | atomically (readTVar (appCursors (_classApp cls)))
119 | case M.lookup cursor cs of
120 | Nothing ->
121 | error ("Couldn't find cursor: " ++ show cursor)
122 | Just (Cursor cursorc :: Cursor) ->
123 | do
124 | _classShouldUpdate cls
125 | (cursorToTraversal (unsafeCoerce cursorc))
126 | ref
127 | return ())
128 | willRecPropsFun <-
129 | syncCallback2
130 | AlwaysRetain
131 | True
132 | (\ref newprops ->
133 | do cursor <- js_React_props_cursor ref
134 | cs <-
135 | atomically (readTVar (appCursors (_classApp cls)))
136 | case M.lookup cursor cs of
137 | Nothing ->
138 | error ("Couldn't find cursor: " ++ show cursor)
139 | Just (Cursor cursorc :: Cursor) ->
140 | do
141 | _classReceivingProps cls
142 | (cursorToTraversal (unsafeCoerce cursorc))
143 | newprops
144 | return ())
145 | n <-
146 | js_React_createClass renderFun didMountFun didUpdateFun shouldUpdateFun willRecPropsFun
147 | return (Component n)
148 |
--------------------------------------------------------------------------------
/src/React.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 | {-# LANGUAGE KindSignatures #-}
3 | {-# LANGUAGE RankNTypes #-}
4 | {-# LANGUAGE FlexibleInstances #-}
5 | {-# LANGUAGE GADTs #-}
6 | {-# LANGUAGE CPP #-}
7 | {-# LANGUAGE OverloadedStrings #-}
8 |
9 | module React
10 | (-- * Basic application
11 | -- $react-pre
12 | react
13 | -- ** The app type
14 | -- $the-app-type
15 | ,App
16 | -- $make-app
17 | ,makeApp
18 | -- ** Render function
19 | -- $render
20 | ,ReactT
21 | -- ** The parent DOM element
22 | -- $parent
23 | ,Element
24 | ,getElementById
25 | -- * Building the DOM
26 | -- $building
27 | ,build
28 | ,text
29 | ,style
30 | ,attr
31 | ,styles
32 | ,attrs
33 | -- * Build components
34 | -- $components
35 | ,buildComponent
36 | ,newClass
37 | ,createComponent
38 | ,Class
39 | ,classApp
40 | -- * Handling events
41 | -- $events
42 | ,onEvent
43 | ,onClick
44 | ,onDblClick
45 | ,onMouseMove
46 | ,onMouseOut
47 | )
48 | where
49 |
50 | import React.Builder
51 | import React.Component
52 | import React.Internal
53 | import React.Event
54 |
55 | import Control.Concurrent.STM
56 | import Data.Monoid
57 | import GHCJS.Compat
58 |
59 | #ifdef __GHCJS__
60 | import JavaScript.JQuery (JQuery)
61 | import GHCJS.Types
62 | import GHCJS.Marshal
63 | import GHCJS.DOM.Types (Element (..), Event (..))
64 | import GHCJS.Foreign
65 | import GHCJS.DOM
66 | import GHCJS.DOM.Element
67 | import GHCJS.DOM.Event
68 | #endif
69 |
70 | --------------------------------------------------------------------------------
71 | -- $react-pre
72 | --
73 | -- This module is laid out as both a tutorial and documentation. You
74 | -- should be able to read it like a tutorial from start to end.
75 | --
76 | -- The way to start a React application within an element is via the
77 | -- 'react' function:
78 |
79 | -- | Loops forever. Blocks on state updates; whenever state changes it
80 | -- re-renders.
81 | react :: (Show state,Eq state)
82 | => App state m
83 | -- ^ The application's state.
84 | -> (state -> ReactT state m ())
85 | -- ^ The view renderer.
86 | -> Element
87 | -- ^ The parent DOM element into which the application is
88 | -- placed.
89 | -> IO ()
90 | react app@(App var run _ _) render dom =
91 | do let loop stateOld =
92 | do node <-
93 | run (runReactT "div" app (render stateOld))
94 | reactRender app
95 | dom
96 | (snd node)
97 | stateNew <-
98 | atomically $
99 | do stateNew <- readTVar var
100 | check (stateOld /= stateNew)
101 | return stateNew
102 | loop stateNew
103 | state0 <- atomically (readTVar var)
104 | loop state0
105 | where unwrap (RNElement e) = e
106 | reactRender :: App state m -> Element -> ReactNode state -> IO ()
107 | reactRender app dom re =
108 | do re' <- toReactElem app re
109 | js_React_render re' dom
110 |
111 | --------------------------------------------------------------------------------
112 | -- $the-app-type
113 | --
114 | -- The 'App' type is an opaque type which holds a mutable cell
115 | -- containing the pure state value.
116 |
117 | --------------------------------------------------------------------------------
118 | -- $make-app
119 | --
120 | -- To make an 'App' value, you can use 'makeApp':
121 |
122 | -- | Make the application with the given initial state and runner.
123 | makeApp :: Monad m
124 | => state
125 | -- ^ The initial application state.
126 | -> (forall a. m a -> IO a)
127 | -- ^ The monad runner. By default you can use 'id'.
128 | -> IO (App state m)
129 | -- ^ A new application state.
130 | makeApp state run =
131 | do var <- newTVarIO state
132 | ints <- newTVarIO 0
133 | cursors <- newTVarIO mempty
134 | let app =
135 | App var run ints cursors
136 | return app
137 |
138 | --------------------------------------------------------------------------------
139 | -- $render
140 | --
141 | -- The 'react' function also needs a rendering function. This should
142 | -- receive the current state and run in the 'ReactT' monad.
143 | --
144 | -- For example:
145 | --
146 | -- @
147 | -- \\state -> build "div" (text "Hello, World!")
148 | -- @
149 | --
150 | -- This will create a @\Hello, World!\
@ element.
151 | --
152 | -- You can find a complete list of HTML builder combinators in
153 | -- "React.Builder".
154 |
155 | --------------------------------------------------------------------------------
156 | -- $parent
157 | --
158 | -- React needs a parent element. You can use this handy
159 | -- 'getElementById' below to find it.
160 |
161 | #ifdef __GHCJS__
162 | foreign import javascript "document.getElementById($1)"
163 | getElementById :: JSString -> IO Element
164 | #else
165 | -- | Get the element by its @id=\"foo\"@ identifer..
166 | getElementById :: JSString -- ^ Element identifier.
167 | -> IO Element
168 | getElementById = undefined
169 | #endif
170 |
171 | --------------------------------------------------------------------------------
172 | -- $building
173 | --
174 | -- There are several basic building functions that you can use to
175 | -- render your reactive DOM tree.
176 |
177 | --------------------------------------------------------------------------------
178 | -- $components
179 | --
180 | -- A component is an element which manages its own state, but that
181 | -- state is managed by a 'Lens' into the main application state.
182 | --
183 | -- So when building a component with `buildComponent`, we pass in a
184 | -- lens which lens that component access the state.
185 |
186 | --------------------------------------------------------------------------------
187 | -- $events
188 | --
189 | -- There is one main function for binding event handlers which is
190 | -- `onEvent` (see below). There are also a few basic helper functions
191 | -- like 'onClick', 'onMouseOut', etc.
192 | --
193 | -- For example:
194 | --
195 | -- @
196 | -- build \"button\" (do text \"Click me!\"
197 | -- onClick (\event state -> putStrLn "You clicked me!"))
198 | -- @
199 | --
200 | -- There is a variety of event predicates and accessors which can be
201 | -- found in "React.Events".
202 |
--------------------------------------------------------------------------------
/examples/es5-sham.min.js:
--------------------------------------------------------------------------------
1 | (function(definition){if(typeof define=="function"){define(definition)}else if(typeof YUI=="function"){YUI.add("es5-sham",definition)}else{definition()}})(function(){var call=Function.prototype.call;var prototypeOfObject=Object.prototype;var owns=call.bind(prototypeOfObject.hasOwnProperty);var defineGetter;var defineSetter;var lookupGetter;var lookupSetter;var supportsAccessors;if(supportsAccessors=owns(prototypeOfObject,"__defineGetter__")){defineGetter=call.bind(prototypeOfObject.__defineGetter__);defineSetter=call.bind(prototypeOfObject.__defineSetter__);lookupGetter=call.bind(prototypeOfObject.__lookupGetter__);lookupSetter=call.bind(prototypeOfObject.__lookupSetter__)}if(!Object.getPrototypeOf){Object.getPrototypeOf=function getPrototypeOf(object){return object.__proto__||(object.constructor?object.constructor.prototype:prototypeOfObject)}}function doesGetOwnPropertyDescriptorWork(object){try{object.sentinel=0;return Object.getOwnPropertyDescriptor(object,"sentinel").value===0}catch(exception){}}if(Object.defineProperty){var getOwnPropertyDescriptorWorksOnObject=doesGetOwnPropertyDescriptorWork({});var getOwnPropertyDescriptorWorksOnDom=typeof document=="undefined"||doesGetOwnPropertyDescriptorWork(document.createElement("div"));if(!getOwnPropertyDescriptorWorksOnDom||!getOwnPropertyDescriptorWorksOnObject){var getOwnPropertyDescriptorFallback=Object.getOwnPropertyDescriptor}}if(!Object.getOwnPropertyDescriptor||getOwnPropertyDescriptorFallback){var ERR_NON_OBJECT="Object.getOwnPropertyDescriptor called on a non-object: ";Object.getOwnPropertyDescriptor=function getOwnPropertyDescriptor(object,property){if(typeof object!="object"&&typeof object!="function"||object===null){throw new TypeError(ERR_NON_OBJECT+object)}if(getOwnPropertyDescriptorFallback){try{return getOwnPropertyDescriptorFallback.call(Object,object,property)}catch(exception){}}if(!owns(object,property)){return}var descriptor={enumerable:true,configurable:true};if(supportsAccessors){var prototype=object.__proto__;object.__proto__=prototypeOfObject;var getter=lookupGetter(object,property);var setter=lookupSetter(object,property);object.__proto__=prototype;if(getter||setter){if(getter){descriptor.get=getter}if(setter){descriptor.set=setter}return descriptor}}descriptor.value=object[property];descriptor.writable=true;return descriptor}}if(!Object.getOwnPropertyNames){Object.getOwnPropertyNames=function getOwnPropertyNames(object){return Object.keys(object)}}if(!Object.create){var createEmpty;var supportsProto=Object.prototype.__proto__===null;if(supportsProto||typeof document=="undefined"){createEmpty=function(){return{__proto__:null}}}else{createEmpty=function(){var iframe=document.createElement("iframe");var parent=document.body||document.documentElement;iframe.style.display="none";parent.appendChild(iframe);iframe.src="javascript:";var empty=iframe.contentWindow.Object.prototype;parent.removeChild(iframe);iframe=null;delete empty.constructor;delete empty.hasOwnProperty;delete empty.propertyIsEnumerable;delete empty.isPrototypeOf;delete empty.toLocaleString;delete empty.toString;delete empty.valueOf;empty.__proto__=null;function Empty(){}Empty.prototype=empty;createEmpty=function(){return new Empty};return new Empty}}Object.create=function create(prototype,properties){var object;function Type(){}if(prototype===null){object=createEmpty()}else{if(typeof prototype!=="object"&&typeof prototype!=="function"){throw new TypeError("Object prototype may only be an Object or null")}Type.prototype=prototype;object=new Type;object.__proto__=prototype}if(properties!==void 0){Object.defineProperties(object,properties)}return object}}function doesDefinePropertyWork(object){try{Object.defineProperty(object,"sentinel",{});return"sentinel"in object}catch(exception){}}if(Object.defineProperty){var definePropertyWorksOnObject=doesDefinePropertyWork({});var definePropertyWorksOnDom=typeof document=="undefined"||doesDefinePropertyWork(document.createElement("div"));if(!definePropertyWorksOnObject||!definePropertyWorksOnDom){var definePropertyFallback=Object.defineProperty,definePropertiesFallback=Object.defineProperties}}if(!Object.defineProperty||definePropertyFallback){var ERR_NON_OBJECT_DESCRIPTOR="Property description must be an object: ";var ERR_NON_OBJECT_TARGET="Object.defineProperty called on non-object: ";var ERR_ACCESSORS_NOT_SUPPORTED="getters & setters can not be defined "+"on this javascript engine";Object.defineProperty=function defineProperty(object,property,descriptor){if(typeof object!="object"&&typeof object!="function"||object===null){throw new TypeError(ERR_NON_OBJECT_TARGET+object)}if(typeof descriptor!="object"&&typeof descriptor!="function"||descriptor===null){throw new TypeError(ERR_NON_OBJECT_DESCRIPTOR+descriptor)}if(definePropertyFallback){try{return definePropertyFallback.call(Object,object,property,descriptor)}catch(exception){}}if(owns(descriptor,"value")){if(supportsAccessors&&(lookupGetter(object,property)||lookupSetter(object,property))){var prototype=object.__proto__;object.__proto__=prototypeOfObject;delete object[property];object[property]=descriptor.value;object.__proto__=prototype}else{object[property]=descriptor.value}}else{if(!supportsAccessors){throw new TypeError(ERR_ACCESSORS_NOT_SUPPORTED)}if(owns(descriptor,"get")){defineGetter(object,property,descriptor.get)}if(owns(descriptor,"set")){defineSetter(object,property,descriptor.set)}}return object}}if(!Object.defineProperties||definePropertiesFallback){Object.defineProperties=function defineProperties(object,properties){if(definePropertiesFallback){try{return definePropertiesFallback.call(Object,object,properties)}catch(exception){}}for(var property in properties){if(owns(properties,property)&&property!="__proto__"){Object.defineProperty(object,property,properties[property])}}return object}}if(!Object.seal){Object.seal=function seal(object){return object}}if(!Object.freeze){Object.freeze=function freeze(object){return object}}try{Object.freeze(function(){})}catch(exception){Object.freeze=function freeze(freezeObject){return function freeze(object){if(typeof object=="function"){return object}else{return freezeObject(object)}}}(Object.freeze)}if(!Object.preventExtensions){Object.preventExtensions=function preventExtensions(object){return object}}if(!Object.isSealed){Object.isSealed=function isSealed(object){return false}}if(!Object.isFrozen){Object.isFrozen=function isFrozen(object){return false}}if(!Object.isExtensible){Object.isExtensible=function isExtensible(object){if(Object(object)!==object){throw new TypeError}var name="";while(owns(object,name)){name+="?"}object[name]=true;var returnValue=owns(object,name);delete object[name];return returnValue}}});
2 | //# sourceMappingURL=es5-sham.map
--------------------------------------------------------------------------------
/src/React/Event.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | {-# LANGUAGE TypeSynonymInstances #-}
3 | {-# LANGUAGE CPP #-}
4 | {-# LANGUAGE FlexibleInstances #-}
5 |
6 | -- |
7 |
8 | module React.Event where
9 |
10 | import Control.Concurrent.STM (TVar, check, atomically, readTVar, modifyTVar)
11 | import Control.Monad.Reader (ReaderT(..), MonadReader(ask))
12 | import Data.Coerce (coerce, Coercible)
13 | import qualified Data.Map as Map (union, fromList, toList, null)
14 | import Data.Text (Text)
15 | import qualified Data.Text as T
16 | import GHCJS.Compat
17 | import React.Internal
18 |
19 | #ifdef __GHCJS__
20 | import JavaScript.JQuery (JQuery)
21 | import GHCJS.Types
22 | import GHCJS.Marshal
23 | import GHCJS.DOM.Types (Element (..), Event (..))
24 | import GHCJS.Foreign
25 | import GHCJS.DOM
26 | import GHCJS.DOM.Element
27 | import GHCJS.DOM.Event
28 | #endif
29 |
30 | onEvent :: (IsReactEvent event, Monad m)
31 | => EventListener event
32 | -> (event -> TVar state -> IO ())
33 | -> ReactT state m ()
34 | onEvent (EventListener name) f = onRaw name $ \re var -> f (coerce re) var
35 |
36 | -- | Add event handler. Does not overwrite existing keys.
37 | onRaw :: Monad m => Text -> (ReactEvent -> TVar state -> IO ()) -> ReactT state m ()
38 | onRaw name f =
39 | do app <- ask
40 | modifyProps
41 | (\ep ->
42 | ep {epEvents =
43 | epEvents ep `Map.union`
44 | Map.fromList
45 | [(name,\ev -> f ev (appState app))]})
46 |
47 | bubbles :: IsReactEvent event => event -> IO Bool
48 | bubbles = getPropBool "bubbles" . coerce
49 |
50 | cancelable :: IsReactEvent event => event -> IO Bool
51 | cancelable = getPropBool "cancelable" . coerce
52 |
53 | currentTarget :: IsReactEvent event => event -> IO Element
54 | currentTarget = getPropElement "currentTarget" . coerce
55 |
56 | defaultPrevented :: IsReactEvent event => event -> IO Bool
57 | defaultPrevented = getPropBool "defaultPrevented" . coerce
58 |
59 | eventPhase :: IsReactEvent event => event -> IO Int
60 | eventPhase = getPropInt "eventPhase" . coerce
61 |
62 | isTrusted :: IsReactEvent event => event -> IO Bool
63 | isTrusted = getPropBool "isTrusted" . coerce
64 |
65 | nativeEvent :: IsReactEvent event => event -> IO Event
66 | nativeEvent = getPropEvent "nativeEvent" . coerce
67 |
68 | preventDefault :: IsReactEvent event => event -> IO ()
69 | preventDefault = js_preventDefault . coerce
70 |
71 | stopPropagation :: IsReactEvent event => event -> IO ()
72 | stopPropagation = js_stopPropagation . coerce
73 |
74 | target :: IsReactEvent event => event -> IO Element
75 | target = getPropElement "target" . coerce
76 |
77 | -- FIXME missing timeStamp
78 |
79 | eventType :: IsReactEvent event => event -> IO Text
80 | eventType e = fmap fromJSString $ getProp ("type" :: JSString) (coerce e :: ReactEvent)
81 |
82 | newtype ClipboardEvent = ClipboardEvent ReactEvent
83 | instance IsReactEvent ClipboardEvent
84 |
85 | copy, cut, paste :: EventListener ClipboardEvent
86 | copy = EventListener "copy"
87 | cut = EventListener "cut"
88 | paste = EventListener "paste"
89 |
90 | onCopy :: Monad m => (ClipboardEvent -> TVar state -> IO ()) -> ReactT state m ()
91 | onCopy = onEvent copy
92 |
93 | onCut :: Monad m => (ClipboardEvent -> TVar state -> IO ()) -> ReactT state m ()
94 | onCut = onEvent cut
95 |
96 | onPaste :: Monad m => (ClipboardEvent -> TVar state -> IO ()) -> ReactT state m ()
97 | onPaste = onEvent paste
98 |
99 | class IsReactEvent event => IsClipboardEvent event
100 | instance IsClipboardEvent ReactEvent
101 | instance IsClipboardEvent ClipboardEvent
102 |
103 | clipboardData :: IsClipboardEvent event => event -> IO (JSRef a) -- FIXME better data type
104 | clipboardData e = getProp ("clipboardData" :: JSString) (coerce e :: ReactEvent)
105 |
106 | newtype MouseEvent = MouseEvent ReactEvent
107 | instance IsReactEvent MouseEvent
108 |
109 | dblClick,click, mouseMove, mouseOut :: EventListener MouseEvent
110 | click = EventListener "click"
111 | dblClick = EventListener "dblClick"
112 | mouseMove = EventListener "mouseMove"
113 | mouseOut = EventListener "mouseOut"
114 |
115 | onClick :: Monad m => (MouseEvent -> TVar state -> IO ()) -> ReactT state m ()
116 | onClick = onEvent click
117 |
118 | onDblClick :: Monad m => (MouseEvent -> TVar state -> IO ()) -> ReactT state m ()
119 | onDblClick = onEvent dblClick
120 |
121 | onMouseMove :: Monad m => (MouseEvent -> TVar state -> IO ()) -> ReactT state m ()
122 | onMouseMove = onEvent mouseMove
123 |
124 | onMouseOut :: Monad m => (MouseEvent -> TVar state -> IO ()) -> ReactT state m ()
125 | onMouseOut = onEvent mouseOut
126 |
127 | class IsReactEvent event => IsMouseEvent event
128 | instance IsMouseEvent ReactEvent
129 | instance IsMouseEvent MouseEvent
130 |
131 | altKey :: IsMouseEvent event => event -> IO Bool
132 | altKey = getPropBool "altKey" . coerce
133 |
134 | button :: IsMouseEvent event => event -> IO Int
135 | button = getPropInt "button" . coerce
136 |
137 | buttons :: IsMouseEvent event => event -> IO Int
138 | buttons = getPropInt "buttons" . coerce
139 |
140 | clientX :: IsMouseEvent event => event -> IO Int
141 | clientX = getPropInt "clientX" . coerce
142 |
143 | clientY :: IsMouseEvent event => event -> IO Int
144 | clientY = getPropInt "clientY" . coerce
145 |
146 | ctrlKey :: IsMouseEvent event => event -> IO Bool
147 | ctrlKey = getPropBool "ctrlKey" . coerce
148 |
149 | getModifierState :: IsMouseEvent event => event -> Text -> IO Bool
150 | getModifierState e t = js_getModifierState (coerce e) (toJSString t)
151 |
152 | metaKey :: IsMouseEvent event => event -> IO Bool
153 | metaKey = getPropBool "metaKey" . coerce
154 |
155 | pageX :: IsMouseEvent event => event -> IO Int
156 | pageX = getPropInt "pageX" . coerce
157 |
158 | pageY :: IsMouseEvent event => event -> IO Int
159 | pageY = getPropInt "pageY" . coerce
160 |
161 | relatedTarget :: IsMouseEvent event => event -> IO Element
162 | relatedTarget = getPropElement "relatedTarget" . coerce
163 |
164 | screenX :: IsMouseEvent event => event -> IO Int
165 | screenX = getPropInt "screenX" . coerce
166 |
167 | screenY :: IsMouseEvent event => event -> IO Int
168 | screenY = getPropInt "screenY" . coerce
169 |
170 | shiftKey :: IsMouseEvent event => event -> IO Bool
171 | shiftKey = getPropBool "shiftKey" . coerce
172 |
173 | -- | Get event val.
174 | getVal :: ReactEvent -> IO Text
175 | getVal ev =
176 | do jstr <- getVal_ ev
177 | return (T.pack (fromJSString jstr))
178 |
179 | #ifdef __GHCJS__
180 | foreign import javascript "$1.target.value"
181 | getVal_ :: ReactEvent -> IO JSString
182 | #else
183 | getVal_ :: ReactEvent -> IO JSString
184 | getVal_ = undefined
185 | #endif
186 |
--------------------------------------------------------------------------------
/examples/React/Ace.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE NoMonomorphismRestriction #-}
2 | {-# LANGUAGE Rank2Types #-}
3 | {-# LANGUAGE CPP #-}
4 | {-# LANGUAGE TypeSynonymInstances #-}
5 | {-# LANGUAGE FlexibleInstances #-}
6 | {-# LANGUAGE OverloadedStrings #-}
7 | {-# LANGUAGE ScopedTypeVariables #-}
8 | {-# OPTIONS_GHC -fno-warn-orphans #-}
9 |
10 | -- | React binding to Ace.
11 |
12 | module React.Ace where
13 |
14 | import Control.Concurrent.STM
15 | import Control.Lens hiding (coerce)
16 | import Control.Monad
17 | import qualified Data.Text as T
18 | import GHC.Exts
19 | import GHCJS.Compat
20 | import React hiding (onClick)
21 | import React.Internal
22 |
23 | #ifdef __GHCJS__
24 | import JavaScript.JQuery (JQuery)
25 | import GHCJS.Types
26 | import GHCJS.Marshal
27 | import GHCJS.DOM.Types (Element (..), Event (..))
28 | import GHCJS.Foreign
29 | import GHCJS.Types
30 | import GHCJS.Marshal
31 | import GHCJS.DOM.Types (Element (..), Event (..))
32 | import GHCJS.Foreign
33 | import GHCJS.DOM
34 | #endif
35 |
36 | --------------------------------------------------------------------------------
37 | -- Types
38 |
39 | -- | Ace component's state.
40 | data Ace = Ace (Maybe Editor')
41 |
42 | -- | An Editor editor.
43 | data Editor_
44 | type Editor' = JSRef Editor_
45 |
46 | -- | Default state for instances of ace.
47 | getDef :: IO Ace
48 | getDef = return (Ace Nothing)
49 |
50 | --------------------------------------------------------------------------------
51 | -- Component
52 |
53 | -- | Create an Ace editor component.
54 | new :: Monad m
55 | => App state m -- ^ The app.
56 | -> IO (Component state Ace m) -- ^ Ace component.
57 | new app =
58 | createComponent
59 | (newClass app
60 | (return ())
61 | (didMount app)
62 | (\_l _props -> return ())
63 | (\_ _ -> return False)
64 | (receivingProps app))
65 |
66 | -- | Setup the ace editor.
67 | didMount :: App a m -> Traversal' a Ace -> JQuery -> JSRef this -> IO ()
68 | didMount app r el this =
69 | do putStrLn "Did mount!"
70 | props <- getProp ("props" :: JSString) this
71 | onClickFun <- getProp ("onClick" :: JSString) props
72 | onDblClickFun <- getProp ("onDblClick" :: JSString) props
73 | editor <- makeEditor el
74 | onClickFun
75 | onDblClickFun
76 | atomically
77 | (modifyTVar (appState app)
78 | (set r (Ace (Just editor))))
79 |
80 | -- | New code attribute has been set, update the editor contents.
81 | receivingProps :: App state m -> Traversal' state Ace -> JSRef a -> IO ()
82 | receivingProps app l props =
83 | do putStrLn "Receiving props!"
84 | codeRef <- getProp ("code" :: JSString) props
85 | mcode <- fromJSRef codeRef
86 | case mcode of
87 | Nothing -> return ()
88 | Just (code :: JSString) ->
89 | do meditor <- fmap (preview l)
90 | (atomically (readTVar (appState app)))
91 | case meditor of
92 | Nothing -> return ()
93 | Just (Ace (Just editor)) ->
94 | do code' <- getValue editor
95 | putStrLn ("Code " ++ fromJSString code)
96 | putStrLn ("Code' " ++ fromJSString code')
97 | when (code /= code')
98 | (do putStrLn "Set the value!"
99 | setValue editor code)
100 | --
101 | stateStartLine <- getStartLine props
102 | stateStartCol <- getStartCol props
103 | stateEndLine <- getEndLine props
104 | stateEndCol <- getEndCol props
105 | --
106 | range <- getSelectionRange editor
107 | curStartLine <- getStartLine range
108 | curStartCol <- getStartCol range
109 | curEndLine <- getEndLine range
110 | curEndCol <- getEndCol range
111 | --
112 | case (stateStartLine,stateStartCol,stateEndLine,stateEndCol) of
113 | (Just sl,Just sc,Just el,Just ec) ->
114 | when ((stateStartLine
115 | ,stateStartCol
116 | ,stateEndLine
117 | ,stateEndCol) /=
118 | (curStartLine
119 | ,curStartCol
120 | ,curEndLine
121 | ,curEndCol))
122 | (setRange editor (sl-1) (sc-1) (el-1) (ec-1))
123 | _ -> return ()
124 | _ -> return ()
125 |
126 | --------------------------------------------------------------------------------
127 | -- Properties
128 |
129 | startline_ :: (Monad m) => Int -> ReactT state m ()
130 | startline_ =
131 | attr "startline" .
132 | T.pack . show
133 |
134 | startcol_ :: (Monad m) => Int -> ReactT state m ()
135 | startcol_ =
136 | attr "startcol" .
137 | T.pack . show
138 |
139 | endline_ :: (Monad m) => Int -> ReactT state m ()
140 | endline_ =
141 | attr "endline" .
142 | T.pack . show
143 |
144 | endcol_ :: (Monad m) => Int -> ReactT state m ()
145 | endcol_ = attr "endcol". T.pack . show
146 |
147 | --------------------------------------------------------------------------------
148 | -- Selection range accessors
149 |
150 | getStartLine :: JSRef a -> IO (Maybe Int)
151 | getStartLine props =
152 | do r <- getProp ("start-line" :: JSString) props
153 | fromJSRef r
154 |
155 | getEndLine :: JSRef a -> IO (Maybe Int)
156 | getEndLine props =
157 | do r <- getProp ("end-line" :: JSString) props
158 | fromJSRef r
159 |
160 | getStartCol :: JSRef a -> IO (Maybe Int)
161 | getStartCol props =
162 | do r <- getProp ("start-col" :: JSString) props
163 | fromJSRef r
164 |
165 | getEndCol :: JSRef a -> IO (Maybe Int)
166 | getEndCol props =
167 | do r <- getProp ("end-col" :: JSString) props
168 | fromJSRef r
169 |
170 | --------------------------------------------------------------------------------
171 | -- Component events
172 |
173 | newtype SelectEvent = SelectEvent ReactEvent
174 | instance IsReactEvent SelectEvent
175 |
176 | -- | When the selection changes.
177 | onClick :: Monad m => (SelectEvent -> TVar state -> IO ()) -> ReactT state m ()
178 | onClick = onEvent (EventListener "click")
179 |
180 | -- | Extract the start line from the event.
181 | selectStartLine :: SelectEvent -> IO Int
182 | selectStartLine = getPropInt "startLine" . coerce
183 |
184 | -- | Extract the start col from the event.
185 | selectStartCol :: SelectEvent -> IO Int
186 | selectStartCol = getPropInt "startCol" . coerce
187 |
188 | -- | Extract the end line from the event.
189 | selectEndLine :: SelectEvent -> IO Int
190 | selectEndLine = getPropInt "endLine" . coerce
191 |
192 | -- | Extract the end col from the event.
193 | selectEndCol :: SelectEvent -> IO Int
194 | selectEndCol = getPropInt "endCol" . coerce
195 |
196 | clientX :: SelectEvent -> IO Int
197 | clientX = getPropInt "clientX" . coerce
198 |
199 | clientY :: SelectEvent -> IO Int
200 | clientY = getPropInt "clientY" . coerce
201 |
202 | --------------------------------------------------------------------------------
203 | -- Foreign imports
204 |
205 | #ifdef __GHCJS__
206 |
207 | foreign import javascript "makeEditor($1,$2,$3)"
208 | makeEditor :: JQuery
209 | -> JSFun (JSRef props -> IO ())
210 | -> JSFun (JSRef props -> IO ())
211 | -> IO Editor'
212 |
213 | foreign import javascript "($1).setValue($2,-1)"
214 | setValue :: Editor' -> JSString -> IO ()
215 |
216 | foreign import javascript "getSelectionRange($1)"
217 | getSelectionRange :: Editor' -> IO (JSRef Int)
218 |
219 | foreign import javascript "($1).selection.setSelectionRange(new AceRange($2,$3,$4,$5))"
220 | setRange :: Editor' -> Int -> Int -> Int -> Int -> IO ()
221 |
222 | foreign import javascript "($1).getValue()"
223 | getValue :: Editor' -> IO JSString
224 |
225 | foreign import javascript "$1===$2"
226 | stringEq :: JSString -> JSString -> Bool
227 |
228 | #else
229 |
230 | getSelectionRange :: Editor' -> IO (JSRef Int)
231 | getSelectionRange = undefined
232 |
233 | setRange :: Editor' -> Int -> Int -> Int -> Int -> IO ()
234 | setRange = undefined
235 |
236 | makeEditor :: JQuery
237 | -> (JSRef props -> IO ())
238 | -> (JSRef props -> IO ())
239 | -> IO Editor'
240 | makeEditor = undefined
241 |
242 | setValue :: Editor' -> JSString -> IO ()
243 | setValue = undefined
244 |
245 | getValue :: Editor' -> IO JSString
246 | getValue = undefined
247 |
248 | stringEq :: JSString -> JSString -> Bool
249 | stringEq = undefined
250 |
251 | #endif
252 |
253 | instance Eq JSString where
254 | (==) = stringEq
255 |
256 | --------------------------------------------------------------------------------
257 | -- Instances
258 |
259 | instance Show Ace where
260 | show (Ace m) = ">"
261 | where c = case m of
262 | Nothing -> "Nothing"
263 | Just _ -> "Just "
264 |
265 | instance Eq Ace where
266 | Ace Nothing == Ace Nothing = True
267 | Ace (Just{}) == Ace (Just {}) = True
268 | _ == _ = False
269 |
--------------------------------------------------------------------------------
/src/React/Monad.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE ScopedTypeVariables #-}
2 | {-# LANGUAGE KindSignatures #-}
3 | {-# LANGUAGE ExistentialQuantification #-}
4 | -- |
5 |
6 | module React.Monad where
7 |
8 | import Control.Applicative (Applicative((<*>), pure), (<$>))
9 | import Control.Concurrent.STM (TVar, check, atomically, readTVar, modifyTVar)
10 | import Control.Monad (liftM, join, unless, forM_, (<=<))
11 | import Control.Monad.Reader (ReaderT(..), MonadReader(ask))
12 | import Control.Monad.State.Strict (MonadState, StateT(..), modify)
13 | import Data.Coerce (coerce, Coercible)
14 | import Data.Functor.Identity (Identity)
15 | import Data.Map (Map)
16 | import qualified Data.Map as M (insert, lookup)
17 | import qualified Data.Map as Map (union, fromList, toList, null)
18 | import Data.Monoid (Monoid(mempty), (<>))
19 | import Data.String (IsString(..))
20 | import Data.Text (Text)
21 | import qualified Data.Text as T (toUpper, take, pack, drop, concat)
22 | import Data.Vector (Vector)
23 | import qualified Data.Vector as V (toList, singleton, mapM)
24 | import GHC.Generics (Generic)
25 | import GHCJS.Compat
26 | import Unsafe.Coerce (unsafeCoerce)
27 |
28 |
29 | instance ToJSRef (Lens state cursor) where
30 | toJSRef = unsafeCoerce
31 |
32 | instance FromJSRef (Lens state cursor) where
33 | fromJSRef = return . Just . unsafeCoerce
34 |
35 | instance Show (Lens state cursor) where
36 | show _ = ""
37 |
38 | -- | Build an element.
39 | build :: Monad m
40 | => Text -> ReactT state m a -> ReactT state m a
41 | build name m =
42 | do var <- ask
43 | (a,child) <- ReaderT (const (StateT (\s ->
44 | do r <- runReactT name var m
45 | return (r,s))))
46 | modifyEl (\e ->
47 | e {elemChildren = elemChildren e <> V.singleton child})
48 | return a
49 |
50 | -- | Build a component.
51 | buildComponent :: Monad m
52 | => Component state cursor m -- ^ The component.
53 | -> Lens state cursor -- ^ A cursor into the state for this instance.
54 | -> ReactT state m a -- ^ Set attributes for the
55 | -- component. Ignores content (for
56 | -- now).
57 | -> ReactT state m a
58 | buildComponent (Component cls) cursor m =
59 | do var <- ask
60 | (a,child) <- ReaderT (const (StateT (\s ->
61 | do r <- runReactT "tmp" var m
62 | return (r,s))))
63 | -- The above is just used for running attributes. ^
64 | modifyEl (\e ->
65 | e {elemChildren =
66 | elemChildren e <>
67 | V.singleton
68 | (RNComponent
69 | (ReactComponent cls
70 | (getProps child)
71 | cursor))})
72 | return a
73 | where getProps (RNElement (ReactElement "tmp" props _)) = props
74 | getProps x = error ("getProps: unexpected case: " ++ show x)
75 |
76 | -- | Add some text to the current node's children.
77 | text :: Monad m
78 | => Text -> ReactT state m ()
79 | text t =
80 | modifyEl (\e -> e {elemChildren = elemChildren e <> V.singleton (RNText t)})
81 |
82 | -- | Add a style.
83 | style :: Monad m => Text -> Text -> ReactT state m ()
84 | style name val = styles [(name,val)]
85 |
86 | -- | Add styles. Does not overwrite existing keys.
87 | styles :: Monad m => [(Text,Text)] -> ReactT state m ()
88 | styles vs =
89 | modifyProps
90 | (\ep ->
91 | ep {epStyle = epStyle ep `Map.union` Map.fromList vs})
92 |
93 | -- | Add attributes. Does not overwrite existing keys.
94 | attrs :: Monad m => [(Text,Text)] -> ReactT state m ()
95 | attrs vs =
96 | modifyProps
97 | (\ep ->
98 | ep {epOtherProps = epOtherProps ep `Map.union` Map.fromList vs})
99 |
100 | -- | Add attributes. Does not overwrite existing keys.
101 | attr :: Monad m => Text -> Text -> ReactT state m ()
102 | attr name prop =
103 | modifyProps
104 | (\ep ->
105 | ep {epOtherProps = epOtherProps ep `Map.union` Map.fromList [(name,prop)]})
106 |
107 | -- | Add event handler. Does not overwrite existing keys.
108 | onRaw :: Monad m => Text -> (ReactEvent -> TVar state -> IO ()) -> ReactT state m ()
109 | onRaw name f =
110 | do var <- ask
111 | modifyProps
112 | (\ep ->
113 | ep {epEvents =
114 | epEvents ep `Map.union`
115 | Map.fromList
116 | [(name,\ev -> f ev var)]})
117 |
118 | -- | React transformer.
119 | type ReactT state m = ReaderT (TVar state) (StateT (ReactNode state) m)
120 |
121 | -- | Pure react monad.
122 | type React state = ReactT state Identity
123 |
124 | instance (a ~ (),Monad m) => IsString (ReaderT (TVar state) (StateT (ReactNode state) m) a) where
125 | fromString = text . T.pack
126 |
127 | -- | Modify the element in the state.
128 | --
129 | -- In the case the current node is text, this is a no-op. Given that
130 | -- the API doesn't have a way to `build' inside a Text, that's
131 | -- okay. We could GADT the ReactNode type to remove this case, but not
132 | -- worth it ATM. Also there is now a Component type, but I don't know
133 | -- how to build inside one of those either, so it's also a no-op (for
134 | -- now).
135 | modifyEl :: Monad m => (ReactElement state -> ReactElement state) -> ReactT state m ()
136 | modifyEl f =
137 | modify (\rn ->
138 | case rn of
139 | RNElement e -> RNElement (f e)
140 | text -> text)
141 |
142 | -- | Modify properties of the element.
143 | modifyProps :: MonadState (ReactNode state) m => (ElemProps -> ElemProps) -> m ()
144 | modifyProps f =
145 | modify (\s ->
146 | case s of
147 | RNElement e ->
148 | RNElement (e {elemProps =
149 | f (elemProps e)})
150 | RNComponent c ->
151 | RNComponent
152 | (c {compProps = f (compProps c)})
153 | RNText{} -> s)
154 |
155 | -- | Run the react monad.
156 | runReactT :: Text -> TVar state -> ReactT state m a -> m (a,ReactNode state)
157 | runReactT name var m = runStateT (runReaderT m var) init
158 | where init =
159 | (RNElement (ReactElement name
160 | (ElemProps mempty mempty mempty)
161 | mempty))
162 |
163 | -- | A component for some state transforming over some monad.
164 | data Component state cursor (m :: * -> *) =
165 | Component (ReactClass' cursor)
166 |
167 | -- | State cursor.
168 | data Cursor =
169 | forall cursor state. Cursor (Lens cursor state)
170 |
171 | -- | An application with some state transforming over some monad.
172 | data App state m =
173 | App {appState :: TVar state -- ^ State used for rendering.
174 | ,appRun :: forall a. m a -> IO a -- ^ Function for running in the particular monad.
175 | ,appIdSource :: TVar Int -- ^ Source of integers.
176 | ,appCursors :: TVar (Map Int Cursor) -- ^ Mapping from integer to state cursors.
177 | }
178 |
179 | -- | Class used for creating components.
180 | data Class state cursor m =
181 | Class {classApp :: App state m -- ^ Application.
182 | ,classRender :: ReactT state m () -- ^ Rendering function.
183 | ,classDidMount :: (forall props. Lens state cursor -> JQuery -> JSRef props -> IO ()) -- ^ Did mount handler.
184 | ,classDidUpdate :: (forall props. Lens state cursor -> JSRef props -> IO ()) -- ^ Did update.
185 | ,classShouldUpdate :: (forall props. Lens state cursor -> JSRef props -> IO Bool) -- ^ Should update?
186 | ,classReceivingProps :: (forall props. Lens state cursor -> JSRef props -> IO ()) -- ^ Receiving new props.
187 | }
188 |
189 | -- | Create a component class.
190 | createClass :: (Monad m)
191 | => Class state cursor m
192 | -> IO (Component state cursor m)
193 | createClass cls =
194 | do renderFun <- syncCallback1
195 | AlwaysRetain
196 | True
197 | (\elRef ->
198 | do el <- appRun (classApp cls)
199 | (liftM snd
200 | (runReactT "div"
201 | (appState (classApp cls))
202 | (do attr "data-component" "true"
203 | (classRender cls))))
204 | el' <- toReactElem (classApp cls)
205 | el
206 | setProp ("r" :: String) el' elRef)
207 | didMountFun <- syncCallback2
208 | AlwaysRetain
209 | True
210 | (\jq ref ->
211 | do el :: Maybe JQuery <- fromJSRef jq
212 | cursor <- js_React_props_cursor ref
213 | cs <- atomically (readTVar (appCursors (classApp cls)))
214 | case M.lookup cursor cs of
215 | Nothing ->
216 | error ("Couldn't find cursor: " ++ show cursor)
217 | Just (Cursor cursor) ->
218 | case unsafeCoerce cursor of
219 | c ->
220 | classDidMount
221 | cls
222 | c
223 | (maybe (error "didMount: Couldn't get jquery element...")
224 | id
225 | el)
226 | ref
227 | return ())
228 | didUpdateFun <- syncCallback1
229 | AlwaysRetain
230 | True
231 | (\ref ->
232 | do cursor <- js_React_props_cursor ref
233 | cs <- atomically (readTVar (appCursors (classApp cls)))
234 | case M.lookup cursor cs of
235 | Nothing ->
236 | error ("Couldn't find cursor: " ++ show cursor)
237 | Just (Cursor cursor) ->
238 | case unsafeCoerce cursor of
239 | c ->
240 | classDidUpdate cls c ref
241 | return ())
242 | shouldUpdateFun <- syncCallback1
243 | AlwaysRetain
244 | True
245 | (\ref ->
246 | do cursor <- js_React_props_cursor ref
247 | cs <- atomically (readTVar (appCursors (classApp cls)))
248 | case M.lookup cursor cs of
249 | Nothing ->
250 | error ("Couldn't find cursor: " ++
251 | show cursor)
252 | Just (Cursor cursor) ->
253 | case unsafeCoerce cursor of
254 | c ->
255 | classShouldUpdate cls c ref
256 | return ())
257 | willRecPropsFun <- syncCallback2
258 | AlwaysRetain
259 | True
260 | (\ref newprops ->
261 | do cursor <- js_React_props_cursor ref
262 | cs <- atomically (readTVar (appCursors (classApp cls)))
263 | case M.lookup cursor cs of
264 | Nothing ->
265 | error ("Couldn't find cursor: " ++
266 | show cursor)
267 | Just (Cursor cursor) ->
268 | case unsafeCoerce cursor of
269 | c ->
270 | classReceivingProps cls c newprops
271 | return ())
272 | n <- js_React_createClass renderFun didMountFun didUpdateFun shouldUpdateFun willRecPropsFun
273 | return (Component n)
274 |
275 | --------------------------------------------------------------------------------
276 | -- Dumb functional references
277 |
278 | -- | A functional reference.
279 | data Ref state cursor get where
280 | Lens :: (state -> cursor) -> (cursor -> state -> state) -> Lens state cursor
281 | Prism :: (state -> Maybe cursor) -> (cursor -> state -> state) -> Prism state cursor
282 |
283 | -- | A prism, i.e. getting is certain.
284 | type Lens state cursor = Ref state cursor cursor
285 |
286 | -- | A prism, i.e. getting is not certain.
287 | type Prism state cursor = Ref state cursor (Maybe cursor)
288 |
289 | -- | Put in the functional reference.
290 | putR :: Ref state cursor get -> (cursor -> state -> state)
291 | putR (Lens _ put) = put
292 | putR (Prism _ put) = put
293 |
294 | -- | Get from the functional reference.
295 | getR :: Ref state cursor get -> (state -> get)
296 | getR (Lens get _) = get
297 | getR (Prism get _) = get
298 |
299 | -- | Modify using a functional reference.
300 | modifyR :: Ref state cursor get -> (cursor -> cursor) -> state -> state
301 | modifyR r f state =
302 | case r of
303 | Lens get put ->
304 | put (f (get state)) state
305 | Prism get put ->
306 | case get state of
307 | Nothing -> state
308 | Just x -> put (f x) state
309 |
310 | -- | Set the state at the given ref.
311 | setAt :: Ref state cursor get -> cursor -> TVar state -> IO ()
312 | setAt ref update var =
313 | atomically
314 | (modifyTVar var
315 | (putR ref update))
316 |
317 | -- | Read a value from the state.
318 | readAt :: Ref state cursor get -> TVar state -> IO get
319 | readAt ref var =
320 | atomically
321 | (fmap (getR ref)
322 | (readTVar var))
323 |
324 | -- | Modify the state at the given ref.
325 | modifyAt :: Ref state cursor get -> (cursor -> cursor) -> TVar state -> IO ()
326 | modifyAt ref update var =
327 | atomically
328 | (modifyTVar var
329 | (modifyR ref update))
330 |
331 | -- | Set the state.
332 | set :: state -> TVar state -> IO ()
333 | set state var =
334 | atomically
335 | (modifyTVar var
336 | (const state))
337 |
--------------------------------------------------------------------------------
/src/React/Internal.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE GADTs #-}
2 | {-# LANGUAGE ExistentialQuantification #-}
3 | {-# LANGUAGE MultiParamTypeClasses #-}
4 | {-# LANGUAGE StandaloneDeriving #-}
5 | {-# LANGUAGE RankNTypes #-}
6 | {-# LANGUAGE FlexibleContexts #-}
7 | {-# LANGUAGE ScopedTypeVariables #-}
8 | {-# LANGUAGE CPP #-}
9 | {-# LANGUAGE ConstraintKinds #-}
10 | {-# LANGUAGE FlexibleContexts #-}
11 | {-# LANGUAGE TemplateHaskell #-}
12 | {-# LANGUAGE TypeFamilies #-}
13 | {-# LANGUAGE BangPatterns #-}
14 | {-# LANGUAGE OverloadedStrings #-}
15 | {-# LANGUAGE OverloadedLists #-}
16 | {-# LANGUAGE TypeSynonymInstances #-}
17 | {-# LANGUAGE FlexibleInstances #-}
18 | {-# LANGUAGE DeriveGeneric #-}
19 | {-# LANGUAGE GeneralizedNewtypeDeriving #-}
20 |
21 | -- | An Om-inspired API for React.js from Haskell.
22 |
23 | module React.Internal where
24 |
25 | import Control.Applicative
26 | import Control.Concurrent.STM
27 | import Control.Lens hiding (children)
28 | import Control.Monad
29 | import Control.Monad.Reader
30 | import Control.Monad.State.Strict
31 | import Control.Monad.Trans.Reader
32 | import Data.Coerce
33 |
34 | import Data.HashMap.Strict (HashMap)
35 | import qualified Data.HashMap.Strict as M
36 | import Data.Map.Strict (Map)
37 | import qualified Data.Map.Strict as Map
38 | import Data.Monoid
39 | import Data.String (IsString(..))
40 | import Data.Text (Text)
41 | import qualified Data.Text as T
42 | import Data.Vector (Vector)
43 | import qualified Data.Vector as V
44 | import GHC.Generics
45 | import GHCJS.Compat
46 |
47 | #ifdef __GHCJS__
48 | import JavaScript.JQuery (JQuery)
49 | import GHCJS.Types
50 | import GHCJS.Marshal
51 | import GHCJS.DOM
52 | import GHCJS.DOM.Element
53 | import GHCJS.DOM.Event
54 | import GHCJS.Foreign
55 | #endif
56 |
57 | -- | An application with some state transforming over some monad.
58 | data App state m =
59 | App {appState :: TVar state -- ^ State used for rendering.
60 | ,appRun :: forall a. m a -> IO a -- ^ Function for running in the particular monad.
61 | ,appIdSource :: TVar Int -- ^ Source of integers.
62 | ,appCursors :: TVar (HashMap Int Cursor) -- ^ Mapping from integer to state cursors.
63 | }
64 |
65 | -- | State cursor.
66 | data Cursor =
67 | forall s a. Cursor ((a -> s -> s),(s -> Maybe a))
68 |
69 | -- | Convert a traversal to an opaque but unambiguous typed cursor.
70 | traversalToCursor :: Traversal' s a -> Cursor
71 | traversalToCursor t =
72 | Cursor ((\a s -> set t a s),(preview t))
73 |
74 | -- | Convert a cursor pair to a traversal.
75 | cursorToTraversal :: ((a -> s -> s),(s -> Maybe a)) -> Traversal' s a
76 | cursorToTraversal (update,getting) f s =
77 | case getting s of
78 | Just a -> fmap (\a' -> update a' s) (f a)
79 | Nothing -> pure s
80 |
81 | -- | A virtual react element.
82 | data ReactElement_
83 | type ReactElement' = JSRef ReactElement_
84 |
85 | -- | A React component class.
86 | data ReactClass_
87 | type ReactClass' a = JSRef ReactClass_
88 |
89 | -- | Not sure what's in here. Perhaps click coordinates and things like that.
90 | data ReactEvent_
91 | type ReactEvent = JSRef ReactEvent_
92 |
93 | -- Used for children of a react element. Can be just a string, or list
94 | -- of strings, ReactElement, or array of ReactElement, probably other
95 | -- things too.
96 | data ReactNode_
97 | type ReactNode' = JSRef ReactNode_
98 | #ifndef __GHCJS__
99 | instance ToJSRef ReactNode_ where
100 | #endif
101 |
102 | instance IsString ReactNode' where
103 | fromString = (castRef :: JSString -> ReactNode') . fromString
104 |
105 | -- | Create a JS object for the properties.
106 | --
107 | -- * Ensures it creates a \"key\" element for React to use.
108 | -- * Style object is converted to a straight key->string object.
109 | -- * For each of the event handlers it creates a foreign JS wrapper
110 | -- and assigns it in the object.
111 | --
112 | -- Example:
113 | --
114 | -- {\"key\": 123123123,
115 | -- onClick: ,
116 | -- style: {textDecoration: \"underline\"}}
117 | --
118 | toPropsRef :: App state m -> ElemProps -> Maybe Int -> IO (JSRef props)
119 | toPropsRef _ (ElemProps style events other) mcursorId = do
120 | o <- newObj
121 | forM_ (Map.toList other) $ \(k, v) ->
122 | setProp k (toJSString v) o
123 | unless (Map.null style) $ do
124 | style' <- toJSRef_aeson style
125 | setProp ("style" :: JSString) style' o
126 | forM_ (Map.toList events) $ \(name, f) -> do
127 | let name' = T.concat ["on", T.toUpper $ T.take 1 name, T.drop 1 name]
128 | f' <- syncCallback1 AlwaysRetain True f
129 | setProp name' f' o
130 | case mcursorId of
131 | Nothing -> return ()
132 | Just cursorId -> do
133 | lensr <- toJSRef (cursorId :: Int)
134 | setProp ("cursor" :: JSString) lensr o
135 | return o
136 |
137 | -- | First argument is the virtual element, latter is a real DOM
138 | -- element serving as the container.
139 | #ifdef __GHCJS__
140 | foreign import javascript "React.render($1, $2)"
141 | js_React_render :: ReactElement' -> Element -> IO ()
142 | #else
143 | js_React_render :: ReactElement' -> Element -> IO ()
144 | js_React_render = undefined
145 | #endif
146 |
147 | -- | Create a virtual ReactElement which can be rendered efficiently.
148 | #ifdef __GHCJS__
149 | foreign import javascript "$1.props.cursor"
150 | js_React_props_cursor :: JSRef this -> IO Int
151 | foreign import javascript "React.createElement($1, $2, $3)"
152 | js_React_createElement
153 | :: JSString
154 | -> JSRef props
155 | -> ReactNode'
156 | -> IO ReactElement'
157 | foreign import javascript "React.createElement($1, $2)"
158 | js_React_createElementFromClass
159 | :: ReactClass' state
160 | -> JSRef props
161 | -> Int
162 | -> IO ReactElement'
163 | foreign import javascript
164 | "React.createClass({ render: function(){ var x = {r:0}; $1(x); return x.r; }, componentDidMount: function(){ $2(jQuery(this.getDOMNode()),this); }, componentDidUpdate: function(){return $3(this)}, shouldComponentUpdate: function(){return $4(this)}, componentWillReceiveProps: function(news){return $5(this,news)} })"
165 | js_React_createClass
166 | :: JSFun (JSObject ReactElement' -> IO ())
167 | -> JSFun (JSRef JQuery -> JSRef props -> IO ())
168 | -> JSFun (JSRef props -> IO ())
169 | -> JSFun (JSRef props -> IO ())
170 | -> JSFun (JSRef props -> JSRef props -> IO ())
171 | -> IO (ReactClass' state)
172 | foreign import javascript "React.DOM.a( {href:'http://venmo.com'}, 'Venmo!!!')"
173 | dom_a :: IO ReactElement'
174 | #else
175 | js_React_props_cursor :: JSRef this -> IO Int
176 | js_React_props_cursor = undefined
177 | js_React_createElementFromClass
178 | :: ReactClass' state
179 | -> JSRef props
180 | -> Int
181 | -> IO ReactElement'
182 | js_React_createElementFromClass = undefined
183 | dom_a :: IO ReactElement'
184 | dom_a = undefined
185 | js_React_createElement
186 | :: JSString
187 | -> JSRef props
188 | -> ReactNode'
189 | -> IO ReactElement'
190 | js_React_createElement = undefined
191 | js_React_createClass
192 | :: (JSObject ReactElement' -> IO ())
193 | -> (JSRef JQuery -> JSRef props -> IO ())
194 | -> (JSRef props -> IO ())
195 | -> (JSRef props -> IO ())
196 | -> (JSRef props -> JSRef props -> IO ())
197 | -> IO (ReactClass' state)
198 | js_React_createClass = undefined
199 | #endif
200 |
201 | -- Create a sort of sum type of various things that React will accept
202 | -- as a "node".
203 | --
204 | -- Can be just a string, or list of strings, ReactElement, or array of
205 | -- ReactElement, probably other things too.
206 | class ToReactNode state a where
207 | toReactNode :: App state m -> a -> IO ReactNode'
208 | instance ToReactNode state JSString where
209 | toReactNode _ = return . castRef
210 | instance ToReactNode state Text where
211 | toReactNode _ = return . castRef . toJSString
212 | instance ToReactNode state ReactElement' where
213 | toReactNode _ = return . castRef
214 | instance ToReactNode state ReactNode' where
215 | toReactNode _ = return . castRef
216 | instance ToReactNode state a => ToReactNode state [a] where
217 | toReactNode app = fmap castRef . toJSRef <=< mapM (toReactNode app)
218 | instance ToReactNode state a => ToReactNode state (Vector a) where
219 | toReactNode app = fmap castRef . toJSRef . V.toList <=< V.mapM (toReactNode app)
220 |
221 | -- | Separated because different types of properties will have to be
222 | -- handled specially just before converting to JS.
223 | data ElemProps = ElemProps
224 | { epStyle :: Map Text Text
225 | , epEvents :: Map Text (ReactEvent -> IO ())
226 | , epOtherProps :: Map Text Text
227 | -- ^ Cannot be: style, key
228 | }
229 | deriving (Generic,Show)
230 |
231 | instance Show (ReactEvent -> IO ()) where
232 | show _ = " IO ()>"
233 |
234 | -- | Used only in the ‘DSL’ to create a pure tree of elements.
235 | data ReactElement state =
236 | ReactElement {elemName :: Text -- ^ Name.
237 | ,elemProps :: ElemProps -- ^ Properties: style, events, generic.
238 | ,elemChildren :: (Vector (ReactNode state)) -- ^ Children.
239 | }
240 | deriving (Show)
241 |
242 | instance Show (ReactClass' a) where
243 | show _ = ""
244 |
245 | -- | Used in the DSL for constructing an instance of a component class.
246 | data ReactComponent state = forall cursor.
247 | ReactComponent {compName :: ReactClass' cursor
248 | ,compProps :: ElemProps
249 | ,compRef :: Int}
250 |
251 | deriving instance Show (ReactComponent state)
252 |
253 | -- | Also used for the DSL AFAICT, not produced directly, constructed
254 | -- via smart constructors.
255 | data ReactNode state
256 | = RNElement (ReactElement state)
257 | | RNComponent (ReactComponent state)
258 | | RNText Text
259 | deriving (Show)
260 |
261 | -- | I believe this is used in 'toReactElem'.
262 | instance ToReactNode state (ReactNode state) where
263 | toReactNode app re'@RNElement{} = toReactNode app =<< toReactElem app re'
264 | toReactNode app (RNText t) = toReactNode app t
265 | toReactNode app c@RNComponent{} = toReactNode app =<< toReactElem app c
266 |
267 | -- | Convert our DSL 'ReactElement' to a JS element.
268 | toReactElem :: App state m -> ReactNode state -> IO ReactElement'
269 | toReactElem app rn =
270 | case rn of
271 | RNElement (ReactElement name props children) ->
272 | do (join $
273 | js_React_createElement <$>
274 | pure (toJSString name) <*>
275 | toPropsRef app props Nothing <*>
276 | toReactNode app children)
277 | RNComponent (ReactComponent cls props cursorId) ->
278 | do putStrLn "RNComponent" -- FIXME: Removing this line causes a runtime exception. Seriously.
279 | (join $
280 | js_React_createElementFromClass <$>
281 | pure cls <*>
282 | toPropsRef app props (Just cursorId) <*>
283 | pure cursorId)
284 | RNText _ -> error "Unexpected RNText in toReactElem."
285 |
286 | genCursor :: App state m -> Cursor -> IO Int
287 | genCursor app cursor =
288 | atomically
289 | (do i <- readTVar (appIdSource app)
290 | modifyTVar (appIdSource app) (+ 1)
291 | modifyTVar (appCursors app) (M.insert i cursor)
292 | return i)
293 |
294 | #ifdef __GHCJS__
295 |
296 | foreign import javascript "$2[$1]"
297 | getPropBool :: JSString -> ReactEvent -> IO Bool
298 | foreign import javascript "$2[$1]"
299 | getPropInt :: JSString -> ReactEvent -> IO Int
300 | foreign import javascript "$2[$1]"
301 | getPropElement :: JSString -> ReactEvent -> IO Element
302 | foreign import javascript "$2[$1]"
303 | getPropEvent :: JSString -> ReactEvent -> IO Event
304 | foreign import javascript unsafe "$1[\"preventDefault\"]()"
305 | js_preventDefault :: ReactEvent -> IO ()
306 | foreign import javascript unsafe "$1[\"stopPropagation\"]()"
307 | js_stopPropagation :: ReactEvent -> IO ()
308 | foreign import javascript unsafe "$1[\"getModifierState\"]($2)"
309 | js_getModifierState :: ReactEvent -> JSString -> IO Bool
310 |
311 | #else
312 |
313 | getPropBool :: JSString -> ReactEvent -> IO Bool
314 | getPropBool = undefined
315 | getPropInt :: JSString -> ReactEvent -> IO Int
316 | getPropInt = undefined
317 | getPropElement :: JSString -> ReactEvent -> IO Element
318 | getPropElement = undefined
319 | getPropEvent :: JSString -> ReactEvent -> IO Event
320 | getPropEvent = undefined
321 | js_preventDefault :: ReactEvent -> IO ()
322 | js_preventDefault = undefined
323 | js_stopPropagation :: ReactEvent -> IO ()
324 | js_stopPropagation = undefined
325 | js_getModifierState :: ReactEvent -> JSString -> IO Bool
326 | js_getModifierState = undefined
327 |
328 | #endif
329 |
330 | -- FIXME Flesh out fully following: http://facebook.github.io/react/docs/events.html
331 | newtype EventListener event = EventListener Text
332 |
333 | class (Coercible ReactEvent event, Coercible event ReactEvent) => IsReactEvent event
334 | instance IsReactEvent ReactEvent
335 |
336 | -- | React transformer.
337 | newtype ReactT state m a =
338 | ReactT {unReactT :: ReaderT (App state m) (StateT (ReactNode state) m) a}
339 | deriving (Monad,MonadState (ReactNode state),Applicative,Functor,MonadReader (App state m),MonadIO)
340 |
341 | -- | Pure react monad.
342 | type React state = ReactT state Identity
343 |
344 | -- | Modify the element in the state.
345 | --
346 | -- In the case the current node is text, this is a no-op. Given that
347 | -- the API doesn't have a way to `build' inside a Text, that's
348 | -- okay. We could GADT the ReactNode type to remove this case, but not
349 | -- worth it ATM. Also there is now a Component type, but I don't know
350 | -- how to build inside one of those either, so it's also a no-op (for
351 | -- now).
352 | modifyEl :: Monad m => (ReactElement state -> ReactElement state) -> ReactT state m ()
353 | modifyEl f =
354 | modify (\rn ->
355 | case rn of
356 | RNElement e -> RNElement (f e)
357 | text -> text)
358 |
359 | -- | Modify properties of the element.
360 | modifyProps :: MonadState (ReactNode state) m => (ElemProps -> ElemProps) -> m ()
361 | modifyProps f =
362 | modify (\s ->
363 | case s of
364 | RNElement e ->
365 | RNElement (e {elemProps =
366 | f (elemProps e)})
367 | RNComponent c ->
368 | RNComponent
369 | (c {compProps = f (compProps c)})
370 | RNText{} -> s)
371 |
372 | -- | Run the react monad.
373 | runReactT :: Text -> App state m -> ReactT state m a -> m (a,ReactNode state)
374 | runReactT name app m = runStateT (runReaderT (unReactT m) app) init'
375 | where init' =
376 | (RNElement (ReactElement name
377 | (ElemProps mempty mempty mempty)
378 | mempty))
379 |
380 | -- | A component for some state transforming over some monad.
381 | data Component state cursor (m :: * -> *) =
382 | Component (ReactClass' cursor)
383 |
384 | -- | Class used for creating components.
385 | data Class state cursor m =
386 | Class {_classApp :: App state m
387 | -- ^ Application.
388 | ,_classRender :: ReactT state m ()
389 | -- ^ Rendering function.
390 | ,_classDidMount :: (forall props. Traversal' state cursor -> JQuery -> JSRef props -> IO ())
391 | -- ^ Did mount handler.
392 | ,_classDidUpdate :: (forall props. Traversal' state cursor -> JSRef props -> IO ())
393 | -- ^ Did update.
394 | ,_classShouldUpdate :: (forall props. Traversal' state cursor -> JSRef props -> IO Bool)
395 | -- ^ Should update?
396 | ,_classReceivingProps :: (forall props. Traversal' state cursor -> JSRef props -> IO ())
397 | -- ^ Receiving new props.
398 | }
399 |
--------------------------------------------------------------------------------
/examples/es5-shim.min.js:
--------------------------------------------------------------------------------
1 | (function(definition){if(typeof define=="function"){define(definition)}else if(typeof YUI=="function"){YUI.add("es5",definition)}else{definition()}})(function(){if(parseInt("08")!==8){parseInt=function(origParseInt){var hexRegex=/^0[xX]/;return function parseIntES5(str,radix){str=String(str).trim();if(!+radix){radix=hexRegex.test(str)?16:10}return origParseInt(str,radix)}}(parseInt)}function Empty(){}if(!Function.prototype.bind){Function.prototype.bind=function bind(that){var target=this;if(typeof target!="function"){throw new TypeError("Function.prototype.bind called on incompatible "+target)}var args=_Array_slice_.call(arguments,1);var binder=function(){if(this instanceof bound){var result=target.apply(this,args.concat(_Array_slice_.call(arguments)));if(Object(result)===result){return result}return this}else{return target.apply(that,args.concat(_Array_slice_.call(arguments)))}};var boundLength=Math.max(0,target.length-args.length);var boundArgs=[];for(var i=0;i0){if(deleteCount<=0){if(start==this.length){array_push.apply(this,args);return[]}if(start==0){array_unshift.apply(this,args);return[]}}result=_Array_slice_.call(this,start,start+deleteCount);args.push.apply(args,_Array_slice_.call(this,start+deleteCount,this.length));args.unshift.apply(args,_Array_slice_.call(this,0,start));args.unshift(0,this.length);array_splice.apply(this,args);return result}return array_splice.call(this,start,deleteCount)}}}if([].unshift(0)!=1){var array_unshift=Array.prototype.unshift;Array.prototype.unshift=function(){array_unshift.apply(this,arguments);return this.length}}if(!Array.isArray){Array.isArray=function isArray(obj){return _toString(obj)=="[object Array]"}}var boxedString=Object("a"),splitString=boxedString[0]!="a"||!(0 in boxedString);var boxedForEach=true;if(Array.prototype.forEach){Array.prototype.forEach.call("foo",function(item,i,obj){if(typeof obj!=="object")boxedForEach=false})}if(!Array.prototype.forEach||!boxedForEach){Array.prototype.forEach=function forEach(fun){var object=toObject(this),self=splitString&&_toString(this)=="[object String]"?this.split(""):object,thisp=arguments[1],i=-1,length=self.length>>>0;if(_toString(fun)!="[object Function]"){throw new TypeError}while(++i>>0,result=Array(length),thisp=arguments[1];if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}for(var i=0;i>>0,result=[],value,thisp=arguments[1];if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}for(var i=0;i>>0,thisp=arguments[1];if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}for(var i=0;i>>0,thisp=arguments[1];if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}for(var i=0;i>>0;if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}if(!length&&arguments.length==1){throw new TypeError("reduce of empty array with no initial value")}var i=0;var result;if(arguments.length>=2){result=arguments[1]}else{do{if(i in self){result=self[i++];break}if(++i>=length){throw new TypeError("reduce of empty array with no initial value")}}while(true)}for(;i>>0;if(_toString(fun)!="[object Function]"){throw new TypeError(fun+" is not a function")}if(!length&&arguments.length==1){throw new TypeError("reduceRight of empty array with no initial value")}var result,i=length-1;if(arguments.length>=2){result=arguments[1]}else{do{if(i in self){result=self[i--];break}if(--i<0){throw new TypeError("reduceRight of empty array with no initial value")}}while(true)}if(i<0){return result}do{if(i in this){result=fun.call(void 0,result,self[i],i,object)}}while(i--);return result}}if(!Array.prototype.indexOf||[0,1].indexOf(1,2)!=-1){Array.prototype.indexOf=function indexOf(sought){var self=splitString&&_toString(this)=="[object String]"?this.split(""):toObject(this),length=self.length>>>0;if(!length){return-1}var i=0;if(arguments.length>1){i=toInteger(arguments[1])}i=i>=0?i:Math.max(0,length+i);for(;i>>0;if(!length){return-1}var i=length-1;if(arguments.length>1){i=Math.min(i,toInteger(arguments[1]))}i=i>=0?i:length-Math.abs(i);for(;i>=0;i--){if(i in self&&sought===self[i]){return i}}return-1}}if(!Object.keys){var hasDontEnumBug=true,dontEnums=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],dontEnumsLength=dontEnums.length;for(var key in{toString:null}){hasDontEnumBug=false}Object.keys=function keys(object){if(typeof object!="object"&&typeof object!="function"||object===null){throw new TypeError("Object.keys called on a non-object")}var keys=[];for(var name in object){if(owns(object,name)){keys.push(name)}}if(hasDontEnumBug){for(var i=0,ii=dontEnumsLength;i9999?"+":"")+("00000"+Math.abs(year)).slice(0<=year&&year<=9999?-4:-6);length=result.length;while(length--){value=result[length];if(value<10){result[length]="0"+value}}return year+"-"+result.slice(0,2).join("-")+"T"+result.slice(2).join(":")+"."+("000"+this.getUTCMilliseconds()).slice(-3)+"Z"}}var dateToJSONIsSupported=false;try{dateToJSONIsSupported=Date.prototype.toJSON&&new Date(NaN).toJSON()===null&&new Date(negativeDate).toJSON().indexOf(negativeYearString)!==-1&&Date.prototype.toJSON.call({toISOString:function(){return true}})}catch(e){}if(!dateToJSONIsSupported){Date.prototype.toJSON=function toJSON(key){var o=Object(this),tv=toPrimitive(o),toISO;if(typeof tv==="number"&&!isFinite(tv)){return null}toISO=o.toISOString;if(typeof toISO!="function"){throw new TypeError("toISOString property is not callable")}return toISO.call(o)}}if(!Date.parse||"Date.parse is buggy"){Date=function(NativeDate){function Date(Y,M,D,h,m,s,ms){var length=arguments.length;if(this instanceof NativeDate){var date=length==1&&String(Y)===Y?new NativeDate(Date.parse(Y)):length>=7?new NativeDate(Y,M,D,h,m,s,ms):length>=6?new NativeDate(Y,M,D,h,m,s):length>=5?new NativeDate(Y,M,D,h,m):length>=4?new NativeDate(Y,M,D,h):length>=3?new NativeDate(Y,M,D):length>=2?new NativeDate(Y,M):length>=1?new NativeDate(Y):new NativeDate;date.constructor=Date;return date}return NativeDate.apply(this,arguments)}var isoDateExpression=new RegExp("^"+"(\\d{4}|[+-]\\d{6})"+"(?:-(\\d{2})"+"(?:-(\\d{2})"+"(?:"+"T(\\d{2})"+":(\\d{2})"+"(?:"+":(\\d{2})"+"(?:(\\.\\d{1,}))?"+")?"+"("+"Z|"+"(?:"+"([-+])"+"(\\d{2})"+":(\\d{2})"+")"+")?)?)?)?"+"$");var months=[0,31,59,90,120,151,181,212,243,273,304,334,365];function dayFromMonth(year,month){var t=month>1?1:0;return months[month]+Math.floor((year-1969+t)/4)-Math.floor((year-1901+t)/100)+Math.floor((year-1601+t)/400)+365*(year-1970)}function toUTC(t){return Number(new NativeDate(1970,0,1,0,0,0,t))}for(var key in NativeDate){Date[key]=NativeDate[key]}Date.now=NativeDate.now;Date.UTC=NativeDate.UTC;Date.prototype=NativeDate.prototype;Date.prototype.constructor=Date;Date.parse=function parse(string){var match=isoDateExpression.exec(string);if(match){var year=Number(match[1]),month=Number(match[2]||1)-1,day=Number(match[3]||1)-1,hour=Number(match[4]||0),minute=Number(match[5]||0),second=Number(match[6]||0),millisecond=Math.floor(Number(match[7]||0)*1e3),isLocalTime=Boolean(match[4]&&!match[8]),signOffset=match[9]==="-"?1:-1,hourOffset=Number(match[10]||0),minuteOffset=Number(match[11]||0),result;if(hour<(minute>0||second>0||millisecond>0?24:25)&&minute<60&&second<60&&millisecond<1e3&&month>-1&&month<12&&hourOffset<24&&minuteOffset<60&&day>-1&&day=0){c+=data[i];data[i]=Math.floor(c/n);c=c%n*base}}function toString(){var i=size;var s="";while(--i>=0){if(s!==""||i===0||data[i]!==0){var t=String(data[i]);if(s===""){s=t}else{s+="0000000".slice(0,7-t.length)+t}}}return s}function pow(x,n,acc){return n===0?acc:n%2===1?pow(x,n-1,acc*x):pow(x*x,n/2,acc)}function log(x){var n=0;while(x>=4096){n+=12;x/=4096}while(x>=2){n+=1;x/=2}return n}Number.prototype.toFixed=function(fractionDigits){var f,x,s,m,e,z,j,k;f=Number(fractionDigits);f=f!==f?0:Math.floor(f);if(f<0||f>20){throw new RangeError("Number.toFixed called with invalid number of decimals")}x=Number(this);if(x!==x){return"NaN"}if(x<=-1e21||x>=1e21){return String(x)}s="";if(x<0){s="-";x=-x}m="0";if(x>1e-21){e=log(x*pow(2,69,1))-69;z=e<0?x*pow(2,-e,1):x/pow(2,e,1);z*=4503599627370496;e=52-e;if(e>0){multiply(0,z);j=f;while(j>=7){multiply(1e7,0);j-=7}multiply(pow(10,j,1),0);j=e-1;while(j>=23){divide(1<<23);j-=23}divide(1<0){k=m.length;if(k<=f){m=s+"0.0000000000000000000".slice(0,f-k+2)+m}else{m=s+m.slice(0,k-f)+"."+m.slice(k-f)}}else{m=s+m}return m}})()}var string_split=String.prototype.split;if("ab".split(/(?:ab)*/).length!==2||".".split(/(.?)(.?)/).length!==4||"tesst".split(/(s)*/)[1]==="t"||"".split(/.?/).length||".".split(/()()/).length>1){(function(){var compliantExecNpcg=/()??/.exec("")[1]===void 0;String.prototype.split=function(separator,limit){var string=this;if(separator===void 0&&limit===0)return[];if(Object.prototype.toString.call(separator)!=="[object RegExp]"){return string_split.apply(this,arguments)}var output=[],flags=(separator.ignoreCase?"i":"")+(separator.multiline?"m":"")+(separator.extended?"x":"")+(separator.sticky?"y":""),lastLastIndex=0,separator=new RegExp(separator.source,flags+"g"),separator2,match,lastIndex,lastLength;string+="";if(!compliantExecNpcg){separator2=new RegExp("^"+separator.source+"$(?!\\s)",flags)}limit=limit===void 0?-1>>>0:limit>>>0;while(match=separator.exec(string)){lastIndex=match.index+match[0].length;if(lastIndex>lastLastIndex){output.push(string.slice(lastLastIndex,match.index));if(!compliantExecNpcg&&match.length>1){match[0].replace(separator2,function(){for(var i=1;i1&&match.index=limit){break}}if(separator.lastIndex===match.index){separator.lastIndex++}}if(lastLastIndex===string.length){if(lastLength||!separator.test("")){output.push("")}}else{output.push(string.slice(lastLastIndex))}return output.length>limit?output.slice(0,limit):output}})()}else if("0".split(void 0,0).length){String.prototype.split=function(separator,limit){if(separator===void 0&&limit===0)return[];return string_split.apply(this,arguments)}}if("".substr&&"0b".substr(-1)!=="b"){var string_substr=String.prototype.substr;String.prototype.substr=function(start,length){return string_substr.call(this,start<0?(start=this.length+start)<0?0:start:start,length)}}var ws=" \n\f\r \xa0\u1680\u180e\u2000\u2001\u2002\u2003"+"\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028"+"\u2029\ufeff";if(!String.prototype.trim||ws.trim()){ws="["+ws+"]";var trimBeginRegexp=new RegExp("^"+ws+ws+"*"),trimEndRegexp=new RegExp(ws+ws+"*$");String.prototype.trim=function trim(){if(this===void 0||this===null){throw new TypeError("can't convert "+this+" to object")}return String(this).replace(trimBeginRegexp,"").replace(trimEndRegexp,"")}}function toInteger(n){n=+n;if(n!==n){n=0}else if(n!==0&&n!==1/0&&n!==-(1/0)){n=(n>0||-1)*Math.floor(Math.abs(n))}return n}function isPrimitive(input){var type=typeof input;return input===null||type==="undefined"||type==="boolean"||type==="number"||type==="string"}function toPrimitive(input){var val,valueOf,toString;if(isPrimitive(input)){return input}valueOf=input.valueOf;if(typeof valueOf==="function"){val=valueOf.call(input);if(isPrimitive(val)){return val}}toString=input.toString;if(typeof toString==="function"){val=toString.call(input);if(isPrimitive(val)){return val}}throw new TypeError}var toObject=function(o){if(o==null){throw new TypeError("can't convert "+o+" to object")}return Object(o)}});
2 | //# sourceMappingURL=es5-shim.map
--------------------------------------------------------------------------------
/examples/react.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * React v0.12.0
3 | *
4 | * Copyright 2013-2014, Facebook, Inc.
5 | * All rights reserved.
6 | *
7 | * This source code is licensed under the BSD-style license found in the
8 | * LICENSE file in the root directory of this source tree. An additional grant
9 | * of patent rights can be found in the PATENTS file in the same directory.
10 | *
11 | */
12 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var t;"undefined"!=typeof window?t=window:"undefined"!=typeof global?t=global:"undefined"!=typeof self&&(t=self),t.React=e()}}(function(){return function e(t,n,r){function o(i,s){if(!n[i]){if(!t[i]){var u="function"==typeof require&&require;if(!s&&u)return u(i,!0);if(a)return a(i,!0);var c=new Error("Cannot find module '"+i+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[i]={exports:{}};t[i][0].call(l.exports,function(e){var n=t[i][1][e];return o(n?n:e)},l,l.exports,e,t,n,r)}return n[i].exports}for(var a="function"==typeof require&&require,i=0;in;n++)e[n].call(t[n]);e.length=0,t.length=0}},reset:function(){this._callbacks=null,this._contexts=null},destructor:function(){this.reset()}}),r.addPoolingTo(n),t.exports=n},{"./Object.assign":27,"./PooledClass":28,"./invariant":124}],7:[function(e,t){"use strict";function n(e){return"SELECT"===e.nodeName||"INPUT"===e.nodeName&&"file"===e.type}function r(e){var t=M.getPooled(P.change,w,e);E.accumulateTwoPhaseDispatches(t),R.batchedUpdates(o,t)}function o(e){y.enqueueEvents(e),y.processEventQueue()}function a(e,t){_=e,w=t,_.attachEvent("onchange",r)}function i(){_&&(_.detachEvent("onchange",r),_=null,w=null)}function s(e,t,n){return e===x.topChange?n:void 0}function u(e,t,n){e===x.topFocus?(i(),a(t,n)):e===x.topBlur&&i()}function c(e,t){_=e,w=t,T=e.value,N=Object.getOwnPropertyDescriptor(e.constructor.prototype,"value"),Object.defineProperty(_,"value",k),_.attachEvent("onpropertychange",p)}function l(){_&&(delete _.value,_.detachEvent("onpropertychange",p),_=null,w=null,T=null,N=null)}function p(e){if("value"===e.propertyName){var t=e.srcElement.value;t!==T&&(T=t,r(e))}}function d(e,t,n){return e===x.topInput?n:void 0}function f(e,t,n){e===x.topFocus?(l(),c(t,n)):e===x.topBlur&&l()}function h(e){return e!==x.topSelectionChange&&e!==x.topKeyUp&&e!==x.topKeyDown||!_||_.value===T?void 0:(T=_.value,w)}function m(e){return"INPUT"===e.nodeName&&("checkbox"===e.type||"radio"===e.type)}function v(e,t,n){return e===x.topClick?n:void 0}var g=e("./EventConstants"),y=e("./EventPluginHub"),E=e("./EventPropagators"),C=e("./ExecutionEnvironment"),R=e("./ReactUpdates"),M=e("./SyntheticEvent"),b=e("./isEventSupported"),O=e("./isTextInputElement"),D=e("./keyOf"),x=g.topLevelTypes,P={change:{phasedRegistrationNames:{bubbled:D({onChange:null}),captured:D({onChangeCapture:null})},dependencies:[x.topBlur,x.topChange,x.topClick,x.topFocus,x.topInput,x.topKeyDown,x.topKeyUp,x.topSelectionChange]}},_=null,w=null,T=null,N=null,I=!1;C.canUseDOM&&(I=b("change")&&(!("documentMode"in document)||document.documentMode>8));var S=!1;C.canUseDOM&&(S=b("input")&&(!("documentMode"in document)||document.documentMode>9));var k={get:function(){return N.get.call(this)},set:function(e){T=""+e,N.set.call(this,e)}},A={eventTypes:P,extractEvents:function(e,t,r,o){var a,i;if(n(t)?I?a=s:i=u:O(t)?S?a=d:(a=h,i=f):m(t)&&(a=v),a){var c=a(e,t,r);if(c){var l=M.getPooled(P.change,c,o);return E.accumulateTwoPhaseDispatches(l),l}}i&&i(e,t,r)}};t.exports=A},{"./EventConstants":16,"./EventPluginHub":18,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactUpdates":77,"./SyntheticEvent":85,"./isEventSupported":125,"./isTextInputElement":127,"./keyOf":131}],8:[function(e,t){"use strict";var n=0,r={createReactRootIndex:function(){return n++}};t.exports=r},{}],9:[function(e,t){"use strict";function n(e){switch(e){case g.topCompositionStart:return E.compositionStart;case g.topCompositionEnd:return E.compositionEnd;case g.topCompositionUpdate:return E.compositionUpdate}}function r(e,t){return e===g.topKeyDown&&t.keyCode===h}function o(e,t){switch(e){case g.topKeyUp:return-1!==f.indexOf(t.keyCode);case g.topKeyDown:return t.keyCode!==h;case g.topKeyPress:case g.topMouseDown:case g.topBlur:return!0;default:return!1}}function a(e){this.root=e,this.startSelection=c.getSelection(e),this.startValue=this.getText()}var i=e("./EventConstants"),s=e("./EventPropagators"),u=e("./ExecutionEnvironment"),c=e("./ReactInputSelection"),l=e("./SyntheticCompositionEvent"),p=e("./getTextContentAccessor"),d=e("./keyOf"),f=[9,13,27,32],h=229,m=u.canUseDOM&&"CompositionEvent"in window,v=!m||"documentMode"in document&&document.documentMode>8&&document.documentMode<=11,g=i.topLevelTypes,y=null,E={compositionEnd:{phasedRegistrationNames:{bubbled:d({onCompositionEnd:null}),captured:d({onCompositionEndCapture:null})},dependencies:[g.topBlur,g.topCompositionEnd,g.topKeyDown,g.topKeyPress,g.topKeyUp,g.topMouseDown]},compositionStart:{phasedRegistrationNames:{bubbled:d({onCompositionStart:null}),captured:d({onCompositionStartCapture:null})},dependencies:[g.topBlur,g.topCompositionStart,g.topKeyDown,g.topKeyPress,g.topKeyUp,g.topMouseDown]},compositionUpdate:{phasedRegistrationNames:{bubbled:d({onCompositionUpdate:null}),captured:d({onCompositionUpdateCapture:null})},dependencies:[g.topBlur,g.topCompositionUpdate,g.topKeyDown,g.topKeyPress,g.topKeyUp,g.topMouseDown]}};a.prototype.getText=function(){return this.root.value||this.root[p()]},a.prototype.getData=function(){var e=this.getText(),t=this.startSelection.start,n=this.startValue.length-this.startSelection.end;return e.substr(t,e.length-n-t)};var C={eventTypes:E,extractEvents:function(e,t,i,u){var c,p;if(m?c=n(e):y?o(e,u)&&(c=E.compositionEnd):r(e,u)&&(c=E.compositionStart),v&&(y||c!==E.compositionStart?c===E.compositionEnd&&y&&(p=y.getData(),y=null):y=new a(t)),c){var d=l.getPooled(c,i,u);return p&&(d.data=p),s.accumulateTwoPhaseDispatches(d),d}}};t.exports=C},{"./EventConstants":16,"./EventPropagators":21,"./ExecutionEnvironment":22,"./ReactInputSelection":57,"./SyntheticCompositionEvent":83,"./getTextContentAccessor":119,"./keyOf":131}],10:[function(e,t){"use strict";function n(e,t,n){e.insertBefore(t,e.childNodes[n]||null)}var r,o=e("./Danger"),a=e("./ReactMultiChildUpdateTypes"),i=e("./getTextContentAccessor"),s=e("./invariant"),u=i();r="textContent"===u?function(e,t){e.textContent=t}:function(e,t){for(;e.firstChild;)e.removeChild(e.firstChild);if(t){var n=e.ownerDocument||document;e.appendChild(n.createTextNode(t))}};var c={dangerouslyReplaceNodeWithMarkup:o.dangerouslyReplaceNodeWithMarkup,updateTextContent:r,processUpdates:function(e,t){for(var i,u=null,c=null,l=0;i=e[l];l++)if(i.type===a.MOVE_EXISTING||i.type===a.REMOVE_NODE){var p=i.fromIndex,d=i.parentNode.childNodes[p],f=i.parentID;s(d),u=u||{},u[f]=u[f]||[],u[f][p]=d,c=c||[],c.push(d)}var h=o.dangerouslyRenderMarkup(t);if(c)for(var m=0;mt||r.hasOverloadedBooleanValue[e]&&t===!1}var r=e("./DOMProperty"),o=e("./escapeTextForBrowser"),a=e("./memoizeStringOnly"),i=(e("./warning"),a(function(e){return o(e)+'="'})),s={createMarkupForID:function(e){return i(r.ID_ATTRIBUTE_NAME)+o(e)+'"'},createMarkupForProperty:function(e,t){if(r.isStandardName.hasOwnProperty(e)&&r.isStandardName[e]){if(n(e,t))return"";var a=r.getAttributeName[e];return r.hasBooleanValue[e]||r.hasOverloadedBooleanValue[e]&&t===!0?o(a):i(a)+o(t)+'"'}return r.isCustomAttribute(e)?null==t?"":i(e)+o(t)+'"':null},setValueForProperty:function(e,t,o){if(r.isStandardName.hasOwnProperty(t)&&r.isStandardName[t]){var a=r.getMutationMethod[t];if(a)a(e,o);else if(n(t,o))this.deleteValueForProperty(e,t);else if(r.mustUseAttribute[t])e.setAttribute(r.getAttributeName[t],""+o);else{var i=r.getPropertyName[t];r.hasSideEffects[t]&&""+e[i]==""+o||(e[i]=o)}}else r.isCustomAttribute(t)&&(null==o?e.removeAttribute(t):e.setAttribute(t,""+o))},deleteValueForProperty:function(e,t){if(r.isStandardName.hasOwnProperty(t)&&r.isStandardName[t]){var n=r.getMutationMethod[t];if(n)n(e,void 0);else if(r.mustUseAttribute[t])e.removeAttribute(r.getAttributeName[t]);else{var o=r.getPropertyName[t],a=r.getDefaultValueForProperty(e.nodeName,o);r.hasSideEffects[t]&&""+e[o]===a||(e[o]=a)}}else r.isCustomAttribute(t)&&e.removeAttribute(t)}};t.exports=s},{"./DOMProperty":11,"./escapeTextForBrowser":107,"./memoizeStringOnly":133,"./warning":141}],13:[function(e,t){"use strict";function n(e){return e.substring(1,e.indexOf(" "))}var r=e("./ExecutionEnvironment"),o=e("./createNodesFromMarkup"),a=e("./emptyFunction"),i=e("./getMarkupWrap"),s=e("./invariant"),u=/^(<[^ \/>]+)/,c="data-danger-index",l={dangerouslyRenderMarkup:function(e){s(r.canUseDOM);for(var t,l={},p=0;pu;u++){var l=s[u];if(l){var p=l.extractEvents(e,t,r,a);p&&(i=o(i,p))}}return i},enqueueEvents:function(e){e&&(u=o(u,e))},processEventQueue:function(){var e=u;u=null,a(e,c),i(!u)},__purge:function(){s={}},__getListenerBank:function(){return s}};t.exports=p},{"./EventPluginRegistry":19,"./EventPluginUtils":20,"./accumulateInto":95,"./forEachAccumulated":110,"./invariant":124}],19:[function(e,t){"use strict";function n(){if(i)for(var e in s){var t=s[e],n=i.indexOf(e);if(a(n>-1),!u.plugins[n]){a(t.extractEvents),u.plugins[n]=t;var o=t.eventTypes;for(var c in o)a(r(o[c],t,c))}}}function r(e,t,n){a(!u.eventNameDispatchConfigs.hasOwnProperty(n)),u.eventNameDispatchConfigs[n]=e;var r=e.phasedRegistrationNames;if(r){for(var i in r)if(r.hasOwnProperty(i)){var s=r[i];o(s,t,n)}return!0}return e.registrationName?(o(e.registrationName,t,n),!0):!1}function o(e,t,n){a(!u.registrationNameModules[e]),u.registrationNameModules[e]=t,u.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var a=e("./invariant"),i=null,s={},u={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},injectEventPluginOrder:function(e){a(!i),i=Array.prototype.slice.call(e),n()},injectEventPluginsByName:function(e){var t=!1;for(var r in e)if(e.hasOwnProperty(r)){var o=e[r];s.hasOwnProperty(r)&&s[r]===o||(a(!s[r]),s[r]=o,t=!0)}t&&n()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return u.registrationNameModules[t.registrationName]||null;for(var n in t.phasedRegistrationNames)if(t.phasedRegistrationNames.hasOwnProperty(n)){var r=u.registrationNameModules[t.phasedRegistrationNames[n]];if(r)return r}return null},_resetEventPlugins:function(){i=null;for(var e in s)s.hasOwnProperty(e)&&delete s[e];u.plugins.length=0;var t=u.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=u.registrationNameModules;for(var o in r)r.hasOwnProperty(o)&&delete r[o]}};t.exports=u},{"./invariant":124}],20:[function(e,t){"use strict";function n(e){return e===m.topMouseUp||e===m.topTouchEnd||e===m.topTouchCancel}function r(e){return e===m.topMouseMove||e===m.topTouchMove}function o(e){return e===m.topMouseDown||e===m.topTouchStart}function a(e,t){var n=e._dispatchListeners,r=e._dispatchIDs;if(Array.isArray(n))for(var o=0;ol;l++){var d=s[l];i.hasOwnProperty(d)&&i[d]||(d===u.topWheel?c("wheel")?m.ReactEventListener.trapBubbledEvent(u.topWheel,"wheel",o):c("mousewheel")?m.ReactEventListener.trapBubbledEvent(u.topWheel,"mousewheel",o):m.ReactEventListener.trapBubbledEvent(u.topWheel,"DOMMouseScroll",o):d===u.topScroll?c("scroll",!0)?m.ReactEventListener.trapCapturedEvent(u.topScroll,"scroll",o):m.ReactEventListener.trapBubbledEvent(u.topScroll,"scroll",m.ReactEventListener.WINDOW_HANDLE):d===u.topFocus||d===u.topBlur?(c("focus",!0)?(m.ReactEventListener.trapCapturedEvent(u.topFocus,"focus",o),m.ReactEventListener.trapCapturedEvent(u.topBlur,"blur",o)):c("focusin")&&(m.ReactEventListener.trapBubbledEvent(u.topFocus,"focusin",o),m.ReactEventListener.trapBubbledEvent(u.topBlur,"focusout",o)),i[u.topBlur]=!0,i[u.topFocus]=!0):f.hasOwnProperty(d)&&m.ReactEventListener.trapBubbledEvent(d,f[d],o),i[d]=!0)}},trapBubbledEvent:function(e,t,n){return m.ReactEventListener.trapBubbledEvent(e,t,n)},trapCapturedEvent:function(e,t,n){return m.ReactEventListener.trapCapturedEvent(e,t,n)},ensureScrollValueMonitoring:function(){if(!p){var e=s.refreshScrollValues;m.ReactEventListener.monitorScrollValue(e),p=!0}},eventNameDispatchConfigs:o.eventNameDispatchConfigs,registrationNameModules:o.registrationNameModules,putListener:o.putListener,getListener:o.getListener,deleteListener:o.deleteListener,deleteAllListeners:o.deleteAllListeners});t.exports=m},{"./EventConstants":16,"./EventPluginHub":18,"./EventPluginRegistry":19,"./Object.assign":27,"./ReactEventEmitterMixin":54,"./ViewportMetrics":94,"./isEventSupported":125}],31:[function(e,t){"use strict";function n(e,t){this.forEachFunction=e,this.forEachContext=t}function r(e,t,n,r){var o=e;o.forEachFunction.call(o.forEachContext,t,r)}function o(e,t,o){if(null==e)return e;var a=n.getPooled(t,o);p(e,r,a),n.release(a)}function a(e,t,n){this.mapResult=e,this.mapFunction=t,this.mapContext=n}function i(e,t,n,r){var o=e,a=o.mapResult,i=!a.hasOwnProperty(n);if(i){var s=o.mapFunction.call(o.mapContext,t,r);a[n]=s}}function s(e,t,n){if(null==e)return e;var r={},o=a.getPooled(r,t,n);return p(e,i,o),a.release(o),r}function u(){return null}function c(e){return p(e,u,null)}var l=e("./PooledClass"),p=e("./traverseAllChildren"),d=(e("./warning"),l.twoArgumentPooler),f=l.threeArgumentPooler;l.addPoolingTo(n,d),l.addPoolingTo(a,f);var h={forEach:o,map:s,count:c};t.exports=h},{"./PooledClass":28,"./traverseAllChildren":140,"./warning":141}],32:[function(e,t){"use strict";var n=e("./ReactElement"),r=e("./ReactOwner"),o=e("./ReactUpdates"),a=e("./Object.assign"),i=e("./invariant"),s=e("./keyMirror"),u=s({MOUNTED:null,UNMOUNTED:null}),c=!1,l=null,p=null,d={injection:{injectEnvironment:function(e){i(!c),p=e.mountImageIntoNode,l=e.unmountIDFromEnvironment,d.BackendIDOperations=e.BackendIDOperations,c=!0}},LifeCycle:u,BackendIDOperations:null,Mixin:{isMounted:function(){return this._lifeCycleState===u.MOUNTED},setProps:function(e,t){var n=this._pendingElement||this._currentElement;this.replaceProps(a({},n.props,e),t)},replaceProps:function(e,t){i(this.isMounted()),i(0===this._mountDepth),this._pendingElement=n.cloneAndReplaceProps(this._pendingElement||this._currentElement,e),o.enqueueUpdate(this,t)},_setPropsInternal:function(e,t){var r=this._pendingElement||this._currentElement;this._pendingElement=n.cloneAndReplaceProps(r,a({},r.props,e)),o.enqueueUpdate(this,t)},construct:function(e){this.props=e.props,this._owner=e._owner,this._lifeCycleState=u.UNMOUNTED,this._pendingCallbacks=null,this._currentElement=e,this._pendingElement=null},mountComponent:function(e,t,n){i(!this.isMounted());var o=this._currentElement.ref;if(null!=o){var a=this._currentElement._owner;r.addComponentAsRefTo(this,o,a)}this._rootNodeID=e,this._lifeCycleState=u.MOUNTED,this._mountDepth=n},unmountComponent:function(){i(this.isMounted());var e=this._currentElement.ref;null!=e&&r.removeComponentAsRefFrom(this,e,this._owner),l(this._rootNodeID),this._rootNodeID=null,this._lifeCycleState=u.UNMOUNTED},receiveComponent:function(e,t){i(this.isMounted()),this._pendingElement=e,this.performUpdateIfNecessary(t)},performUpdateIfNecessary:function(e){if(null!=this._pendingElement){var t=this._currentElement,n=this._pendingElement;this._currentElement=n,this.props=n.props,this._owner=n._owner,this._pendingElement=null,this.updateComponent(e,t)}},updateComponent:function(e,t){var n=this._currentElement;(n._owner!==t._owner||n.ref!==t.ref)&&(null!=t.ref&&r.removeComponentAsRefFrom(this,t.ref,t._owner),null!=n.ref&&r.addComponentAsRefTo(this,n.ref,n._owner))},mountComponentIntoNode:function(e,t,n){var r=o.ReactReconcileTransaction.getPooled();r.perform(this._mountComponentIntoNode,this,e,t,r,n),o.ReactReconcileTransaction.release(r)},_mountComponentIntoNode:function(e,t,n,r){var o=this.mountComponent(e,n,0);p(o,t,r)},isOwnedBy:function(e){return this._owner===e},getSiblingByRef:function(e){var t=this._owner;return t&&t.refs?t.refs[e]:null}}};t.exports=d},{"./Object.assign":27,"./ReactElement":50,"./ReactOwner":65,"./ReactUpdates":77,"./invariant":124,"./keyMirror":130}],33:[function(e,t){"use strict";var n=e("./ReactDOMIDOperations"),r=e("./ReactMarkupChecksum"),o=e("./ReactMount"),a=e("./ReactPerf"),i=e("./ReactReconcileTransaction"),s=e("./getReactRootElementInContainer"),u=e("./invariant"),c=e("./setInnerHTML"),l=1,p=9,d={ReactReconcileTransaction:i,BackendIDOperations:n,unmountIDFromEnvironment:function(e){o.purgeID(e)},mountImageIntoNode:a.measure("ReactComponentBrowserEnvironment","mountImageIntoNode",function(e,t,n){if(u(t&&(t.nodeType===l||t.nodeType===p)),n){if(r.canReuseMarkup(e,s(t)))return;u(t.nodeType!==p)}u(t.nodeType!==p),c(t,e)})};t.exports=d},{"./ReactDOMIDOperations":41,"./ReactMarkupChecksum":60,"./ReactMount":61,"./ReactPerf":66,"./ReactReconcileTransaction":72,"./getReactRootElementInContainer":118,"./invariant":124,"./setInnerHTML":136}],34:[function(e,t){"use strict";function n(e){var t=e._owner||null;return t&&t.constructor&&t.constructor.displayName?" Check the render method of `"+t.constructor.displayName+"`.":""}function r(e,t){for(var n in t)t.hasOwnProperty(n)&&D("function"==typeof t[n])}function o(e,t){var n=S.hasOwnProperty(t)?S[t]:null;L.hasOwnProperty(t)&&D(n===N.OVERRIDE_BASE),e.hasOwnProperty(t)&&D(n===N.DEFINE_MANY||n===N.DEFINE_MANY_MERGED)}function a(e){var t=e._compositeLifeCycleState;D(e.isMounted()||t===A.MOUNTING),D(null==f.current),D(t!==A.UNMOUNTING)}function i(e,t){if(t){D(!g.isValidFactory(t)),D(!h.isValidElement(t));var n=e.prototype;t.hasOwnProperty(T)&&k.mixins(e,t.mixins);for(var r in t)if(t.hasOwnProperty(r)&&r!==T){var a=t[r];if(o(n,r),k.hasOwnProperty(r))k[r](e,a);else{var i=S.hasOwnProperty(r),s=n.hasOwnProperty(r),u=a&&a.__reactDontBind,p="function"==typeof a,d=p&&!i&&!s&&!u;if(d)n.__reactAutoBindMap||(n.__reactAutoBindMap={}),n.__reactAutoBindMap[r]=a,n[r]=a;else if(s){var f=S[r];D(i&&(f===N.DEFINE_MANY_MERGED||f===N.DEFINE_MANY)),f===N.DEFINE_MANY_MERGED?n[r]=c(n[r],a):f===N.DEFINE_MANY&&(n[r]=l(n[r],a))}else n[r]=a}}}}function s(e,t){if(t)for(var n in t){var r=t[n];if(t.hasOwnProperty(n)){var o=n in k;D(!o);var a=n in e;D(!a),e[n]=r}}}function u(e,t){return D(e&&t&&"object"==typeof e&&"object"==typeof t),_(t,function(t,n){D(void 0===e[n]),e[n]=t}),e}function c(e,t){return function(){var n=e.apply(this,arguments),r=t.apply(this,arguments);return null==n?r:null==r?n:u(n,r)}}function l(e,t){return function(){e.apply(this,arguments),t.apply(this,arguments)}}var p=e("./ReactComponent"),d=e("./ReactContext"),f=e("./ReactCurrentOwner"),h=e("./ReactElement"),m=(e("./ReactElementValidator"),e("./ReactEmptyComponent")),v=e("./ReactErrorUtils"),g=e("./ReactLegacyElement"),y=e("./ReactOwner"),E=e("./ReactPerf"),C=e("./ReactPropTransferer"),R=e("./ReactPropTypeLocations"),M=(e("./ReactPropTypeLocationNames"),e("./ReactUpdates")),b=e("./Object.assign"),O=e("./instantiateReactComponent"),D=e("./invariant"),x=e("./keyMirror"),P=e("./keyOf"),_=(e("./monitorCodeUse"),e("./mapObject")),w=e("./shouldUpdateReactComponent"),T=(e("./warning"),P({mixins:null})),N=x({DEFINE_ONCE:null,DEFINE_MANY:null,OVERRIDE_BASE:null,DEFINE_MANY_MERGED:null}),I=[],S={mixins:N.DEFINE_MANY,statics:N.DEFINE_MANY,propTypes:N.DEFINE_MANY,contextTypes:N.DEFINE_MANY,childContextTypes:N.DEFINE_MANY,getDefaultProps:N.DEFINE_MANY_MERGED,getInitialState:N.DEFINE_MANY_MERGED,getChildContext:N.DEFINE_MANY_MERGED,render:N.DEFINE_ONCE,componentWillMount:N.DEFINE_MANY,componentDidMount:N.DEFINE_MANY,componentWillReceiveProps:N.DEFINE_MANY,shouldComponentUpdate:N.DEFINE_ONCE,componentWillUpdate:N.DEFINE_MANY,componentDidUpdate:N.DEFINE_MANY,componentWillUnmount:N.DEFINE_MANY,updateComponent:N.OVERRIDE_BASE},k={displayName:function(e,t){e.displayName=t},mixins:function(e,t){if(t)for(var n=0;n";return this._createOpenTagMarkupAndPutListeners(t)+this._createContentMarkup(t)+o}),_createOpenTagMarkupAndPutListeners:function(e){var t=this.props,n="<"+this._tag;for(var o in t)if(t.hasOwnProperty(o)){var a=t[o];if(null!=a)if(R.hasOwnProperty(o))r(this._rootNodeID,o,a,e);else{o===b&&(a&&(a=t.style=m({},t.style)),a=i.createMarkupForStyles(a));var s=u.createMarkupForProperty(o,a);s&&(n+=" "+s)}}if(e.renderToStaticMarkup)return n+">";var c=u.createMarkupForID(this._rootNodeID);return n+" "+c+">"},_createContentMarkup:function(e){var t=this.props.dangerouslySetInnerHTML;if(null!=t){if(null!=t.__html)return t.__html}else{var n=M[typeof this.props.children]?this.props.children:null,r=null!=n?null:this.props.children;if(null!=n)return v(n);if(null!=r){var o=this.mountChildren(r,e);return o.join("")}}return""},receiveComponent:function(e,t){(e!==this._currentElement||null==e._owner)&&l.Mixin.receiveComponent.call(this,e,t)},updateComponent:h.measure("ReactDOMComponent","updateComponent",function(e,t){n(this._currentElement.props),l.Mixin.updateComponent.call(this,e,t),this._updateDOMProperties(t.props,e),this._updateDOMChildren(t.props,e)}),_updateDOMProperties:function(e,t){var n,o,a,i=this.props;for(n in e)if(!i.hasOwnProperty(n)&&e.hasOwnProperty(n))if(n===b){var u=e[n];for(o in u)u.hasOwnProperty(o)&&(a=a||{},a[o]="")}else R.hasOwnProperty(n)?E(this._rootNodeID,n):(s.isStandardName[n]||s.isCustomAttribute(n))&&l.BackendIDOperations.deletePropertyByID(this._rootNodeID,n);for(n in i){var c=i[n],p=e[n];if(i.hasOwnProperty(n)&&c!==p)if(n===b)if(c&&(c=i.style=m({},c)),p){for(o in p)!p.hasOwnProperty(o)||c&&c.hasOwnProperty(o)||(a=a||{},a[o]="");for(o in c)c.hasOwnProperty(o)&&p[o]!==c[o]&&(a=a||{},a[o]=c[o])}else a=c;else R.hasOwnProperty(n)?r(this._rootNodeID,n,c,t):(s.isStandardName[n]||s.isCustomAttribute(n))&&l.BackendIDOperations.updatePropertyByID(this._rootNodeID,n,c)}a&&l.BackendIDOperations.updateStylesByID(this._rootNodeID,a)},_updateDOMChildren:function(e,t){var n=this.props,r=M[typeof e.children]?e.children:null,o=M[typeof n.children]?n.children:null,a=e.dangerouslySetInnerHTML&&e.dangerouslySetInnerHTML.__html,i=n.dangerouslySetInnerHTML&&n.dangerouslySetInnerHTML.__html,s=null!=r?null:e.children,u=null!=o?null:n.children,c=null!=r||null!=a,p=null!=o||null!=i;null!=s&&null==u?this.updateChildren(null,t):c&&!p&&this.updateTextContent(""),null!=o?r!==o&&this.updateTextContent(""+o):null!=i?a!==i&&l.BackendIDOperations.updateInnerHTMLByID(this._rootNodeID,i):null!=u&&this.updateChildren(u,t)},unmountComponent:function(){this.unmountChildren(),p.deleteAllListeners(this._rootNodeID),l.Mixin.unmountComponent.call(this)}},m(a.prototype,l.Mixin,a.Mixin,f.Mixin,c),t.exports=a},{"./CSSPropertyOperations":5,"./DOMProperty":11,"./DOMPropertyOperations":12,"./Object.assign":27,"./ReactBrowserComponentMixin":29,"./ReactBrowserEventEmitter":30,"./ReactComponent":32,"./ReactMount":61,"./ReactMultiChild":62,"./ReactPerf":66,"./escapeTextForBrowser":107,"./invariant":124,"./isEventSupported":125,"./keyOf":131,"./monitorCodeUse":134}],40:[function(e,t){"use strict";var n=e("./EventConstants"),r=e("./LocalEventTrapMixin"),o=e("./ReactBrowserComponentMixin"),a=e("./ReactCompositeComponent"),i=e("./ReactElement"),s=e("./ReactDOM"),u=i.createFactory(s.form.type),c=a.createClass({displayName:"ReactDOMForm",mixins:[o,r],render:function(){return u(this.props)},componentDidMount:function(){this.trapBubbledEvent(n.topLevelTypes.topReset,"reset"),this.trapBubbledEvent(n.topLevelTypes.topSubmit,"submit")}});t.exports=c},{"./EventConstants":16,"./LocalEventTrapMixin":25,"./ReactBrowserComponentMixin":29,"./ReactCompositeComponent":34,"./ReactDOM":37,"./ReactElement":50}],41:[function(e,t){"use strict";var n=e("./CSSPropertyOperations"),r=e("./DOMChildrenOperations"),o=e("./DOMPropertyOperations"),a=e("./ReactMount"),i=e("./ReactPerf"),s=e("./invariant"),u=e("./setInnerHTML"),c={dangerouslySetInnerHTML:"`dangerouslySetInnerHTML` must be set using `updateInnerHTMLByID()`.",style:"`style` must be set using `updateStylesByID()`."},l={updatePropertyByID:i.measure("ReactDOMIDOperations","updatePropertyByID",function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),null!=n?o.setValueForProperty(r,t,n):o.deleteValueForProperty(r,t)}),deletePropertyByID:i.measure("ReactDOMIDOperations","deletePropertyByID",function(e,t,n){var r=a.getNode(e);s(!c.hasOwnProperty(t)),o.deleteValueForProperty(r,t,n)}),updateStylesByID:i.measure("ReactDOMIDOperations","updateStylesByID",function(e,t){var r=a.getNode(e);n.setValueForStyles(r,t)}),updateInnerHTMLByID:i.measure("ReactDOMIDOperations","updateInnerHTMLByID",function(e,t){var n=a.getNode(e);u(n,t)}),updateTextContentByID:i.measure("ReactDOMIDOperations","updateTextContentByID",function(e,t){var n=a.getNode(e);r.updateTextContent(n,t)}),dangerouslyReplaceNodeWithMarkupByID:i.measure("ReactDOMIDOperations","dangerouslyReplaceNodeWithMarkupByID",function(e,t){var n=a.getNode(e);r.dangerouslyReplaceNodeWithMarkup(n,t)}),dangerouslyProcessChildrenUpdates:i.measure("ReactDOMIDOperations","dangerouslyProcessChildrenUpdates",function(e,t){for(var n=0;nc;c++){var h=u[c];if(h!==i&&h.form===i.form){var v=l.getID(h);f(v);var g=m[v];f(g),p.asap(n,g)}}}return t}});t.exports=v},{"./AutoFocusMixin":2,"./DOMPropertyOperations":12,"./LinkedValueUtils":24,"./Object.assign":27,"./ReactBrowserComponentMixin":29,"./ReactCompositeComponent":34,"./ReactDOM":37,"./ReactElement":50,"./ReactMount":61,"./ReactUpdates":77,"./invariant":124}],44:[function(e,t){"use strict";var n=e("./ReactBrowserComponentMixin"),r=e("./ReactCompositeComponent"),o=e("./ReactElement"),a=e("./ReactDOM"),i=(e("./warning"),o.createFactory(a.option.type)),s=r.createClass({displayName:"ReactDOMOption",mixins:[n],componentWillMount:function(){},render:function(){return i(this.props,this.props.children)}});t.exports=s},{"./ReactBrowserComponentMixin":29,"./ReactCompositeComponent":34,"./ReactDOM":37,"./ReactElement":50,"./warning":141}],45:[function(e,t){"use strict";function n(){this.isMounted()&&(this.setState({value:this._pendingValue}),this._pendingValue=0)}function r(e,t){if(null!=e[t])if(e.multiple){if(!Array.isArray(e[t]))return new Error("The `"+t+"` prop supplied to