├── .dir-locals.el ├── .ghci ├── .gitignore ├── LICENSE ├── README.md ├── Setup.hs ├── examples ├── Main.hs ├── React │ └── Ace.hs ├── ace-helpers.js ├── ace.js ├── console-polyfill.js ├── es5-sham.min.js ├── es5-shim.min.js ├── index.html ├── react.js └── react.min.js ├── ghcjs-react.cabal └── src ├── GHCJS └── Compat.hs ├── React.hs └── React ├── Builder.hs ├── Component.hs ├── Event.hs ├── Internal.hs ├── Lucid.hs └── Monad.hs /.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 | -------------------------------------------------------------------------------- /.ghci: -------------------------------------------------------------------------------- 1 | :set -isrc 2 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 -------------------------------------------------------------------------------- /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/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example app 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /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 must be a scalar value if `multiple` is false.")}function o(e,t){var n,r,o,a=e.props.multiple,i=null!=t?t:e.state.value,s=e.getDOMNode().options;if(a)for(n={},r=0,o=i.length;o>r;++r)n[""+i[r]]=!0;else n=""+i;for(r=0,o=s.length;o>r;r++){var u=a?n.hasOwnProperty(s[r].value):s[r].value===n;u!==s[r].selected&&(s[r].selected=u)}}var a=e("./AutoFocusMixin"),i=e("./LinkedValueUtils"),s=e("./ReactBrowserComponentMixin"),u=e("./ReactCompositeComponent"),c=e("./ReactElement"),l=e("./ReactDOM"),p=e("./ReactUpdates"),d=e("./Object.assign"),f=c.createFactory(l.select.type),h=u.createClass({displayName:"ReactDOMSelect",mixins:[a,i.Mixin,s],propTypes:{defaultValue:r,value:r},getInitialState:function(){return{value:this.props.defaultValue||(this.props.multiple?[]:"")}},componentWillMount:function(){this._pendingValue=null},componentWillReceiveProps:function(e){!this.props.multiple&&e.multiple?this.setState({value:[this.state.value]}):this.props.multiple&&!e.multiple&&this.setState({value:this.state.value[0]}) 14 | },render:function(){var e=d({},this.props);return e.onChange=this._handleChange,e.value=null,f(e,this.props.children)},componentDidMount:function(){o(this,i.getValue(this))},componentDidUpdate:function(e){var t=i.getValue(this),n=!!e.multiple,r=!!this.props.multiple;(null!=t||n!==r)&&o(this,t)},_handleChange:function(e){var t,r=i.getOnChange(this);r&&(t=r.call(this,e));var o;if(this.props.multiple){o=[];for(var a=e.target.options,s=0,u=a.length;u>s;s++)a[s].selected&&o.push(a[s].value)}else o=e.target.value;return this._pendingValue=o,p.asap(n,this),t}});t.exports=h},{"./AutoFocusMixin":2,"./LinkedValueUtils":24,"./Object.assign":27,"./ReactBrowserComponentMixin":29,"./ReactCompositeComponent":34,"./ReactDOM":37,"./ReactElement":50,"./ReactUpdates":77}],46:[function(e,t){"use strict";function n(e,t,n,r){return e===n&&t===r}function r(e){var t=document.selection,n=t.createRange(),r=n.text.length,o=n.duplicate();o.moveToElementText(e),o.setEndPoint("EndToStart",n);var a=o.text.length,i=a+r;return{start:a,end:i}}function o(e){var t=window.getSelection&&window.getSelection();if(!t||0===t.rangeCount)return null;var r=t.anchorNode,o=t.anchorOffset,a=t.focusNode,i=t.focusOffset,s=t.getRangeAt(0),u=n(t.anchorNode,t.anchorOffset,t.focusNode,t.focusOffset),c=u?0:s.toString().length,l=s.cloneRange();l.selectNodeContents(e),l.setEnd(s.startContainer,s.startOffset);var p=n(l.startContainer,l.startOffset,l.endContainer,l.endOffset),d=p?0:l.toString().length,f=d+c,h=document.createRange();h.setStart(r,o),h.setEnd(a,i);var m=h.collapsed;return{start:m?f:d,end:m?d:f}}function a(e,t){var n,r,o=document.selection.createRange().duplicate();"undefined"==typeof t.end?(n=t.start,r=n):t.start>t.end?(n=t.end,r=t.start):(n=t.start,r=t.end),o.moveToElementText(e),o.moveStart("character",n),o.setEndPoint("EndToStart",o),o.moveEnd("character",r-n),o.select()}function i(e,t){if(window.getSelection){var n=window.getSelection(),r=e[c()].length,o=Math.min(t.start,r),a="undefined"==typeof t.end?o:Math.min(t.end,r);if(!n.extend&&o>a){var i=a;a=o,o=i}var s=u(e,o),l=u(e,a);if(s&&l){var p=document.createRange();p.setStart(s.node,s.offset),n.removeAllRanges(),o>a?(n.addRange(p),n.extend(l.node,l.offset)):(p.setEnd(l.node,l.offset),n.addRange(p))}}}var s=e("./ExecutionEnvironment"),u=e("./getNodeForCharacterOffset"),c=e("./getTextContentAccessor"),l=s.canUseDOM&&document.selection,p={getOffsets:l?r:o,setOffsets:l?a:i};t.exports=p},{"./ExecutionEnvironment":22,"./getNodeForCharacterOffset":117,"./getTextContentAccessor":119}],47:[function(e,t){"use strict";function n(){this.isMounted()&&this.forceUpdate()}var r=e("./AutoFocusMixin"),o=e("./DOMPropertyOperations"),a=e("./LinkedValueUtils"),i=e("./ReactBrowserComponentMixin"),s=e("./ReactCompositeComponent"),u=e("./ReactElement"),c=e("./ReactDOM"),l=e("./ReactUpdates"),p=e("./Object.assign"),d=e("./invariant"),f=(e("./warning"),u.createFactory(c.textarea.type)),h=s.createClass({displayName:"ReactDOMTextarea",mixins:[r,a.Mixin,i],getInitialState:function(){var e=this.props.defaultValue,t=this.props.children;null!=t&&(d(null==e),Array.isArray(t)&&(d(t.length<=1),t=t[0]),e=""+t),null==e&&(e="");var n=a.getValue(this);return{initialValue:""+(null!=n?n:e)}},render:function(){var e=p({},this.props);return d(null==e.dangerouslySetInnerHTML),e.defaultValue=null,e.value=null,e.onChange=this._handleChange,f(e,this.state.initialValue)},componentDidUpdate:function(){var e=a.getValue(this);if(null!=e){var t=this.getDOMNode();o.setValueForProperty(t,"value",""+e)}},_handleChange:function(e){var t,r=a.getOnChange(this);return r&&(t=r.call(this,e)),l.asap(n,this),t}});t.exports=h},{"./AutoFocusMixin":2,"./DOMPropertyOperations":12,"./LinkedValueUtils":24,"./Object.assign":27,"./ReactBrowserComponentMixin":29,"./ReactCompositeComponent":34,"./ReactDOM":37,"./ReactElement":50,"./ReactUpdates":77,"./invariant":124,"./warning":141}],48:[function(e,t){"use strict";function n(){this.reinitializeTransaction()}var r=e("./ReactUpdates"),o=e("./Transaction"),a=e("./Object.assign"),i=e("./emptyFunction"),s={initialize:i,close:function(){p.isBatchingUpdates=!1}},u={initialize:i,close:r.flushBatchedUpdates.bind(r)},c=[u,s];a(n.prototype,o.Mixin,{getTransactionWrappers:function(){return c}});var l=new n,p={isBatchingUpdates:!1,batchedUpdates:function(e,t,n){var r=p.isBatchingUpdates;p.isBatchingUpdates=!0,r?e(t,n):l.perform(e,null,t,n)}};t.exports=p},{"./Object.assign":27,"./ReactUpdates":77,"./Transaction":93,"./emptyFunction":105}],49:[function(e,t){"use strict";function n(){O.EventEmitter.injectReactEventListener(b),O.EventPluginHub.injectEventPluginOrder(s),O.EventPluginHub.injectInstanceHandle(D),O.EventPluginHub.injectMount(x),O.EventPluginHub.injectEventPluginsByName({SimpleEventPlugin:w,EnterLeaveEventPlugin:u,ChangeEventPlugin:o,CompositionEventPlugin:i,MobileSafariClickEventPlugin:p,SelectEventPlugin:P,BeforeInputEventPlugin:r}),O.NativeComponent.injectGenericComponentClass(m),O.NativeComponent.injectComponentClasses({button:v,form:g,img:y,input:E,option:C,select:R,textarea:M,html:N("html"),head:N("head"),body:N("body")}),O.CompositeComponent.injectMixin(d),O.DOMProperty.injectDOMPropertyConfig(l),O.DOMProperty.injectDOMPropertyConfig(T),O.EmptyComponent.injectEmptyComponent("noscript"),O.Updates.injectReconcileTransaction(f.ReactReconcileTransaction),O.Updates.injectBatchingStrategy(h),O.RootIndex.injectCreateReactRootIndex(c.canUseDOM?a.createReactRootIndex:_.createReactRootIndex),O.Component.injectEnvironment(f)}var r=e("./BeforeInputEventPlugin"),o=e("./ChangeEventPlugin"),a=e("./ClientReactRootIndex"),i=e("./CompositionEventPlugin"),s=e("./DefaultEventPluginOrder"),u=e("./EnterLeaveEventPlugin"),c=e("./ExecutionEnvironment"),l=e("./HTMLDOMPropertyConfig"),p=e("./MobileSafariClickEventPlugin"),d=e("./ReactBrowserComponentMixin"),f=e("./ReactComponentBrowserEnvironment"),h=e("./ReactDefaultBatchingStrategy"),m=e("./ReactDOMComponent"),v=e("./ReactDOMButton"),g=e("./ReactDOMForm"),y=e("./ReactDOMImg"),E=e("./ReactDOMInput"),C=e("./ReactDOMOption"),R=e("./ReactDOMSelect"),M=e("./ReactDOMTextarea"),b=e("./ReactEventListener"),O=e("./ReactInjection"),D=e("./ReactInstanceHandles"),x=e("./ReactMount"),P=e("./SelectEventPlugin"),_=e("./ServerReactRootIndex"),w=e("./SimpleEventPlugin"),T=e("./SVGDOMPropertyConfig"),N=e("./createFullPageComponent");t.exports={inject:n}},{"./BeforeInputEventPlugin":3,"./ChangeEventPlugin":7,"./ClientReactRootIndex":8,"./CompositionEventPlugin":9,"./DefaultEventPluginOrder":14,"./EnterLeaveEventPlugin":15,"./ExecutionEnvironment":22,"./HTMLDOMPropertyConfig":23,"./MobileSafariClickEventPlugin":26,"./ReactBrowserComponentMixin":29,"./ReactComponentBrowserEnvironment":33,"./ReactDOMButton":38,"./ReactDOMComponent":39,"./ReactDOMForm":40,"./ReactDOMImg":42,"./ReactDOMInput":43,"./ReactDOMOption":44,"./ReactDOMSelect":45,"./ReactDOMTextarea":47,"./ReactDefaultBatchingStrategy":48,"./ReactEventListener":55,"./ReactInjection":56,"./ReactInstanceHandles":58,"./ReactMount":61,"./SVGDOMPropertyConfig":78,"./SelectEventPlugin":79,"./ServerReactRootIndex":80,"./SimpleEventPlugin":81,"./createFullPageComponent":101}],50:[function(e,t){"use strict";var n=e("./ReactContext"),r=e("./ReactCurrentOwner"),o=(e("./warning"),{key:!0,ref:!0}),a=function(e,t,n,r,o,a){this.type=e,this.key=t,this.ref=n,this._owner=r,this._context=o,this.props=a};a.prototype={_isReactElement:!0},a.createElement=function(e,t,i){var s,u={},c=null,l=null;if(null!=t){l=void 0===t.ref?null:t.ref,c=null==t.key?null:""+t.key;for(s in t)t.hasOwnProperty(s)&&!o.hasOwnProperty(s)&&(u[s]=t[s])}var p=arguments.length-2;if(1===p)u.children=i;else if(p>1){for(var d=Array(p),f=0;p>f;f++)d[f]=arguments[f+2];u.children=d}if(e.defaultProps){var h=e.defaultProps;for(s in h)"undefined"==typeof u[s]&&(u[s]=h[s])}return new a(e,c,l,r.current,n.current,u)},a.createFactory=function(e){var t=a.createElement.bind(null,e);return t.type=e,t},a.cloneAndReplaceProps=function(e,t){var n=new a(e.type,e.key,e.ref,e._owner,e._context,t);return n},a.isValidElement=function(e){var t=!(!e||!e._isReactElement);return t},t.exports=a},{"./ReactContext":35,"./ReactCurrentOwner":36,"./warning":141}],51:[function(e,t){"use strict";function n(){var e=p.current;return e&&e.constructor.displayName||void 0}function r(e,t){e._store.validated||null!=e.key||(e._store.validated=!0,a("react_key_warning",'Each child in an array should have a unique "key" prop.',e,t))}function o(e,t,n){v.test(e)&&a("react_numeric_key_warning","Child objects should have non-numeric keys so ordering is preserved.",t,n)}function a(e,t,r,o){var a=n(),i=o.displayName,s=a||i,u=f[e];if(!u.hasOwnProperty(s)){u[s]=!0,t+=a?" Check the render method of "+a+".":" Check the renderComponent call using <"+i+">.";var c=null;r._owner&&r._owner!==p.current&&(c=r._owner.constructor.displayName,t+=" It was passed a child from "+c+"."),t+=" See http://fb.me/react-warning-keys for more information.",d(e,{component:s,componentOwner:c}),console.warn(t)}}function i(){var e=n()||"";h.hasOwnProperty(e)||(h[e]=!0,d("react_object_map_children"))}function s(e,t){if(Array.isArray(e))for(var n=0;no;o++){t=e.ancestors[o];var i=l.getID(t)||"";m._handleTopLevel(e.topLevelType,t,i,e.nativeEvent)}}function a(e){var t=h(window);e(t)}var i=e("./EventListener"),s=e("./ExecutionEnvironment"),u=e("./PooledClass"),c=e("./ReactInstanceHandles"),l=e("./ReactMount"),p=e("./ReactUpdates"),d=e("./Object.assign"),f=e("./getEventTarget"),h=e("./getUnboundedScrollPosition");d(r.prototype,{destructor:function(){this.topLevelType=null,this.nativeEvent=null,this.ancestors.length=0}}),u.addPoolingTo(r,u.twoArgumentPooler);var m={_enabled:!0,_handleTopLevel:null,WINDOW_HANDLE:s.canUseDOM?window:null,setHandleTopLevel:function(e){m._handleTopLevel=e},setEnabled:function(e){m._enabled=!!e},isEnabled:function(){return m._enabled},trapBubbledEvent:function(e,t,n){var r=n;return r?i.listen(r,t,m.dispatchEvent.bind(null,e)):void 0},trapCapturedEvent:function(e,t,n){var r=n;return r?i.capture(r,t,m.dispatchEvent.bind(null,e)):void 0},monitorScrollValue:function(e){var t=a.bind(null,e);i.listen(window,"scroll",t),i.listen(window,"resize",t)},dispatchEvent:function(e,t){if(m._enabled){var n=r.getPooled(e,t);try{p.batchedUpdates(o,n)}finally{r.release(n)}}}};t.exports=m},{"./EventListener":17,"./ExecutionEnvironment":22,"./Object.assign":27,"./PooledClass":28,"./ReactInstanceHandles":58,"./ReactMount":61,"./ReactUpdates":77,"./getEventTarget":115,"./getUnboundedScrollPosition":120}],56:[function(e,t){"use strict";var n=e("./DOMProperty"),r=e("./EventPluginHub"),o=e("./ReactComponent"),a=e("./ReactCompositeComponent"),i=e("./ReactEmptyComponent"),s=e("./ReactBrowserEventEmitter"),u=e("./ReactNativeComponent"),c=e("./ReactPerf"),l=e("./ReactRootIndex"),p=e("./ReactUpdates"),d={Component:o.injection,CompositeComponent:a.injection,DOMProperty:n.injection,EmptyComponent:i.injection,EventPluginHub:r.injection,EventEmitter:s.injection,NativeComponent:u.injection,Perf:c.injection,RootIndex:l.injection,Updates:p.injection};t.exports=d},{"./DOMProperty":11,"./EventPluginHub":18,"./ReactBrowserEventEmitter":30,"./ReactComponent":32,"./ReactCompositeComponent":34,"./ReactEmptyComponent":52,"./ReactNativeComponent":64,"./ReactPerf":66,"./ReactRootIndex":73,"./ReactUpdates":77}],57:[function(e,t){"use strict";function n(e){return o(document.documentElement,e)}var r=e("./ReactDOMSelection"),o=e("./containsNode"),a=e("./focusNode"),i=e("./getActiveElement"),s={hasSelectionCapabilities:function(e){return e&&("INPUT"===e.nodeName&&"text"===e.type||"TEXTAREA"===e.nodeName||"true"===e.contentEditable)},getSelectionInformation:function(){var e=i();return{focusedElem:e,selectionRange:s.hasSelectionCapabilities(e)?s.getSelection(e):null}},restoreSelection:function(e){var t=i(),r=e.focusedElem,o=e.selectionRange;t!==r&&n(r)&&(s.hasSelectionCapabilities(r)&&s.setSelection(r,o),a(r))},getSelection:function(e){var t;if("selectionStart"in e)t={start:e.selectionStart,end:e.selectionEnd};else if(document.selection&&"INPUT"===e.nodeName){var n=document.selection.createRange();n.parentElement()===e&&(t={start:-n.moveStart("character",-e.value.length),end:-n.moveEnd("character",-e.value.length)})}else t=r.getOffsets(e);return t||{start:0,end:0}},setSelection:function(e,t){var n=t.start,o=t.end;if("undefined"==typeof o&&(o=n),"selectionStart"in e)e.selectionStart=n,e.selectionEnd=Math.min(o,e.value.length);else if(document.selection&&"INPUT"===e.nodeName){var a=e.createTextRange();a.collapse(!0),a.moveStart("character",n),a.moveEnd("character",o-n),a.select()}else r.setOffsets(e,t)}};t.exports=s},{"./ReactDOMSelection":46,"./containsNode":99,"./focusNode":109,"./getActiveElement":111}],58:[function(e,t){"use strict";function n(e){return d+e.toString(36)}function r(e,t){return e.charAt(t)===d||t===e.length}function o(e){return""===e||e.charAt(0)===d&&e.charAt(e.length-1)!==d}function a(e,t){return 0===t.indexOf(e)&&r(t,e.length)}function i(e){return e?e.substr(0,e.lastIndexOf(d)):""}function s(e,t){if(p(o(e)&&o(t)),p(a(e,t)),e===t)return e;for(var n=e.length+f,i=n;i=i;i++)if(r(e,i)&&r(t,i))a=i;else if(e.charAt(i)!==t.charAt(i))break;var s=e.substr(0,a);return p(o(s)),s}function c(e,t,n,r,o,u){e=e||"",t=t||"",p(e!==t);var c=a(t,e);p(c||a(e,t));for(var l=0,d=c?i:s,f=e;;f=d(f,t)){var m;if(o&&f===e||u&&f===t||(m=n(f,c,r)),m===!1||f===t)break;p(l++1){var t=e.indexOf(d,1);return t>-1?e.substr(0,t):e}return null},traverseEnterLeave:function(e,t,n,r,o){var a=u(e,t);a!==e&&c(e,a,n,r,!1,!0),a!==t&&c(a,t,n,o,!0,!1)},traverseTwoPhase:function(e,t,n){e&&(c("",e,t,n,!0,!1),c(e,"",t,n,!1,!0))},traverseAncestors:function(e,t,n){c("",e,t,n,!0,!1)},_getFirstCommonAncestorID:u,_getNextDescendantID:s,isAncestorIDOf:a,SEPARATOR:d};t.exports=m},{"./ReactRootIndex":73,"./invariant":124}],59:[function(e,t){"use strict";function n(e,t){if("function"==typeof t)for(var n in t)if(t.hasOwnProperty(n)){var r=t[n];if("function"==typeof r){var o=r.bind(t);for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);e[n]=o}else e[n]=r}}var r=(e("./ReactCurrentOwner"),e("./invariant")),o=(e("./monitorCodeUse"),e("./warning"),{}),a={},i={};i.wrapCreateFactory=function(e){var t=function(t){return"function"!=typeof t?e(t):t.isReactNonLegacyFactory?e(t.type):t.isReactLegacyFactory?e(t.type):t};return t},i.wrapCreateElement=function(e){var t=function(t){if("function"!=typeof t)return e.apply(this,arguments);var n;return t.isReactNonLegacyFactory?(n=Array.prototype.slice.call(arguments,0),n[0]=t.type,e.apply(this,n)):t.isReactLegacyFactory?(t._isMockFunction&&(t.type._mockedReactClassConstructor=t),n=Array.prototype.slice.call(arguments,0),n[0]=t.type,e.apply(this,n)):t.apply(null,Array.prototype.slice.call(arguments,1))};return t},i.wrapFactory=function(e){r("function"==typeof e);var t=function(){return e.apply(this,arguments)};return n(t,e.type),t.isReactLegacyFactory=o,t.type=e.type,t},i.markNonLegacyFactory=function(e){return e.isReactNonLegacyFactory=a,e},i.isValidFactory=function(e){return"function"==typeof e&&e.isReactLegacyFactory===o},i.isValidClass=function(e){return i.isValidFactory(e)},i._isLegacyCallWarningEnabled=!0,t.exports=i},{"./ReactCurrentOwner":36,"./invariant":124,"./monitorCodeUse":134,"./warning":141}],60:[function(e,t){"use strict";var n=e("./adler32"),r={CHECKSUM_ATTR_NAME:"data-react-checksum",addChecksumToMarkup:function(e){var t=n(e);return e.replace(">"," "+r.CHECKSUM_ATTR_NAME+'="'+t+'">')},canReuseMarkup:function(e,t){var o=t.getAttribute(r.CHECKSUM_ATTR_NAME);o=o&&parseInt(o,10);var a=n(e);return a===o}};t.exports=r},{"./adler32":96}],61:[function(e,t){"use strict";function n(e){var t=E(e);return t&&S.getID(t)}function r(e){var t=o(e);if(t)if(x.hasOwnProperty(t)){var n=x[t];n!==e&&(R(!s(n,t)),x[t]=e)}else x[t]=e;return t}function o(e){return e&&e.getAttribute&&e.getAttribute(D)||""}function a(e,t){var n=o(e);n!==t&&delete x[n],e.setAttribute(D,t),x[t]=e}function i(e){return x.hasOwnProperty(e)&&s(x[e],e)||(x[e]=S.findReactNodeByID(e)),x[e]}function s(e,t){if(e){R(o(e)===t);var n=S.findReactContainerForID(t);if(n&&g(n,e))return!0}return!1}function u(e){delete x[e]}function c(e){var t=x[e];return t&&s(t,e)?void(I=t):!1}function l(e){I=null,m.traverseAncestors(e,c);var t=I;return I=null,t}var p=e("./DOMProperty"),d=e("./ReactBrowserEventEmitter"),f=(e("./ReactCurrentOwner"),e("./ReactElement")),h=e("./ReactLegacyElement"),m=e("./ReactInstanceHandles"),v=e("./ReactPerf"),g=e("./containsNode"),y=e("./deprecated"),E=e("./getReactRootElementInContainer"),C=e("./instantiateReactComponent"),R=e("./invariant"),M=e("./shouldUpdateReactComponent"),b=(e("./warning"),h.wrapCreateElement(f.createElement)),O=m.SEPARATOR,D=p.ID_ATTRIBUTE_NAME,x={},P=1,_=9,w={},T={},N=[],I=null,S={_instancesByReactRootID:w,scrollMonitor:function(e,t){t()},_updateRootComponent:function(e,t,n,r){var o=t.props;return S.scrollMonitor(n,function(){e.replaceProps(o,r)}),e},_registerComponent:function(e,t){R(t&&(t.nodeType===P||t.nodeType===_)),d.ensureScrollValueMonitoring();var n=S.registerContainer(t);return w[n]=e,n},_renderNewRootComponent:v.measure("ReactMount","_renderNewRootComponent",function(e,t,n){var r=C(e,null),o=S._registerComponent(r,t);return r.mountComponentIntoNode(o,t,n),r}),render:function(e,t,r){R(f.isValidElement(e));var o=w[n(t)];if(o){var a=o._currentElement;if(M(a,e))return S._updateRootComponent(o,e,t,r);S.unmountComponentAtNode(t)}var i=E(t),s=i&&S.isRenderedByReact(i),u=s&&!o,c=S._renderNewRootComponent(e,t,u);return r&&r.call(c),c},constructAndRenderComponent:function(e,t,n){var r=b(e,t);return S.render(r,n)},constructAndRenderComponentByID:function(e,t,n){var r=document.getElementById(n);return R(r),S.constructAndRenderComponent(e,t,r)},registerContainer:function(e){var t=n(e);return t&&(t=m.getReactRootIDFromNodeID(t)),t||(t=m.createReactRootID()),T[t]=e,t},unmountComponentAtNode:function(e){var t=n(e),r=w[t];return r?(S.unmountComponentFromNode(r,e),delete w[t],delete T[t],!0):!1},unmountComponentFromNode:function(e,t){for(e.unmountComponent(),t.nodeType===_&&(t=t.documentElement);t.lastChild;)t.removeChild(t.lastChild)},findReactContainerForID:function(e){var t=m.getReactRootIDFromNodeID(e),n=T[t];return n},findReactNodeByID:function(e){var t=S.findReactContainerForID(e);return S.findComponentRoot(t,e)},isRenderedByReact:function(e){if(1!==e.nodeType)return!1;var t=S.getID(e);return t?t.charAt(0)===O:!1},getFirstReactDOM:function(e){for(var t=e;t&&t.parentNode!==t;){if(S.isRenderedByReact(t))return t;t=t.parentNode}return null},findComponentRoot:function(e,t){var n=N,r=0,o=l(t)||e;for(n[0]=o.firstChild,n.length=1;r>",R=i(),M=p(),b={array:r("array"),bool:r("boolean"),func:r("function"),number:r("number"),object:r("object"),string:r("string"),any:o(),arrayOf:a,element:R,instanceOf:s,node:M,objectOf:c,oneOf:u,oneOfType:l,shape:d,component:y("React.PropTypes","component","element",this,R),renderable:y("React.PropTypes","renderable","node",this,M)};t.exports=b},{"./ReactElement":50,"./ReactPropTypeLocationNames":68,"./deprecated":104,"./emptyFunction":105}],71:[function(e,t){"use strict";function n(){this.listenersToPut=[]}var r=e("./PooledClass"),o=e("./ReactBrowserEventEmitter"),a=e("./Object.assign");a(n.prototype,{enqueuePutListener:function(e,t,n){this.listenersToPut.push({rootNodeID:e,propKey:t,propValue:n})},putListeners:function(){for(var e=0;e"+a+""},receiveComponent:function(e){var t=e.props;t!==this.props&&(this.props=t,r.BackendIDOperations.updateTextContentByID(this._rootNodeID,t))}});var u=function(e){return new o(s,null,null,null,null,e)};u.type=s,t.exports=u},{"./DOMPropertyOperations":12,"./Object.assign":27,"./ReactComponent":32,"./ReactElement":50,"./escapeTextForBrowser":107}],77:[function(e,t){"use strict";function n(){h(O.ReactReconcileTransaction&&y)}function r(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=c.getPooled(),this.reconcileTransaction=O.ReactReconcileTransaction.getPooled()}function o(e,t,r){n(),y.batchedUpdates(e,t,r)}function a(e,t){return e._mountDepth-t._mountDepth}function i(e){var t=e.dirtyComponentsLength;h(t===m.length),m.sort(a);for(var n=0;t>n;n++){var r=m[n];if(r.isMounted()){var o=r._pendingCallbacks;if(r._pendingCallbacks=null,r.performUpdateIfNecessary(e.reconcileTransaction),o)for(var i=0;i":">","<":"<",'"':""","'":"'"},a=/[&><"']/g;t.exports=r},{}],108:[function(e,t){"use strict";function n(e,t,n){var r=e,a=!r.hasOwnProperty(n);if(a&&null!=t){var i,s=typeof t;i="string"===s?o(t):"number"===s?o(""+t):t,r[n]=i}}function r(e){if(null==e)return e;var t={};return a(e,n,t),t}{var o=e("./ReactTextComponent"),a=e("./traverseAllChildren");e("./warning")}t.exports=r},{"./ReactTextComponent":76,"./traverseAllChildren":140,"./warning":141}],109:[function(e,t){"use strict";function n(e){try{e.focus()}catch(t){}}t.exports=n},{}],110:[function(e,t){"use strict";var n=function(e,t,n){Array.isArray(e)?e.forEach(t,n):e&&t.call(n,e)};t.exports=n},{}],111:[function(e,t){function n(){try{return document.activeElement||document.body}catch(e){return document.body}}t.exports=n},{}],112:[function(e,t){"use strict";function n(e){var t,n=e.keyCode;return"charCode"in e?(t=e.charCode,0===t&&13===n&&(t=13)):t=n,t>=32||13===t?t:0}t.exports=n},{}],113:[function(e,t){"use strict";function n(e){if(e.key){var t=o[e.key]||e.key;if("Unidentified"!==t)return t}if("keypress"===e.type){var n=r(e);return 13===n?"Enter":String.fromCharCode(n)}return"keydown"===e.type||"keyup"===e.type?a[e.keyCode]||"Unidentified":""}var r=e("./getEventCharCode"),o={Esc:"Escape",Spacebar:" ",Left:"ArrowLeft",Up:"ArrowUp",Right:"ArrowRight",Down:"ArrowDown",Del:"Delete",Win:"OS",Menu:"ContextMenu",Apps:"ContextMenu",Scroll:"ScrollLock",MozPrintableKey:"Unidentified"},a={8:"Backspace",9:"Tab",12:"Clear",13:"Enter",16:"Shift",17:"Control",18:"Alt",19:"Pause",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",45:"Insert",46:"Delete",112:"F1",113:"F2",114:"F3",115:"F4",116:"F5",117:"F6",118:"F7",119:"F8",120:"F9",121:"F10",122:"F11",123:"F12",144:"NumLock",145:"ScrollLock",224:"Meta"};t.exports=n},{"./getEventCharCode":112}],114:[function(e,t){"use strict";function n(e){var t=this,n=t.nativeEvent;if(n.getModifierState)return n.getModifierState(e);var r=o[e];return r?!!n[r]:!1}function r(){return n}var o={Alt:"altKey",Control:"ctrlKey",Meta:"metaKey",Shift:"shiftKey"};t.exports=r},{}],115:[function(e,t){"use strict";function n(e){var t=e.target||e.srcElement||window;return 3===t.nodeType?t.parentNode:t}t.exports=n},{}],116:[function(e,t){function n(e){return o(!!a),p.hasOwnProperty(e)||(e="*"),i.hasOwnProperty(e)||(a.innerHTML="*"===e?"":"<"+e+">",i[e]=!a.firstChild),i[e]?p[e]:null}var r=e("./ExecutionEnvironment"),o=e("./invariant"),a=r.canUseDOM?document.createElement("div"):null,i={circle:!0,defs:!0,ellipse:!0,g:!0,line:!0,linearGradient:!0,path:!0,polygon:!0,polyline:!0,radialGradient:!0,rect:!0,stop:!0,text:!0},s=[1,'"],u=[1,"","
"],c=[3,"","
"],l=[1,"",""],p={"*":[1,"?
","
"],area:[1,"",""],col:[2,"","
"],legend:[1,"
","
"],param:[1,"",""],tr:[2,"","
"],optgroup:s,option:s,caption:u,colgroup:u,tbody:u,tfoot:u,thead:u,td:c,th:c,circle:l,defs:l,ellipse:l,g:l,line:l,linearGradient:l,path:l,polygon:l,polyline:l,radialGradient:l,rect:l,stop:l,text:l};t.exports=n},{"./ExecutionEnvironment":22,"./invariant":124}],117:[function(e,t){"use strict";function n(e){for(;e&&e.firstChild;)e=e.firstChild;return e}function r(e){for(;e;){if(e.nextSibling)return e.nextSibling;e=e.parentNode}}function o(e,t){for(var o=n(e),a=0,i=0;o;){if(3==o.nodeType){if(i=a+o.textContent.length,t>=a&&i>=t)return{node:o,offset:t-a};a=i}o=n(r(o))}}t.exports=o},{}],118:[function(e,t){"use strict";function n(e){return e?e.nodeType===r?e.documentElement:e.firstChild:null}var r=9;t.exports=n},{}],119:[function(e,t){"use strict";function n(){return!o&&r.canUseDOM&&(o="textContent"in document.documentElement?"textContent":"innerText"),o}var r=e("./ExecutionEnvironment"),o=null;t.exports=n},{"./ExecutionEnvironment":22}],120:[function(e,t){"use strict";function n(e){return e===window?{x:window.pageXOffset||document.documentElement.scrollLeft,y:window.pageYOffset||document.documentElement.scrollTop}:{x:e.scrollLeft,y:e.scrollTop}}t.exports=n},{}],121:[function(e,t){function n(e){return e.replace(r,"-$1").toLowerCase()}var r=/([A-Z])/g;t.exports=n},{}],122:[function(e,t){"use strict";function n(e){return r(e).replace(o,"-ms-")}var r=e("./hyphenate"),o=/^ms-/;t.exports=n},{"./hyphenate":121}],123:[function(e,t){"use strict";function n(e,t){var n;return n="string"==typeof e.type?r.createInstanceForTag(e.type,e.props,t):new e.type(e.props),n.construct(e),n}{var r=(e("./warning"),e("./ReactElement"),e("./ReactLegacyElement"),e("./ReactNativeComponent"));e("./ReactEmptyComponent")}t.exports=n},{"./ReactElement":50,"./ReactEmptyComponent":52,"./ReactLegacyElement":59,"./ReactNativeComponent":64,"./warning":141}],124:[function(e,t){"use strict";var n=function(e,t,n,r,o,a,i,s){if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,o,a,i,s],l=0;u=new Error("Invariant Violation: "+t.replace(/%s/g,function(){return c[l++]}))}throw u.framesToPop=1,u}};t.exports=n},{}],125:[function(e,t){"use strict";function n(e,t){if(!o.canUseDOM||t&&!("addEventListener"in document))return!1;var n="on"+e,a=n in document;if(!a){var i=document.createElement("div");i.setAttribute(n,"return;"),a="function"==typeof i[n]}return!a&&r&&"wheel"===e&&(a=document.implementation.hasFeature("Events.wheel","3.0")),a}var r,o=e("./ExecutionEnvironment");o.canUseDOM&&(r=document.implementation&&document.implementation.hasFeature&&document.implementation.hasFeature("","")!==!0),t.exports=n},{"./ExecutionEnvironment":22}],126:[function(e,t){function n(e){return!(!e||!("function"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName))}t.exports=n},{}],127:[function(e,t){"use strict";function n(e){return e&&("INPUT"===e.nodeName&&r[e.type]||"TEXTAREA"===e.nodeName)}var r={color:!0,date:!0,datetime:!0,"datetime-local":!0,email:!0,month:!0,number:!0,password:!0,range:!0,search:!0,tel:!0,text:!0,time:!0,url:!0,week:!0};t.exports=n},{}],128:[function(e,t){function n(e){return r(e)&&3==e.nodeType}var r=e("./isNode");t.exports=n},{"./isNode":126}],129:[function(e,t){"use strict";function n(e){e||(e="");var t,n=arguments.length;if(n>1)for(var r=1;n>r;r++)t=arguments[r],t&&(e=(e?e+" ":"")+t);return e}t.exports=n},{}],130:[function(e,t){"use strict";var n=e("./invariant"),r=function(e){var t,r={};n(e instanceof Object&&!Array.isArray(e));for(t in e)e.hasOwnProperty(t)&&(r[t]=t);return r};t.exports=r},{"./invariant":124}],131:[function(e,t){var n=function(e){var t;for(t in e)if(e.hasOwnProperty(t))return t;return null};t.exports=n},{}],132:[function(e,t){"use strict";function n(e,t,n){if(!e)return null;var o={};for(var a in e)r.call(e,a)&&(o[a]=t.call(n,e[a],a,e));return o}var r=Object.prototype.hasOwnProperty;t.exports=n},{}],133:[function(e,t){"use strict";function n(e){var t={};return function(n){return t.hasOwnProperty(n)?t[n]:t[n]=e.call(this,n)}}t.exports=n},{}],134:[function(e,t){"use strict";function n(e){r(e&&!/[^a-z0-9_]/.test(e))}var r=e("./invariant");t.exports=n},{"./invariant":124}],135:[function(e,t){"use strict";function n(e){return o(r.isValidElement(e)),e}var r=e("./ReactElement"),o=e("./invariant");t.exports=n},{"./ReactElement":50,"./invariant":124}],136:[function(e,t){"use strict";var n=e("./ExecutionEnvironment"),r=/^[ \r\n\t\f]/,o=/<(!--|link|noscript|meta|script|style)[ \r\n\t\f\/>]/,a=function(e,t){e.innerHTML=t};if(n.canUseDOM){var i=document.createElement("div");i.innerHTML=" ",""===i.innerHTML&&(a=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),r.test(t)||"<"===t[0]&&o.test(t)){e.innerHTML=""+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t})}t.exports=a},{"./ExecutionEnvironment":22}],137:[function(e,t){"use strict"; 16 | function n(e,t){if(e===t)return!0;var n;for(n in e)if(e.hasOwnProperty(n)&&(!t.hasOwnProperty(n)||e[n]!==t[n]))return!1;for(n in t)if(t.hasOwnProperty(n)&&!e.hasOwnProperty(n))return!1;return!0}t.exports=n},{}],138:[function(e,t){"use strict";function n(e,t){return e&&t&&e.type===t.type&&e.key===t.key&&e._owner===t._owner?!0:!1}t.exports=n},{}],139:[function(e,t){function n(e){var t=e.length;if(r(!Array.isArray(e)&&("object"==typeof e||"function"==typeof e)),r("number"==typeof t),r(0===t||t-1 in e),e.hasOwnProperty)try{return Array.prototype.slice.call(e)}catch(n){}for(var o=Array(t),a=0;t>a;a++)o[a]=e[a];return o}var r=e("./invariant");t.exports=n},{"./invariant":124}],140:[function(e,t){"use strict";function n(e){return d[e]}function r(e,t){return e&&null!=e.key?a(e.key):t.toString(36)}function o(e){return(""+e).replace(f,n)}function a(e){return"$"+o(e)}function i(e,t,n){return null==e?0:h(e,"",0,t,n)}var s=e("./ReactElement"),u=e("./ReactInstanceHandles"),c=e("./invariant"),l=u.SEPARATOR,p=":",d={"=":"=0",".":"=1",":":"=2"},f=/[=.:]/g,h=function(e,t,n,o,i){var u,d,f=0;if(Array.isArray(e))for(var m=0;m=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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | --------------------------------------------------------------------------------