on master help sign in
Merge branch 'dev' into json

Conflicts: libraries/Text.elm

commit09c91a9b2bebea16c5ff9d57c9fead222f44db76 authorEvan Czaplicki <info@elm-lang.org> dateTue 25 Mar 2014, 14:36 (13 years ago) parenta3d99f89943120
39 files changed+929−748
Elm.cabal+4−4 LICENSE+1−1 changelog.txt → changelog.md+10−7 compiler/Build/Flags.hs+1−1 compiler/Generate/JavaScript.hs+8−5 compiler/Metadata/Prelude.hs+4−4 compiler/Type/Constrain/Expression.hs+4−4 compiler/Type/Type.hs+5 libraries/Automaton.elm.hidden−123 libraries/AutomatonV2.elm.hidden−149 libraries/Basics.elm+27 libraries/Char.elm+2−2 libraries/Debug.elm+2 libraries/Dict.elm+1−2 libraries/Graphics/Input.elm+15−65 libraries/Graphics/Input/Field.elm+170 libraries/Http.elm+2−4 libraries/Keyboard.elm+2−4 libraries/Native/Basics.js+3 libraries/Native/Color.js+9−2 libraries/Native/Debug.js+5−3 libraries/Native/Graphics/Input.js+118−39 libraries/Native/Regex.js+1−1 libraries/Native/Show.js−3 libraries/Native/Signal/Http.js+52−49 libraries/Native/String.js+3−2 libraries/Native/Text.js+58−42 libraries/Native/Utils.js+6−8 libraries/Prelude.elm−8 libraries/Signal.elm+1−1 libraries/String.elm+34−11 libraries/Text.elm+156−44 libraries/elm_dependencies.json+6−3 runtime/HotSwap.js+2−2 runtime/Init.js+1−1 runtime/Render/Element.js+89−41 server/LICENSE+1−1 server/Server.hs+112−99 server/elm-server.cabal+14−13
Elm.cabal+4−4
@@ -1,5 +1,5 @@
11Name: Elm
2−Version: 0.11
2+Version: 0.12
33Synopsis: The Elm language module.
44Description: Elm aims to make client-side web-development more pleasant.
55 It is a statically/strongly typed, functional reactive
@@ -14,13 +14,13 @@ License-file: LICENSE
1414
1515Author: Evan Czaplicki
1616Maintainer: info@elm-lang.org
17−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
17+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
1818
1919Category: Compiler, Language
2020
2121Build-type: Custom
2222
23−Extra-source-files: changelog.txt
23+Extra-source-files: changelog.md
2424Data-dir: data
2525Data-files: elm-runtime.js interfaces.data docs.json
2626Cabal-version: >=1.9
@@ -251,7 +251,7 @@ Test-Suite test-elm
251251 Elm,
252252 test-framework,
253253 test-framework-hunit,
254− test-framework-quickcheck2,
254+ test-framework-quickcheck2 >= 0.3,
255255 HUnit,
256256 pretty,
257257 QuickCheck >= 2 && < 3,
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2012-2013, Evan Czaplicki
1+Copyright (c) 2012-2014, Evan Czaplicki
22
33All rights reserved.
44
changelog.txt → changelog.mdrenamed+10−7
@@ -1,25 +1,28 @@
11
2−## next
2+## 0.12
33
4−Breaking Changes:
4+#### Breaking Changes:
55
6− * Overhaul Graphics.Input library
7− * Overhaul Regex library
6+ * Overhaul Graphics.Input library (inspired by Spiros Eliopoulos and Jeff Smitts)
7+ * Overhaul Text library to accomodate new Graphics.Input.Field
8+ library and make the API more consistent overall
9+ * Overhaul Regex library (inspired by Attila Gazso)
810 * Change syntax for "import open List" to "import List (..)"
911 * Improved JSON format for types generated by elm-doc
1012 * Remove problematic Mouse.isClicked signal
1113 * Revise the semantics of keepWhen and dropWhen to only update when
1214 the filtered signal changes (thanks Max New and Janis Voigtländer)
1315
14−Improvements:
16+#### Improvements:
1517
18+ * Add Graphics.Input.Field for customizable text fields
1619 * Add Trampoline library (thanks to @maxsnew and @timthelion)
1720 * Add Debug library (inspired by @timthelion)
1821 * Drastically improved performance on markdown parsing (thanks to @Dandandan)
1922 * Add Date.fromTime function
2023 * Use pointer-events to detect hovers on layered elements (thanks to @Xashili)
21− * Fix serious bugs in Bitwise library
22− * Fix bug in exporting Maybe values through ports
24+ * Fix bugs in Bitwise library
25+ * Fix bug when exporting Maybe values through ports
2326
2427## 0.11
2528
compiler/Build/Flags.hs+1−1
@@ -41,4 +41,4 @@ flags = Flags
4141 } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
4242 &= helpArg [explicit, name "help", name "h"]
4343 &= versionArg [explicit, name "version", name "v", summary (show Version.elmVersion)]
44− &= summary ("The Elm Compiler " ++ show Version.elmVersion ++ ", (c) Evan Czaplicki 2011-2013")
44+ &= summary ("The Elm Compiler " ++ show Version.elmVersion ++ ", (c) Evan Czaplicki 2011-2014")
compiler/Generate/JavaScript.hs+8−5
@@ -55,7 +55,7 @@ literal lit =
5555expression :: Expr -> State Int (Expression ())
5656expression (A region expr) =
5757 case expr of
58− Var (V.Raw x) -> return $ ref x
58+ Var (V.Raw x) -> return $ obj x
5959 Literal lit -> return $ literal lit
6060
6161 Range lo hi ->
@@ -87,10 +87,9 @@ expression (A region expr) =
8787 do fields' <- forM fields $ \(f,e) -> do
8888 (,) f <$> expression e
8989 let fieldMap = List.foldl' combine Map.empty fields'
90− return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
90+ return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
9191 where
9292 combine r (k,v) = Map.insertWith (++) k [v] r
93− prop = PropId () . var
9493 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
9594 Map.toList . Map.filter (not . null) $ Map.map tail fs
9695 visible fs = map (first prop) . Map.toList $ Map.map head fs
@@ -243,7 +242,11 @@ match region mtch =
243242 isLiteral p = case p of
244243 Case.Clause (Right _) _ _ -> True
245244 _ -> False
246− access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
245+
246+ access name
247+ | any isLiteral clauses = obj name
248+ | otherwise = dotSep (split name ++ ["ctor"])
249+
247250 format isChars e
248251 | or isChars = InfixExpr () OpAdd e (string "")
249252 | otherwise = e
@@ -307,7 +310,7 @@ generate unsafeModule =
307310 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
308311 where
309312 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
310− entry x = (PropId () (var x), ref x)
313+ entry x = (prop x, ref x)
311314
312315 assign path expr =
313316 case path of
compiler/Metadata/Prelude.hs+4−4
@@ -20,12 +20,12 @@ add noPrelude (Module name exs ims decls) = Module name exs (customIms ++ ims) d
2020 Just _ -> []
2121
2222prelude :: [(String, ImportMethod)]
23−prelude = string : text ++ map (\n -> (n, Hiding [])) modules
23+prelude = string ++ text ++ map (\n -> (n, Hiding [])) modules
2424 where
2525 text = map ((,) "Text") [ As "Text", Hiding ["link", "color", "height"] ]
26− string = ("String", As "String")
27− modules = [ "Basics", "Signal", "List", "Maybe", "Time", "Prelude"
28− , "Graphics.Element", "Color", "Graphics.Collage", "Native.Ports" ]
26+ string = map ((,) "String") [ As "String", Importing ["show"] ]
27+ modules = [ "Basics", "Signal", "List", "Maybe", "Time", "Color"
28+ , "Graphics.Element", "Graphics.Collage", "Native.Ports" ]
2929
3030interfaces :: Bool -> IO Interfaces
3131interfaces noPrelude =
compiler/Type/Constrain/Expression.hs+4−4
@@ -36,10 +36,10 @@ constrain env (A region expr) tipe =
3636 | otherwise -> return (name <? tipe)
3737
3838 Range lo hi ->
39− exists $ \x -> do
40− clo <- constrain env lo x
41− chi <- constrain env hi x
42− return $ and [clo, chi, list x === tipe]
39+ existsNumber $ \n -> do
40+ clo <- constrain env lo n
41+ chi <- constrain env hi n
42+ return $ and [clo, chi, list n === tipe]
4343
4444 ExplicitList exprs ->
4545 exists $ \x -> do
compiler/Type/Type.hs+5
@@ -141,6 +141,11 @@ exists f = do
141141 v <- liftIO $ var Flexible
142142 ex [v] <$> f (VarN v)
143143
144+existsNumber :: Error e => (Type -> ErrorT e IO TypeConstraint) -> ErrorT e IO TypeConstraint
145+existsNumber f = do
146+ v <- liftIO $ var (Is Number)
147+ ex [v] <$> f (VarN v)
148+
144149
145150instance Show a => Show (UF.Point a) where
146151 show point = unsafePerformIO $ fmap show (UF.descriptor point)
libraries/Automaton.elm.hiddendeleted−123
@@ -1,123 +0,0 @@
1−module Automaton ( pure, state, hiddenState, run, step
2− , (<<<), (>>>), combine, count, average
3− ) where
4−
5−{-| This library is a way to package up dynamic behavior. It makes it easier to
6−dynamically create dynamic components. See the [original release
7−notes](http://elm-lang.org/blog/announce/0.5.0.elm) on this library to get a feel for how
8−it can be used.
9−
10−# Create
11−@docs pure, state, hiddenState
12−
13−# Evaluate
14−@docs run, step
15−
16−# Combine
17−@docs (>>>), (<<<), combine
18−
19−# Common Automatons
20−@docs count, average
21−-}
22−
23−import open Basics
24−import Signal (lift,foldp,Signal)
25−import open List
26−import Maybe (Just, Nothing)
27−
28−data Automaton a b = Step (a -> (Automaton a b, b))
29−
30−{-| Run an automaton on a given signal. The automaton steps forward whenever the
31−input signal updates.
32−-}
33−run : Automaton a b -> b -> Signal a -> Signal b
34−run auto base inputs =
35− let step a (Step f, _) = f a
36− in lift (\(x,y) -> y) (foldp step (auto,base) inputs)
37−
38−{-| Step an automaton forward once with a given input. -}
39−step : a -> Automaton a b -> (Automaton a b, b)
40−step a (Step f) = f a
41−
42−{-| Compose two automatons, chaining them together. -}
43−(>>>) : Automaton a b -> Automaton b c -> Automaton a c
44−f >>> g =
45− Step (\a -> let (f', b) = step a f
46− (g', c) = step b g
47− in (f' >>> g', c))
48−
49−{-| Compose two automatons, chaining them together. -}
50−(<<<) : Automaton b c -> Automaton a b -> Automaton a c
51−g <<< f = f >>> g
52−
53−{-| Combine a list of automatons into a single automaton that produces a
54−list.
55−-}
56−combine : [Automaton a b] -> Automaton a [b]
57−combine autos =
58− Step (\a -> let (autos', bs) = unzip (map (step a) autos)
59− in (combine autos', bs))
60−
61−{-| Create an automaton with no memory. It just applies the given function to
62−every input.
63−-}
64−pure : (a -> b) -> Automaton a b
65−pure f = Step (\x -> (pure f, f x))
66−
67−
68−{-| Create an automaton with state. Requires an initial state and a step
69−function to step the state forward. For example, an automaton that counted
70−how many steps it has taken would look like this:
71−
72− count = Automaton a Int
73− count = state 0 (\\_ c -> c+1)
74−
75−It is a stateful automaton. The initial state is zero, and the step function
76−increments the state on every step.
77−-}
78−state : b -> (a -> b -> b) -> Automaton a b
79−state s f = Step (\x -> let s' = f x s
80− in (state s' f, s'))
81−
82−{-| Create an automaton with hidden state. Requires an initial state and a
83−step function to step the state forward and produce an output.
84−-}
85−hiddenState : s -> (a -> s -> (s,b)) -> Automaton a b
86−hiddenState s f = Step (\x -> let (s',out) = f x s
87− in (hiddenState s' f, out))
88−
89−{-| Count the number of steps taken. -}
90−count : Automaton a Int
91−count = state 0 (\_ c -> c + 1)
92−
93−type Queue t = ([t],[t])
94−empty = ([],[])
95−enqueue x (en,de) = (x::en, de)
96−dequeue q = case q of
97− ([],[]) -> Nothing
98− (en,[]) -> dequeue ([], reverse en)
99− (en,hd::tl) -> Just (hd, (en,tl))
100−
101−{-| Computes the running average of the last `n` inputs. -}
102−average : Int -> Automaton Float Float
103−average k =
104− let step n (ns,len,sum) =
105− if len == k then stepFull n (ns,len,sum)
106− else ((enqueue n ns, len+1, sum+n), (sum+n) / (toFloat len+1))
107− stepFull n (ns,len,sum) =
108− case dequeue ns of
109− Nothing -> ((ns,len,sum), 0)
110− Just (m,ns') -> let sum' = sum + n - m
111− in ((enqueue n ns', len, sum'), sum' / toFloat len)
112− in hiddenState (empty,0,0) step
113−
114−
115−{-- TODO(evancz): See the following papers for ideas on how to make this
116−library faster and better:
117−
118−- Functional Reactive Programming, Continued
119−- Causal commutative arrows and their optimization
120−
121−Speeding things up is a really low priority. Language features and
122−libraries with nice APIs and are way more important!
123−--}
libraries/AutomatonV2.elm.hiddendeleted−149
@@ -1,149 +0,0 @@
1−module AutomatonV2 where
2−{-| This library is a way to package up dynamic behavior. It makes it easier to
3−dynamically create dynamic components. See the [original release
4−notes](/blog/announce/version-0.5.0.elm) on this library to get a feel for how
5−it can be used.
6−-}
7−
8−import open Basics
9−import Signal (lift,foldp,Signal)
10−import open List
11−import Maybe (Just, Nothing)
12−
13−data Automaton input output = Pure (input -> output)
14− | Stateful state (input -> state -> (output,state))
15−
16−-- The basics
17−
18−-- AFRP name: arr
19−pure: (i -> o) -> Automaton i o
20−pure = Pure
21−
22−-- AFRP name: >>>
23−andThen: Automaton i inter -> Automaton inter o -> Automaton i o
24−andThen first second =
25− case first of
26− Pure f -> case second of -- f is the function from first
27− Pure s -> Pure (s . f) -- s is the function from second
28− Stateful b s -> Stateful b (\i -> s (f i)) -- b is the base state from second
29− Stateful fb f -> case second of -- fb is the base state from first
30− Pure s -> Stateful fb (\i st -> -- sb is the base state from second
31− let (inter, st') = f i st -- i is the input
32− in (s inter, st')) -- st is the input state
33− Stateful sb s -> Stateful (fb, sb) (\i (fst, sst) -> -- inter is the intermediate value
34− let (inter, fst') = f i fst -- st' is the new state
35− (o, sst') = s inter sst -- fst and sst are the state of first and second
36− in (o, (fst', sst'))) -- ect...
37−
38−-- AFRP name: first
39−extendDown: Automaton i o -> Automaton (i,extra) (o,extra)
40−extendDown auto = case auto of
41− Pure fun -> Pure (\(i,extra) -> (fun i, extra))
42− Stateful base fun -> Stateful base (\(i,extra) s -> (fun i s, extra))
43−
44−-- AFRP name: loop
45−loop: s -> Automaton (i,s) (o,s) -> Automaton i o
46−loop base auto = case auto of
47− Pure fun -> Stateful base (curry fun)
48− Stateful base2 fun -> -- fun: (i, s) -> s2 -> ((o, s), s2)
49− let newFun = (\i (s,s2) ->
50− let ((o, s'), s2') = fun (i, s) s2
51− in (o, (s', s2'))) -- newFun: i -> (s, s2) -> (o, (s, s2))
52− in Stateful (base, base2) newFun
53−
54−-- Run an automaton on a given signal
55−run: Automaton i o -> o -> Signal i -> Signal o
56−run auto baseOut input = case auto of
57− Pure fun -> lift fun input
58− Stateful base fun -> lift fst
59− (foldp (\i (o, s) -> fun i s)
60− (baseOut, base) input)
61−
62−
63−-- Other frequently used functions/operators
64−
65−-- Create an automaton with state. Requires an initial state and a step
66−-- function to step the state forward. For example, an automaton that counted
67−-- how many steps it has taken would look like this:
68−--
69−-- count = Automaton a Int
70−-- count = state 0 (\\_ c -> c+1)
71−--
72−-- It is a stateful automaton. The initial state is zero, and the step function
73−-- increments the state on every step.
74−state : s -> (i -> s -> s) -> Automaton i s
75−state base fun = loop base (pure (\(i,s) ->
76− let s' = fun i s
77− in (s',s')))
78−
79−-- Create an automaton with hidden state. Requires an initial state and a
80−-- step function to step the state forward and produce an output.
81−hiddenState : s -> (i -> s -> (s,o)) -> Automaton i o
82−hiddenState base fun = loop base (pure (\(i,s) ->
83− let (o,s') = fun i s
84− in (s',o)))
85−
86−-- AFRP name: second
87−extendUp: Automaton i o -> Automaton (extra,i) (extra,o)
88−extendUp auto =
89− let swap (a, b) = (b, a)
90− in pure swap `andThen` extendDown auto `andThen` pure swap
91−
92−-- (parallel composition)
93−pair: Automaton i1 o1 -> Automaton i2 o2 -> Automaton (i1,i2) (o1,o2)
94−pair f g = extendDown f `andThen` extendUp g
95−
96−branch : Automaton i o1 -> Automaton i o2 -> Automaton i (o1,o2)
97−branch f g =
98− let double = pure (\i -> (i,i))
99− in double `andThen` pair f g
100−
101−combi: Automaton i o -> Automaton i [o] -> Automaton i [o]
102−combi a1 a2 = (a1 `branch` a2) `andThen` pure (uncurry (::))
103−
104−-- Combine a list of automatons into a single automaton that produces a list.
105−combine : [Automaton i o] -> Automaton i [o]
106−combine autos =
107− let l = length autos
108− in if l == 0
109− then pure (\_ -> [])
110− else foldr combi (last autos `andThen` pure (\a -> [a])) (take (l-1) autos)
111−
112−
113−-- Examples of automata
114−
115−-- Count the number of steps taken.
116−count : Automaton a Int
117−count = state 0 (\_ c -> c + 1)
118−
119−type Queue t = ([t],[t])
120−empty = ([],[])
121−enqueue x (en,de) = (x::en, de)
122−dequeue q = case q of
123− ([],[]) -> Nothing
124− (en,[]) -> dequeue ([], reverse en)
125− (en,hd::tl) -> Just (hd, (en,tl))
126−
127−-- Computes the running average of the last `n` inputs.
128−average : Int -> Automaton Float Float
129−average k =
130− let step n (ns,len,sum) =
131− if len == k then stepFull n (ns,len,sum)
132− else ((enqueue n ns, len+1, sum+n), (sum+n) / (toFloat len+1))
133− stepFull n (ns,len,sum) =
134− case dequeue ns of
135− Nothing -> ((ns,len,sum), 0)
136− Just (m,ns') -> let sum' = sum + n - m
137− in ((enqueue n ns', len, sum'), sum' / toFloat len)
138− in hiddenState (empty,0,0) step
139−
140−
141−{-- TODO(evancz): See the following papers for ideas on how to make this
142−library faster and better:
143−
144−- Functional Reactive Programming, Continued -- took some inspirations from this paper (Apanatshka)
145−- Causal commutative arrows and their optimization
146−
147−Speeding things up is a really low priority. Language features and
148−libraries with nice APIs and are way more important!
149−--}
libraries/Basics.elm+27
@@ -39,6 +39,9 @@ which happen to be radians.
3939# Polar Coordinates
4040@docs toPolar, fromPolar
4141
42+# Floating Point Checks
43+@docs isNaN, isInfinite
44+
4245# Tuples
4346@docs fst, snd
4447
@@ -271,6 +274,30 @@ ceiling = Native.Basics.ceiling
271274toFloat : Int -> Float
272275toFloat = Native.Basics.toFloat
273276
277+{- | Determines whether a float is an undefined or unrepresentable number.
278+NaN stands for *not a number* and it is [a standardized part of floating point
279+numbers](http://en.wikipedia.org/wiki/NaN).
280+
281+ isNaN (0/0) == True
282+ isNaN (sqrt -1) == True
283+ isNaN (1/0) == False -- infinity is a number
284+ isNaN 1 == False
285+-}
286+isNaN : Float -> Bool
287+isNaN = Native.Basics.isNaN
288+
289+{- | Determines whether a float is positive or negative infinity.
290+
291+ isInfinite (0/0) == False
292+ isInfinite (sqrt -1) == False
293+ isInfinite (1/0) == True
294+ isInfinite 1 == False
295+
296+Notice that NaN is not infinite! For float `n` to be finite implies that
297+`not (isInfinite n || isNaN n)` evaluates to `True`.
298+-}
299+isInfinite : Float -> Bool
300+isInfinite = Native.Basics.isInfinite
274301
275302-- Function Helpers
276303
libraries/Char.elm+2−2
@@ -52,8 +52,8 @@ toLocaleLower = Native.Char.toLocaleLower
5252
5353type KeyCode = Int
5454
55−{-| Convert to unicode. Used with the [`Keyboard`](/docs/Keyboard.elm)
56−library, which expects the input to be uppercase. -}
55+{-| Convert to unicode. Used with the `Keyboard` library, which expects the
56+input to be uppercase. -}
5757toCode : Char -> KeyCode
5858toCode = Native.Char.toCode
5959
libraries/Debug.elm+2
@@ -1,6 +1,8 @@
11module Debug where
22{-| This library is for investigating bugs or performance problems. It should
33*not* be used in production code.
4+
5+@docs log
46-}
57
68import Native.Debug
libraries/Dict.elm+1−2
@@ -35,7 +35,6 @@ import Basics (..)
3535import Maybe (..)
3636import Native.Error
3737import List
38−import String
3938import Native.Utils
4039
4140-- BBlack and NBlack should only be used during the deletion
@@ -197,7 +196,7 @@ lessBlackTree t = case t of
197196
198197reportRemBug : String -> NColor -> String -> String -> a
199198reportRemBug msg c lgot rgot =
200− Native.Error.raise . String.concat <| [
199+ Native.Error.raise <| List.concat [
201200 "Internal red-black tree invariant violated, expected ",
202201 msg,
203202 "and got",
libraries/Graphics/Input.elm+15−65
@@ -19,14 +19,15 @@ examples in this library, so just read on to get a better idea of how it works!
1919@docs Input, input
2020
2121# Basic Input Elements
22−Text fields come later.
22+
23+To learn about text fields, see the
24+[`Graphics.Input.Field`](Graphics-Input-Field) library.
25+
2326@docs button, customButton, checkbox, dropDown
2427
2528# Clicks and Hovers
2629@docs clickable, hoverable
2730
28−# Text Fields
29−@docs field, password, email, noContent, FieldContent, Selection, Direction
3031-}
3132
3233import Signal (Signal)
@@ -45,14 +46,17 @@ type Input a = { signal : Signal a, handle : Handle a }
4546
4647data Handle a = Handle
4748
48−{-| Create a new `Input`. You just give the initial value for the
49−input&rsquo;s `signal`.
49+{-| This creates a new `Input`. You provide a single argument that will serve
50+as the initial value of the input&rsquo;s `signal`. For example:
51+
52+ numbers : Input Int
53+ numbers = input 42
5054
51−Note for Advanced Users: creating an `Input` is an inherently imperative
52−action, so this is one of very few impure functions in Elm. That means
53−`(input ())` and `(input ())` are actually two different inputs with different
54−signals and handles. By design, Elm&rsquo;s impure functions can only be useful
55−as you build your signal graph at startup, so Elm is still pure at runtime.
55+The initial value of `numbers.signal` is 42, and you will be able
56+to pipe updates to the input using `numbers.handle`.
57+
58+Note: This is an inherently impure function. Specifically, `(input ())` and
59+`(input ())` are actually two different inputs with different signals and handles.
5660-}
5761input : a -> Input a
5862input = Native.Graphics.Input.input
@@ -114,7 +118,7 @@ favorite British sport:
114118
115119 data Sport = Football | Cricket | Snooker
116120
117− sport : Input Sport
121+ sport : Input (Maybe Sport)
118122 sport = input Nothing
119123
120124 sportDropDown : Element
@@ -172,57 +176,3 @@ distinguished with IDs or more complex data structures.
172176-}
173177clickable : Handle a -> a -> Element -> Element
174178clickable = Native.Graphics.Input.clickable
175−
176−{-| Represents the current content of a text field. For example:
177−
178− FieldContent "She sells sea shells" (Selection 0 3 Backward)
179−
180−This means the user highlighted the substring `"She"` backwards.
181−-}
182−type FieldContent = { string:String, selection:Selection }
183−
184−{-| The selection within a text field. `start` is never greater than `end`:
185−
186− Selection 0 0 Forward -- cursor precedes all characters
187−
188− Selection 5 9 Backward -- highlighting characters starting after
189− -- the 5th and ending after the 9th
190−-}
191−type Selection = { start:Int, end:Int, direction:Direction }
192−
193−{-| The direction of selection.-}
194−data Direction = Forward | Backward
195−
196−{-| A field with no content:
197−
198− FieldContent "" (Selection 0 0 Forward)
199−-}
200−noContent : FieldContent
201−noContent = FieldContent "" (Selection 0 0 Forward)
202−
203−{-| Create a text field. The following example creates a time-varying element
204−called `nameField`. As the user types their name, the field will be updated
205−to match what they have entered.
206−
207− name : Input FieldContent
208− name = input noContent
209−
210− nameField : Signal Element
211− nameField = field name.handle (\content -> content) "Name" <~ nameContent
212−-}
213−field : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
214−field = Native.Graphics.Input.field
215−
216−{-| Same as `field` but the UI element blocks out each characters. -}
217−password : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
218−password = Native.Graphics.Input.password
219−
220−{-| Same as `field` but it adds an annotation that this field is for email
221−addresses. This is helpful for auto-complete and for mobile users who may
222−get a custom keyboard with an `@` and `.com` button.
223−-}
224−email : Handle a -> (FieldContent -> a) -> String -> FieldContent -> Element
225−email = Native.Graphics.Input.email
226−
227−-- area : Handle a -> (FieldContent -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> FieldContent -> Element
228−-- area = Native.Graphics.Input.area
libraries/Graphics/Input/Field.elmnew file+170
@@ -0,0 +1,170 @@
1+module Graphics.Input.Field where
2+{-| This library provides an API for creating and updating text fields.
3+Text fields use exactly the same approach as [`Graphics.Input`](Graphics-Input)
4+for modelling user input, allowing you to keep track of new events and update
5+text fields programmatically.
6+
7+# Create Fields
8+@docs field, password, email
9+
10+# Field Content
11+@docs Content, Selection, Direction, noContent
12+
13+# Field Style
14+@docs Style, Outline, noOutline, Highlight, noHighlight, Dimensions, uniformly
15+-}
16+
17+import Color (Color)
18+import Color
19+import Graphics.Element (Element)
20+import Graphics.Input (Input, Handle)
21+import Native.Graphics.Input
22+import Text
23+
24+{-| Create uniform dimensions:
25+
26+ uniformly 4 == { left=4, right=4, top=4, bottom=4 }
27+
28+The following example creates an outline where the left, right, top, and bottom
29+edges all have width 1:
30+
31+ Outline grey (uniformly 1) 4
32+-}
33+uniformly : Int -> Dimensions
34+uniformly n = Dimensions n n n n
35+
36+{-| For setting dimensions of a fields padding or border. The left, right, top,
37+and bottom may all have different sizes. The following example creates
38+dimensions such that the left and right are twice as wide as the top and bottom:
39+
40+ myDimensions : Int -> Dimensions
41+ myDimensions n = { left = 2 * n, right = 2 * n, top = n, bottom = n }
42+-}
43+type Dimensions = { left:Int, right:Int, top:Int, bottom:Int }
44+
45+{-| A field can have a outline around it. This lets you set its color, width,
46+and radius. The radius allows you to round the corners of your field. Set the
47+width to zero to make it invisible. Here is an example outline that is grey
48+and thin with slightly rounded corners:
49+
50+ { color = grey, width = uniformly 1, radius = 4 }
51+-}
52+type Outline = { color:Color, width:Dimensions, radius:Int }
53+
54+{-| An outline with zero width, so you cannot see it. -}
55+noOutline : Outline
56+noOutline = Outline Color.grey (uniformly 0) 0
57+
58+{-| When a field has focus, it has a blue highlight around it by default. The
59+`Highlight` lets you set the `color` and `width` of this highlight. Set the
60+`width` to zero to turn the highlight off. Here is an example highlight that
61+is blue and thin:
62+
63+ { color = blue, width = 1 }
64+-}
65+type Highlight = { color:Color, width:Int }
66+
67+{-| An highlight with zero width, so you cannot see it. -}
68+noHighlight : Highlight
69+noHighlight = Highlight Color.blue 0
70+
71+{-| Describe the style of a text box. `style` describes the style of the text
72+itself using [`Text.Style`](/Text#Style). `outline` describes the glowing blue
73+outline that shows up when the field has focus. `outline` describes the line
74+surrounding the text field, and `padding` adds whitespace between the `outline`
75+and the text.
76+
77+The width and height of the text box *includes* the `padding` and `outline`.
78+Say we have a text box that is 40 pixels tall. It has a uniform outline of
79+1 pixel and a uniform padding of 5 pixels. Both of these must be subtracted
80+from the total height to determine how much room there is for text. The
81+`padding` and `outline` appear on the top and bottom, so there will be 28
82+vertical pixels remaining for the text (40 - 1 - 5 - 5 - 1).
83+-}
84+type Style =
85+ { padding : Dimensions
86+ , outline : Outline
87+ , highlight : Highlight
88+ , style : Text.Style
89+ }
90+
91+{-| The default style for a text field. The outline is `Color.grey` with width
92+1 and radius 2. The highlight is `Color.blue` with width 1, and the default
93+text color is black.
94+-}
95+defaultStyle : Style
96+defaultStyle =
97+ { padding = uniformly 4
98+ , outline = Outline Color.grey (uniformly 1) 2
99+ , highlight = Highlight Color.blue 1
100+ , style = Text.defaultStyle
101+ }
102+
103+{-| Represents the current content of a text field. For example:
104+
105+ content = Content "She sells sea shells" (Selection 0 3 Backward)
106+
107+This means the user highlighted the substring `"She"` backwards. The value of
108+`content.string` is `"She sells sea shells"`.
109+-}
110+type Content = { string:String, selection:Selection }
111+
112+{-| The selection within a text field. `start` is never greater than `end`:
113+
114+ Selection 0 0 Forward -- cursor precedes all characters
115+
116+ Selection 5 9 Backward -- highlighting characters starting after
117+ -- the 5th and ending after the 9th
118+-}
119+type Selection = { start:Int, end:Int, direction:Direction }
120+
121+{-| The direction of selection. When the user highlights a selection in a text
122+field, they must do it in a particular direction. This determines which end of
123+the selection moves when they change the selection by pressing Shift-Left or
124+Shift-Right.
125+-}
126+data Direction = Forward | Backward
127+
128+{-| A field with no content:
129+
130+ Content "" (Selection 0 0 Forward)
131+-}
132+noContent : Content
133+noContent = Content "" (Selection 0 0 Forward)
134+
135+{-| Create a text field. The following example creates a time-varying element
136+called `nameField`. As the user types their name, the field will be updated
137+to match what they have entered.
138+
139+ name : Input Content
140+ name = input noContent
141+
142+ nameField : Signal Element
143+ nameField = field defaultStyle name.handle id "Name" <~ name.signal
144+
145+When we use the `field` function, we first give it a visual style. This is
146+the first argument so that it is easier to define your own custom field
147+(`myField = field myStyle`). The next two arguments are a `Handle` and a
148+handler function that processes or augments events before sending them along
149+to the associated `Input`. In the example above we use the `id` function to
150+pass events along unchanged to the `name` `Input`. We then provide the
151+place-holder message to use when no input has been provided yet. Finally,
152+we give the current `Content` of the field. This argument is last because
153+it is most likely to change frequently, making function composition easier.
154+-}
155+field : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
156+field = Native.Graphics.Input.field
157+
158+{-| Same as `field` but the UI element blocks out each characters. -}
159+password : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
160+password = Native.Graphics.Input.password
161+
162+{-| Same as `field` but it adds an annotation that this field is for email
163+addresses. This is helpful for auto-complete and for mobile users who may
164+get a custom keyboard with an `@` and `.com` button.
165+-}
166+email : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
167+email = Native.Graphics.Input.email
168+
169+-- area : Handle a -> (Content -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> Content -> Element
170+-- area = Native.Graphics.Input.area
libraries/Http.elm+2−4
@@ -1,8 +1,6 @@
11module Http where
2−
3−{-| A library for asynchronous HTTP requests. See the
4−[WebSocket](http://elm-lang.org/docs/WebSocket.elm) library if
5−you have very strict latency requirements.
2+{-| A library for asynchronous HTTP requests. See the `WebSocket`
3+library if you have very strict latency requirements.
64
75# Sending Requests
86@docs send, sendGet
libraries/Keyboard.elm+2−4
@@ -1,5 +1,4 @@
11module Keyboard where
2−
32{-| Library for working with keyboard input.
43
54# Representing Keys
@@ -20,9 +19,8 @@ import Signal (Signal)
2019import Native.Keyboard
2120
2221{-| Type alias to make it clearer what integers are supposed to represent
23−in this library. Use [`Char.toCode`](docs/Char.elm#toCode) and
24−[`Char.fromCode`](/docs/Char.elm#fromCode) to convert key codes to characters.
25−Use the uppercase character with `toCode`.
22+in this library. Use `Char.toCode` and `Char.fromCode` to convert key codes
23+to characters. Use the uppercase character with `toCode`.
2624-}
2725type KeyCode = Int
2826
libraries/Native/Basics.js+3
@@ -19,6 +19,7 @@ Elm.Native.Basics.make = function(elm) {
1919 return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n; }
2020 function xor(a,b) { return a !== b; }
2121 function not(b) { return !b; }
22+ function isInfinite(n) { return n === Infinity || n === -Infinity }
2223
2324 function truncate(n) { return n|0; }
2425
@@ -53,6 +54,8 @@ Elm.Native.Basics.make = function(elm) {
5354 floor:Math.floor,
5455 round:Math.round,
5556 toFloat:function(x) { return x; },
57+ isNaN:isNaN,
58+ isInfinite:isInfinite
5659 };
5760
5861 return elm.Native.Basics.values = basics;
libraries/Native/Color.js+9−2
@@ -6,6 +6,12 @@ Elm.Native.Color.make = function(elm) {
66
77 var Utils = Elm.Native.Utils.make(elm);
88
9+ function toCss(c) {
10+ return (c._3 === 1)
11+ ? ('rgb(' + c._0 + ', ' + c._1 + ', ' + c._2 + ')')
12+ : ('rgba(' + c._0 + ', ' + c._1 + ', ' + c._2 + ', ' + c._3 + ')');
13+ }
14+
915 function complement(rgb) {
1016 var hsv = toHSV(rgb);
1117 hsv.hue = (hsv.hue + 180) % 360;
@@ -63,7 +69,8 @@ Elm.Native.Color.make = function(elm) {
6369 return elm.Native.Color.values = {
6470 hsva:F4(hsva),
6571 hsv:F3(hsv),
66− complement:complement
72+ complement:complement,
73+ toCss:toCss
6774 };
6875
69−};
76+};
libraries/Native/Debug.js+5−3
@@ -7,10 +7,12 @@ Elm.Native.Debug.make = function(elm) {
77 var show = Elm.Native.Show.make(elm).show;
88
99 function log(tag,value) {
10− if (process && process.stdout) {
11− process.stdout.write(tag + ': ' + show(value));
10+ var msg = tag + ': ' + show(value);
11+ var process = process || {};
12+ if (process.stdout) {
13+ process.stdout.write(msg);
1214 } else {
13− console.log(tag + ': ' + show(value));
15+ console.log(msg);
1416 }
1517 return value;
1618 }
libraries/Native/Graphics/Input.js+118−39
@@ -8,6 +8,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
88 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
99 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
1010
11+ var toCss = Elm.Native.Color.make(elm).toCss;
12+ var Text = Elm.Native.Text.make(elm);
1113 var Signal = Elm.Signal.make(elm);
1214 var newElement = Elm.Graphics.Element.make(elm).newElement;
1315 var JS = Elm.Native.JavaScript.make(elm);
@@ -78,7 +80,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
7880
7981 function updateButton(node, oldModel, newModel) {
8082 node.elm_signal = newModel.signal;
81− node.elm_value = nemModel.value;
83+ node.elm_value = newModel.value;
8284 var txt = newModel.text;
8385 if (oldModel.text !== txt) node.innerHTML = txt;
8486 }
@@ -215,72 +217,149 @@ Elm.Native.Graphics.Input.make = function(elm) {
215217 }
216218 }
217219
220+ function updateIfNeeded(css, attribute, latestAttribute) {
221+ if (css[attribute] !== latestAttribute) {
222+ css[attribute] = latestAttribute;
223+ }
224+ }
225+ function cssDimensions(dimensions) {
226+ return dimensions.top + 'px ' +
227+ dimensions.right + 'px ' +
228+ dimensions.bottom + 'px ' +
229+ dimensions.left + 'px';
230+ }
231+ function updateFieldStyle(css, style) {
232+ updateIfNeeded(css, 'padding', cssDimensions(style.padding));
233+
234+ var outline = style.outline;
235+ updateIfNeeded(css, 'border-width', cssDimensions(outline.width));
236+ updateIfNeeded(css, 'border-color', toCss(outline.color));
237+ updateIfNeeded(css, 'border-radius', outline.radius + 'px');
238+
239+ var highlight = style.highlight;
240+ if (highlight.width === 0) {
241+ css.outline = 'none';
242+ } else {
243+ updateIfNeeded(css, 'outline-width', highlight.width + 'px');
244+ updateIfNeeded(css, 'outline-color', toCss(highlight.color));
245+ }
246+
247+ var textStyle = style.style;
248+ updateIfNeeded(css, 'color', toCss(textStyle.color));
249+ if (textStyle.typeface.ctor !== '[]') {
250+ updateIfNeeded(css, 'font-family', Text.toTypefaces(textStyle.typeface));
251+ }
252+ if (textStyle.height.ctor !== "Nothing") {
253+ updateIfNeeded(css, 'font-size', textStyle.height._0 + 'px');
254+ }
255+ updateIfNeeded(css, 'font-weight', textStyle.bold ? 'bold' : 'normal');
256+ updateIfNeeded(css, 'font-style', textStyle.italic ? 'italic' : 'normal');
257+ if (textStyle.line.ctor !== 'Nothing') {
258+ updateIfNeeded(css, 'text-decoration', Text.toLine(textStyle.line._0));
259+ }
260+ }
261+
218262 function renderField(model) {
219263 var field = newNode('input');
264+ updateFieldStyle(field.style, model.style);
265+ field.style.borderStyle = 'solid';
220266 field.style.pointerEvents = 'auto';
221− field.elm_signal = model.signal;
222− field.elm_handler = model.handler;
223267
224268 field.type = model.type;
225269 field.placeholder = JS.fromString(model.placeHolder);
226270 field.value = JS.fromString(model.content.string);
227− var selection = model.content.selection;
228− var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
229− setRange(field, selection.start, selection.end, direction);
230− field.style.border = 'none';
231271
232− function update() {
233− var direction = field.selectionDirection === 'backward' ? 'Backward' : 'Forward';
272+ field.elm_signal = model.signal;
273+ field.elm_handler = model.handler;
274+ field.elm_old_value = field.value;
275+
276+ function inputUpdate(event) {
277+ var curr = field.elm_old_value;
278+ var next = field.value;
279+ if (curr === next) {
280+ return;
281+ }
282+
283+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
284+ var start = field.selectionStart;
285+ var end = field.selectionEnd;
286+ field.value = field.elm_old_value;
287+
234288 elm.notify(field.elm_signal.id, field.elm_handler({
235289 _:{},
236− string:JS.toString(field.value),
290+ string: JS.toString(next),
237291 selection: {
238− start:field.selectionStart,
239− end:field.selectionEnd,
240− direction: { ctor:direction }
292+ _:{},
293+ start: start,
294+ end: end,
295+ direction: { ctor: direction }
241296 },
242297 }));
243298 }
244− function mousedown() {
245− update();
299+
300+ function mouseUpdate(event) {
301+ var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
302+ elm.notify(field.elm_signal.id, field.elm_handler({
303+ _:{},
304+ string: field.value,
305+ selection: {
306+ _:{},
307+ start: field.selectionStart,
308+ end: field.selectionEnd,
309+ direction: { ctor: direction }
310+ },
311+ }));
312+ }
313+ function mousedown(event) {
314+ mouseUpdate(event);
246315 elm.node.addEventListener('mouseup', mouseup);
247316 }
248− function mouseup() {
249− update();
317+ function mouseup(event) {
318+ mouseUpdate(event);
250319 elm.node.removeEventListener('mouseup', mouseup)
251320 }
252− field.addEventListener('keyup', update);
321+ field.addEventListener('input', inputUpdate);
253322 field.addEventListener('mousedown', mousedown);
323+ field.addEventListener('focus', function() {
324+ field.elm_hasFocus = true;
325+ });
326+ field.addEventListener('blur', function() {
327+ field.elm_hasFocus = false;
328+ });
254329
255330 return field;
256331 }
257332
258− function updateField(node, oldModel, newModel) {
259− node.elmHandler = newModel.handler;
260− var newStr = JS.fromString(newModel.content.string);
261− if (node.value !== newStr) node.value = newStr;
262−
263− var selection = newModel.content.selection;
264− var start = selection.start;
265− var end = selection.end;
266− var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
267− if (end < start) {
268− start = end;
269− end = selection.start;
333+ function updateField(field, oldModel, newModel) {
334+ if (oldModel.style !== newModel.style) {
335+ updateFieldStyle(field.style, newModel.style);
270336 }
271−
272− if (node.selectionStart !== start
273− || node.selectionEnd !== end
274− || node.selectionDirection !== direction) {
275− setRange(node, start, end, direction);
337+ field.elm_signal = newModel.signal;
338+ field.elm_handler = newModel.handler;
339+
340+ field.type = newModel.type;
341+ field.placeholder = JS.fromString(newModel.placeHolder);
342+ var value = JS.fromString(newModel.content.string);
343+ field.value = value;
344+ field.elm_old_value = value;
345+ if (field.elm_hasFocus) {
346+ var selection = newModel.content.selection;
347+ var direction = selection.direction.ctor === 'Forward' ? 'forward' : 'backward';
348+ setRange(field, selection.start, selection.end, direction);
276349 }
277350 }
278351
279352 function mkField(type) {
280− function field(signal, handler, placeHolder, content) {
353+ function field(style, signal, handler, placeHolder, content) {
354+ var padding = style.padding;
355+ var outline = style.outline.width;
356+ var adjustWidth = padding.left + padding.right + outline.left + outline.right;
357+ var adjustHeight = padding.top + padding.bottom + outline.top + outline.bottom;
281358 return A3(newElement, 200, 30, {
282359 ctor: 'Custom',
283− type: type + 'Input',
360+ type: type + 'Field',
361+ adjustWidth: adjustWidth,
362+ adjustHeight: adjustHeight,
284363 render: renderField,
285364 update: updateField,
286365 model: {
@@ -288,14 +367,14 @@ Elm.Native.Graphics.Input.make = function(elm) {
288367 handler:handler,
289368 placeHolder:placeHolder,
290369 content:content,
370+ style:style,
291371 type:type
292372 }
293373 });
294374 }
295− return F4(field);
375+ return F5(field);
296376 }
297377
298−
299378 function hoverable(signal, handler, elem) {
300379 function onHover(bool) {
301380 elm.notify(signal.id, handler(bool));
libraries/Native/Regex.js+1−1
@@ -20,7 +20,7 @@ Elm.Native.Regex.make = function(elm) {
2020 }
2121
2222 function contains(re, string) {
23− return re.test(JS.fromString(string));
23+ return JS.fromString(string).match(re) !== null;
2424 }
2525
2626 function find(n, re, str) {
libraries/Native/Show.js−3
@@ -6,10 +6,7 @@ Elm.Native.Show.make = function(elm) {
66
77 var NList = Elm.Native.List.make(elm);
88 var List = Elm.List.make(elm);
9− var Maybe = Elm.Maybe.make(elm);
10− var JS = Elm.JavaScript.make(elm);
119 var Dict = Elm.Dict.make(elm);
12− var Json = Elm.Json.make(elm);
1310 var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;
1411
1512 var toString = function(v) {
libraries/Native/Signal/Http.js+52−49
@@ -1,61 +1,64 @@
11Elm.Native.Http = {};
22Elm.Native.Http.make = function(elm) {
33
4− elm.Native = elm.Native || {};
5− elm.Native.Http = elm.Native.Http || {};
6− if (elm.Native.Http.values) return elm.Native.Http.values;
4+ elm.Native = elm.Native || {};
5+ elm.Native.Http = elm.Native.Http || {};
6+ if (elm.Native.Http.values) return elm.Native.Http.values;
77
8+ var JS = Elm.JavaScript.make(elm);
9+ var List = Elm.List.make(elm);
10+ var Signal = Elm.Signal.make(elm);
811
9− var JS = Elm.JavaScript.make(elm);
10− var List = Elm.List.make(elm);
11− var Signal = Elm.Signal.make(elm);
12+ function registerReq(queue,responses) {
13+ return function(req) {
14+ if (req.url.length > 0) {
15+ sendReq(queue,responses,req);
16+ }
17+ };
18+ }
1219
20+ function updateQueue(queue,responses) {
21+ if (queue.length > 0) {
22+ elm.notify(responses.id, queue[0].value);
23+ if (queue[0].value.ctor !== 'Waiting') {
24+ queue.shift();
25+ setTimeout(function() { updateQueue(queue,responses); }, 0);
26+ }
27+ }
28+ }
1329
14− function registerReq(queue,responses) { return function(req) {
15− if (req.url.ctor !== '[]') { sendReq(queue,responses,req); }
16− };
17− }
30+ function sendReq(queue,responses,req) {
31+ var response = { value: { ctor:'Waiting' } };
32+ queue.push(response);
1833
19− function updateQueue(queue,responses) {
20− if (queue.length > 0) {
21− elm.notify(responses.id, queue[0].value);
22− if (queue[0].value.ctor !== 'Waiting') {
23− queue.shift();
24− setTimeout(function() { updateQueue(queue,responses); }, 0);
25− }
26− }
27− }
28−
29− function sendReq(queue,responses,req) {
30− var response = { value: { ctor:'Waiting' } };
31− queue.push(response);
32−
33− var request = null;
34− if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
35− if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
36− request.onreadystatechange = function(e) {
37− if (request.readyState === 4) {
38− response.value = (request.status >= 200 && request.status < 300 ?
39− { ctor:'Success', _0:JS.toString(request.responseText) } :
40− { ctor:'Failure', _0:request.status, _1:JS.toString(request.statusText) });
41− setTimeout(function() { updateQueue(queue,responses); }, 0);
42− }
43− };
44− request.open(JS.fromString(req.verb), JS.fromString(req.url), true);
45− function setHeader(pair) {
46− request.setRequestHeader( JS.fromString(pair._0), JS.fromString(pair._1) );
47− }
48− List.map(setHeader)(req.headers);
49− request.send(JS.fromString(req.body));
50− }
34+ var request = (window.ActiveXObject
35+ ? new ActiveXObject("Microsoft.XMLHTTP")
36+ : new XMLHttpRequest());
5137
52− function send(requests) {
53− var responses = Signal.constant(elm.Http.values.Waiting);
54− var sender = A2( Signal.lift, registerReq([],responses), requests );
55− function f(x) { return function(y) { return x; } }
56− return A3( Signal.lift2, f, responses, sender );
57− }
38+ request.onreadystatechange = function(e) {
39+ if (request.readyState === 4) {
40+ response.value = (request.status >= 200 && request.status < 300 ?
41+ { ctor:'Success', _0:JS.toString(request.responseText) } :
42+ { ctor:'Failure', _0:request.status, _1:JS.toString(request.statusText) });
43+ setTimeout(function() { updateQueue(queue,responses); }, 0);
44+ }
45+ };
46+ request.open(JS.fromString(req.verb), JS.fromString(req.url), true);
47+ function setHeader(pair) {
48+ request.setRequestHeader( JS.fromString(pair._0), JS.fromString(pair._1) );
49+ }
50+ List.map(setHeader)(req.headers);
51+ request.send(JS.fromString(req.body));
52+ }
5853
59− return elm.Native.Http.values = {send:send};
54+ function send(requests) {
55+ var responses = Signal.constant(elm.Http.values.Waiting);
56+ var sender = A2( Signal.lift, registerReq([],responses), requests );
57+ function f(x) { return function(y) { return x; } }
58+ return A3( Signal.lift2, f, responses, sender );
59+ }
6060
61+ return elm.Native.Http.values = {
62+ send:send
63+ };
6164};
libraries/Native/String.js+3−2
@@ -144,7 +144,8 @@ Elm.Native.String.make = function(elm) {
144144 return str.indexOf(sub) === 0;
145145 }
146146 function endsWith(sub, str) {
147− return str.lastIndexOf(sub) === str.length - sub.length;
147+ return str.length >= sub.length &&
148+ str.lastIndexOf(sub) === str.length - sub.length;
148149 }
149150 function indexes(sub, str) {
150151 var subLen = sub.length;
@@ -248,4 +249,4 @@ Elm.Native.String.make = function(elm) {
248249 toList: toList,
249250 fromList: fromList,
250251 };
251−};
252+};
libraries/Native/Text.js+58−42
@@ -4,11 +4,10 @@ Elm.Native.Text.make = function(elm) {
44 elm.Native.Text = elm.Native.Text || {};
55 if (elm.Native.Text.values) return elm.Native.Text.values;
66
7− var JS = Elm.JavaScript.make(elm);
8− var Utils = Elm.Native.Utils.make(elm);
9− var Color = Elm.Native.Color.make(elm);
7+ var toCss = Elm.Native.Color.make(elm).toCss;
108 var Element = Elm.Graphics.Element.make(elm);
11− var show = Elm.Native.Show.make(elm).show;
9+ var List = Elm.Native.List.make(elm);
10+ var Utils = Elm.Native.Utils.make(elm);
1211
1312 function makeSpaces(s) {
1413 if (s.length == 0) { return s; }
@@ -51,13 +50,51 @@ Elm.Native.Text.make = function(elm) {
5150 return arr.join('<br/>');
5251 }
5352
54− function toText(str) { return Utils.txt(properEscape(JS.fromString(str))); }
53+ function toText(str) { return Utils.txt(properEscape(str)); }
54+
55+ // conversions from Elm values to CSS
56+ function toTypefaces(list) {
57+ var typefaces = List.toArray(list);
58+ for (var i = typefaces.length; i--; ) {
59+ var typeface = typefaces[i];
60+ if (typeface.indexOf(' ') > -1) {
61+ typefaces[i] = "'" + typeface + "'";
62+ }
63+ }
64+ return typefaces.join(',');
65+ }
66+ function toLine(line) {
67+ var ctor = line.ctor;
68+ return ctor === 'Under' ? 'underline' :
69+ ctor === 'Over' ? 'overline' : 'line-through';
70+ }
5571
72+ // setting styles of Text
73+ function style(style, text) {
74+ var newText = '<span style="color:' + toCss(style.color) + ';'
75+ if (style.typeface.ctor !== '[]') {
76+ newText += 'font-family:' + toTypefaces(style.typeface) + ';'
77+ }
78+ if (style.height.ctor !== "Nothing") {
79+ newText += 'font-size:' + style.height._0 + 'px;';
80+ }
81+ if (style.bold) {
82+ newText += 'font-weight:bold;';
83+ }
84+ if (style.italic) {
85+ newText += 'font-style:italic;';
86+ }
87+ if (style.line.ctor !== 'Nothing') {
88+ newText += 'text-decoration:' + toLine(style.line._0) + ';';
89+ }
90+ newText += '">' + Utils.makeText(text) + '</span>'
91+ return Utils.txt(newText);
92+ }
5693 function height(px, text) {
5794 return { style: 'font-size:' + px + 'px;', text:text }
5895 }
59− function typeface(name, text) {
60− return { style: 'font-family:' + name + ';', text:text }
96+ function typeface(names, text) {
97+ return { style: 'font-family:' + toTypefaces(names) + ';', text:text }
6198 }
6299 function monospace(text) {
63100 return { style: 'font-family:monospace;', text:text }
@@ -71,25 +108,16 @@ Elm.Native.Text.make = function(elm) {
71108 function link(href, text) {
72109 return { href: toText(href), text:text };
73110 }
74− function underline(text) {
75− return { line: ' underline', text:text };
76− }
77− function overline(text) {
78− return { line: ' overline', text:text };
79− }
80− function strikeThrough(text) {
81− return { line: ' line-through', text:text };
111+ function line(line, text) {
112+ return { style: 'text-decoration:' + toLine(line) + ';', text:text };
82113 }
83114
84− function color(c, text) {
85− var color = (c._3 === 1)
86− ? ('rgb(' + c._0 + ', ' + c._1 + ', ' + c._2 + ')')
87− : ('rgba(' + c._0 + ', ' + c._1 + ', ' + c._2 + ', ' + c._3 + ')');
88− return { style: 'color:' + color + ';', text:text };
115+ function color(color, text) {
116+ return { style: 'color:' + toCss(color) + ';', text:text };
89117 }
90118
91− function position(align) {
92− function create(text) {
119+ function block(align) {
120+ return function(text) {
93121 var raw = {
94122 ctor :'RawHtml',
95123 html : Utils.makeText(text),
@@ -100,7 +128,6 @@ Elm.Native.Text.make = function(elm) {
100128 var pos = A2(Utils.htmlHeight, 0, raw);
101129 return A3(Element.newElement, pos._0, pos._1, raw);
102130 }
103− return create;
104131 }
105132
106133 function markdown(text, guid) {
@@ -115,36 +142,25 @@ Elm.Native.Text.make = function(elm) {
115142 return A3(Element.newElement, pos._0, pos._1, raw);
116143 }
117144
118− var text = position('left');
119− function asText(v) {
120− return text(monospace(toText(show(v))));
121− }
122−
123− function plainText(v) {
124− return text(toText(v));
125− }
126−
127145 return elm.Native.Text.values = {
128146 toText: toText,
129147
130148 height : F2(height),
131149 italic : italic,
132150 bold : bold,
133− underline : underline,
134− overline : overline,
135− strikeThrough : strikeThrough,
151+ line : F2(line),
136152 monospace : monospace,
137153 typeface : F2(typeface),
138154 color : F2(color),
139155 link : F2(link),
140156
141− justified : position('justify'),
142− centered : position('center'),
143− righted : position('right'),
144− text : text,
145− plainText : plainText,
146− markdown : markdown,
157+ leftAligned : block('left'),
158+ rightAligned : block('right'),
159+ centered : block('center'),
160+ justified : block('justify'),
161+ markdown : markdown,
147162
148− asText : asText,
163+ toTypefaces:toTypefaces,
164+ toLine:toLine,
149165 };
150166};
libraries/Native/Utils.js+6−8
@@ -9,7 +9,12 @@ Elm.Native.Utils.make = function(elm) {
99 if (x === y) return true;
1010 if (typeof x === "object") {
1111 var c = 0;
12− for (var i in x) { ++c; if (!eq(x[i],y[i])) return false; }
12+ for (var i in x) {
13+ ++c;
14+ if (!eq(x[i],y[i])) {
15+ return false;
16+ }
17+ }
1318 return c === Object.keys(y).length;
1419 }
1520 if (typeof x === 'function') {
@@ -76,14 +81,8 @@ Elm.Native.Utils.make = function(elm) {
7681
7782 function makeText(text) {
7883 var style = '';
79− var line = '';
8084 var href = '';
8185 while (true) {
82− if (text.line) {
83− line += text.line;
84− text = text.text;
85− continue;
86− }
8786 if (text.style) {
8887 style += text.style;
8988 text = text.text;
@@ -95,7 +94,6 @@ Elm.Native.Utils.make = function(elm) {
9594 continue;
9695 }
9796 if (href) text = '<a href="' + href + '">' + text + '</a>';
98− if (line) style += 'text-decoration:' + line + ';';
9997 if (style) text = '<span style="' + style + '">' + text + '</span>';
10098 return text;
10199 }
libraries/Prelude.elmdeleted−8
@@ -1,8 +0,0 @@
1−module Prelude where
2−{-| Everything that is automatically imported -}
3−
4−import Native.Show
5−
6−-- Convert almost any value to its string representation.
7−show : a -> String
8−show = Native.Show.show
libraries/Signal.elm+1−1
@@ -10,7 +10,7 @@ past-dependence.
1010
1111Some useful functions for working with time (e.g. setting FPS) and combining
1212signals and time (e.g. delaying updates, getting timestamps) can be found in
13−the [`Time`](/docs/Signal/Time.elm) library.
13+the `Time` library.
1414
1515# Combine
1616@docs constant, lift, lift2, merge, merges, combine
libraries/String.elm+34−11
@@ -15,7 +15,7 @@ are enclosed in `"double quotes"`. Strings are *not* lists of characters.
1515@docs contains, startsWith, endsWith, indexes, indices
1616
1717# Conversions
18−@docs toInt, toFloat, toList, fromList
18+@docs show, toInt, toFloat, toList, fromList
1919
2020# Formatting
2121Cosmetic operations such as padding with extra characters or trimming whitespace.
@@ -28,10 +28,15 @@ Cosmetic operations such as padding with extra characters or trimming whitespace
2828@docs map, filter, foldl, foldr, any, all
2929-}
3030
31+import Native.Show
3132import Native.String
3233import Maybe (Maybe)
3334
34−{-| Check if a string is empty `(isEmpty "" == True)` -}
35+{-| Check if a string is empty.
36+
37+ isEmpty "" == True
38+ isEmpty "the world" == False
39+-}
3540isEmpty : String -> Bool
3641isEmpty = Native.String.isEmpty
3742
@@ -63,7 +68,11 @@ append = Native.String.append
6368concat : [String] -> String
6469concat = Native.String.concat
6570
66−{-| Get the length of a string `(length "innumerable" == 11)` -}
71+{-| Get the length of a string.
72+
73+ length "innumerable" == 11
74+
75+-}
6776length : String -> Int
6877length = Native.String.length
6978
@@ -81,7 +90,10 @@ map = Native.String.map
8190filter : (Char -> Bool) -> String -> String
8291filter = Native.String.filter
8392
84−{-| Reverse a string. `(reverse "stressed" == "desserts")` -}
93+{-| Reverse a string.
94+
95+ reverse "stressed" == "desserts"
96+-}
8597reverse : String -> String
8698reverse = Native.String.reverse
8799
@@ -117,7 +129,10 @@ split = Native.String.split
117129join : String -> [String] -> String
118130join = Native.String.join
119131
120−{-| Repeat a string N times `(repeat 3 "ha" == "hahaha")` -}
132+{-| Repeat a string N times.
133+
134+ repeat 3 "ha" == "hahaha"
135+-}
121136repeat : Int -> String -> String
122137repeat = Native.String.repeat
123138
@@ -159,18 +174,18 @@ pad = Native.String.pad
159174
160175{-| Pad a string on the left until it has a given length.
161176
162− pad 5 '.' "1" == "....1"
163− pad 5 '.' "11" == "...11"
164− pad 5 '.' "121" == "..121"
177+ padLeft 5 '.' "1" == "....1"
178+ padLeft 5 '.' "11" == "...11"
179+ padLeft 5 '.' "121" == "..121"
165180-}
166181padLeft : Int -> Char -> String -> String
167182padLeft = Native.String.padLeft
168183
169184{-| Pad a string on the right until it has a given length.
170185
171− pad 5 '.' "1" == "1...."
172− pad 5 '.' "11" == "11..."
173− pad 5 '.' "121" == "121.."
186+ padRight 5 '.' "1" == "1...."
187+ padRight 5 '.' "11" == "11..."
188+ padRight 5 '.' "121" == "121.."
174189-}
175190padRight : Int -> Char -> String -> String
176191padRight = Native.String.padRight
@@ -278,6 +293,14 @@ indexes = Native.String.indexes
278293indices : String -> String -> [Int]
279294indices = Native.String.indexes
280295
296+{-| Turn any kind of value into a string.
297+
298+ show 42 == "42"
299+ show [1,2] == "[1,2]"
300+-}
301+show : a -> String
302+show = Native.Show.show
303+
281304{-| Try to convert a string into an int, failing on improperly formatted strings.
282305
283306 toInt "123" == Just 123
libraries/Text.elm+156−44
@@ -1,98 +1,209 @@
11module Text where
2−
3−{-| Functions for displaying text
2+{-| A library for styling and displaying text. Whlie the `String` library
3+focuses on representing and manipulating strings of character strings, the
4+`Text` library focuses on how those strings should look on screen. It lets
5+you make text bold or italic, set the typeface, set the text size, etc.
46
57# Creating Text
68@docs toText
79
810# Creating Elements
9−@docs plainText, asText, text, centered, justified, righted
1011
11−# Formatting
12−@docs color, typeface, height, link
12+Each of the following functions places `Text` into a box. The function you use
13+determines the alignment of the text.
14+
15+@docs leftAligned, rightAligned, centered, justified
16+
17+# Links and Style
18+@docs link, Style, style, defaultStyle, Line
19+
20+# Convenience Functions
21+
22+There are two convenience functions for creating an `Element` which can be
23+useful when debugging or prototyping:
1324
14−# Simple Formatting
15−@docs monospace, bold, italic, underline, overline, strikeThrough
25+@docs plainText, asText
26+
27+There are also a bunch of functions to set parts of a `Style` individually:
28+
29+@docs typeface, monospace, height, color, bold, italic, line
1630
1731-}
1832
1933import Basics (..)
20−import Color (Color)
34+import String
35+import Color (Color, black)
2136import Graphics.Element (Element, Three, Pos, ElementPrim, Properties)
22−import Maybe (Maybe)
37+import Maybe (Maybe, Nothing)
38+import Native.Show
2339import Native.Text
2440
2541data Text = Text
2642
27−{-| Convert a string into text which can be styled and displayed. -}
43+{-| Styles for lines on text. This allows you to add an underline, an overline,
44+or a strike out text:
45+
46+ line Under (toText "underline")
47+ line Over (toText "overline")
48+ line Through (toText "strike out")
49+-}
50+data Line = Under | Over | Through
51+
52+{-| Representation of all the ways you can style `Text`. If the `typeface` list
53+is empty or the `height` is `Nothing`, the users will fall back on their
54+browser's default settings. The following `Style` is black, 16 pixel tall,
55+underlined, and Times New Roman (assuming that typeface is available on the
56+user's computer):
57+
58+ { typeface = [ "Times New Roman", "serif" ]
59+ , height = Just 16
60+ , color = black
61+ , bold = False
62+ , italic = False
63+ , line = Just Under
64+ }
65+-}
66+type Style =
67+ { typeface : [String]
68+ , height : Maybe Float
69+ , color : Color
70+ , bold : Bool
71+ , italic : Bool
72+ , line : Maybe Line
73+ }
74+
75+{-| Plain black text. It uses the browsers default typeface and text height.
76+No decorations are used:
77+
78+ { typeface = []
79+ , height = Nothing
80+ , color = black
81+ , bold = False
82+ , italic = False
83+ , line = Nothing
84+ }
85+-}
86+defaultStyle : Style
87+defaultStyle =
88+ { typeface = []
89+ , height = Nothing
90+ , color = black
91+ , bold = False
92+ , italic = False
93+ , line = Nothing
94+ }
95+
96+{-| Convert a string into text which can be styled and displayed. To show the
97+string `"Hello World!"` on screen in italics, you could say:
98+
99+ main = leftAligned (italic (toText "Hello World!"))
100+-}
28101toText : String -> Text
29102toText = Native.Text.toText
30103
31−{-| Set the typeface of some text. The first argument should be a comma
32−separated listing of the desired typefaces:
104+{-| Set the style of some text. For example, if you design a `Style` called
105+`footerStyle` that is specifically for the bottom of your page, you could apply
106+it to text like this:
107+
108+ style footerStyle (toText "the old prince / 2007")
109+-}
110+style : Style -> Text -> Text
111+style = Native.Text.style
112+
113+{-| Provide a list of prefered typefaces for some text.
33114
34− "helvetica, arial, sans-serif"
115+ ["helvetica","arial","sans-serif"]
35116
36−Works the same as the CSS font-family property.
117+Not every browser has access to the same typefaces, so rendering will use the
118+first typeface in the list that is found on the user's computer. If there are
119+no matches, it will use their default typeface. This works the same as the CSS
120+font-family property.
37121-}
38−typeface : String -> Text -> Text
122+typeface : [String] -> Text -> Text
39123typeface = Native.Text.typeface
40124
41−{-| Switch to a monospace typeface. Good for code snippets. -}
125+{-| Switch to a monospace typeface. Good for code snippets.
126+
127+ monospace (toText "foldl (+) 0 [1,2,3]")
128+-}
42129monospace : Text -> Text
43130monospace = Native.Text.monospace
44131
45−{-| Create a link. -}
132+{-| Create a link by providing a URL and the text of the link:
133+
134+ link "http://elm-lang.org" (toText "Elm Website")
135+-}
46136link : String -> Text -> Text
47137link = Native.Text.link
48138
49−{-| Set the height of text in pixels. -}
139+{-| Set the height of some text:
140+
141+ height 40 (toText "Title")
142+-}
50143height : Float -> Text -> Text
51144height = Native.Text.height
52145
53−{-| Set the color of a string. -}
146+{-| Set the color of some text:
147+
148+ color red (toText "Red")
149+-}
54150color : Color -> Text -> Text
55151color = Native.Text.color
56152
57−{-| Make a string bold. -}
153+{-| Make text bold:
154+
155+ toText "sometimes you want " ++ bold (toText "emphasis")
156+-}
58157bold : Text -> Text
59158bold = Native.Text.bold
60159
61−{-| Italicize a string. -}
160+{-| Make text italic:
161+
162+ toText "make it " ++ italic (toText "important")
163+-}
62164italic : Text -> Text
63165italic = Native.Text.italic
64166
65−{-| Draw a line above a string. -}
66−overline : Text -> Text
67−overline = Native.Text.overline
167+{-| Put lines on text:
68168
69−{-| Underline a string. -}
70−underline : Text -> Text
71−underline = Native.Text.underline
169+ line Under (toText "underlined")
170+ line Over (toText "overlined")
171+ line Through (toText "strike out")
172+-}
173+line : Line -> Text -> Text
174+line = Native.Text.line
72175
73−{-| Draw a line through a string. -}
74−strikeThrough : Text -> Text
75−strikeThrough = Native.Text.strikeThrough
176+{-| `Text` is aligned along the left side of the text block. This is sometimes
177+known as *ragged right*.
178+-}
179+leftAligned : Text -> Element
180+leftAligned = Native.Text.leftAligned
76181
77−{-| Display justified, styled text. -}
78−justified : Text -> Element
79−justified = Native.Text.justified
182+{-| `Text` is aligned along the right side of the text block. This is sometimes
183+known as *ragged left*.
184+-}
185+rightAligned : Text -> Element
186+rightAligned = Native.Text.rightAligned
80187
81−{-| Display centered, styled text. -}
188+{-| `Text` is centered in the text block. There is equal spacing on either side
189+of a line of text.
190+-}
82191centered : Text -> Element
83192centered = Native.Text.centered
84193
85−{-| Display right justified, styled text. -}
86−righted : Text -> Element
87−righted = Native.Text.righted
194+{-| `Text` is aligned along the left and right sides of the text block. Word
195+spacing is adjusted to make this possible.
196+-}
197+justified : Text -> Element
198+justified = Native.Text.justified
88199
89−{-| Display styled text. -}
90−text : Text -> Element
91−text = Native.Text.text
200+{-| Display a string with no styling:
92201
93−{-| Display a plain string. -}
202+ plainText string = leftAligned (toText string)
203+-}
94204plainText : String -> Element
95−plainText = Native.Text.plainText
205+plainText str =
206+ leftAligned (toText str)
96207
97208{-| for internal use only -}
98209markdown : Element
@@ -101,9 +212,10 @@ markdown = Native.Text.markdown
101212{-| Convert anything to its textual representation and make it displayable in
102213the browser:
103214
104− asText == text . monospace . show
215+ asText value = text (monospace (show value))
105216
106217Excellent for debugging.
107218-}
108219asText : a -> Element
109−asText = Native.Text.asText
220+asText value =
221+ leftAligned (monospace (toText (Native.Show.show value)))
libraries/elm_dependencies.json+6−3
@@ -1,9 +1,9 @@
1−{ "version": "0.11"
1+{ "version": "0.12"
22, "summary": "Elm's standard libraries"
3−, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.11, you should be using version 0.11 of the standard libraries."
3+, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.12, you should be using version 0.12 of the standard libraries."
44, "license": "BSD3"
55, "repository": "http://github.com/evancz/Elm.git"
6−, "elm-version": "0.11"
6+, "elm-version": "0.12"
77, "dependencies": {}
88, "exposed-modules":
99 [ "Basics"
@@ -11,11 +11,13 @@
1111 , "Char"
1212 , "Color"
1313 , "Date"
14+ , "Debug"
1415 , "Dict"
1516 , "Either"
1617 , "Graphics.Element"
1718 , "Graphics.Collage"
1819 , "Graphics.Input"
20+ , "Graphics.Input.Field"
1921 , "Http"
2022 , "JavaScript"
2123 , "JavaScript.Experimental"
@@ -32,6 +34,7 @@
3234 , "Text"
3335 , "Time"
3436 , "Touch"
37+ , "Trampoline"
3538 , "Transform2D"
3639 , "WebSocket"
3740 , "Window"
runtime/HotSwap.js+2−2
@@ -5,8 +5,8 @@
55// structure.
66ElmRuntime.swap = function(from, to) {
77 function similar(nodeOld,nodeNew) {
8− idOkay = nodeOld.id === nodeNew.id;
9− lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
8+ var idOkay = nodeOld.id === nodeNew.id;
9+ var lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
1010 return idOkay && lengthOkay;
1111 }
1212 function swap(nodeOld,nodeNew) {
runtime/Init.js+1−1
@@ -91,7 +91,7 @@ function init(display, container, module, ports, moduleToReplace) {
9191 checkPorts(elm);
9292 } catch(e) {
9393 var directions = "<br/>&nbsp; &nbsp; Open the developer console for more details."
94− Module.main = Elm.Text.make(elm).text('<code>' + e.message + directions + '</code>');
94+ Module.main = Elm.Text.make(elm).leftAligned('<code>' + e.message + directions + '</code>');
9595 reportAnyErrors = function() { throw e; }
9696 }
9797 inputs = ElmRuntime.filterDeadInputs(inputs);
runtime/Render/Element.js+89−41
@@ -7,10 +7,16 @@ var newElement = Utils.newElement, extract = Utils.extract,
77 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
88 fromList = Utils.fromList, eq = Utils.eq;
99
10−function setProps(props, e) {
11− e.style.width = (props.width |0) + 'px';
12− e.style.height = (props.height|0) + 'px';
13− if (props.opacity !== 1) { e.style.opacity = props.opacity; }
10+function setProps(elem, e) {
11+ var props = elem.props;
12+ var element = elem.element;
13+ var width = props.width - (element.adjustWidth || 0);
14+ var height = props.height - (element.adjustHeight || 0);
15+ e.style.width = (width |0) + 'px';
16+ e.style.height = (height|0) + 'px';
17+ if (props.opacity !== 1) {
18+ e.style.opacity = props.opacity;
19+ }
1420 if (props.color.ctor === 'Just') {
1521 e.style.backgroundColor = extract(props.color._0);
1622 }
@@ -28,35 +34,59 @@ function setProps(props, e) {
2834 e.appendChild(a);
2935 }
3036 if (props.hover.ctor !== '_Tuple0') {
31− e.style.pointerEvents = 'auto';
32− e.elm_hover_handler = props.hover;
33− e.elm_hover_count = 0;
34− e.addEventListener('mouseover', function() {
35− if (e.elm_hover_count++ > 0) return;
36− var handler = e.elm_hover_handler;
37− if (handler !== null) {
38− handler(true);
39− }
40− });
41− e.addEventListener('mouseout', function(evt) {
42− if (e.contains(evt.toElement || evt.relatedTarget)) return;
43− e.elm_hover_count = 0;
44− var handler = e.elm_hover_handler;
45− if (handler !== null) {
46− handler(false);
47− }
48− });
37+ addHover(e, props.hover);
4938 }
5039 if (props.click.ctor !== '_Tuple0') {
51− e.style.pointerEvents = 'auto';
52− e.elm_click_handler = props.click;
53− e.addEventListener('click', function() {
54− e.elm_click_handler(Tuple0);
55− });
40+ addClick(e, props.click);
5641 }
5742 return e;
5843}
5944
45+function addClick(e, handler) {
46+ e.style.pointerEvents = 'auto';
47+ e.elm_click_handler = handler;
48+ function trigger() {
49+ e.elm_click_handler(Utils.Tuple0);
50+ }
51+ e.elm_click_trigger = trigger;
52+ e.addEventListener('click', trigger);
53+}
54+
55+function removeClick(e, handler) {
56+ if (e.elm_click_trigger) {
57+ e.removeEventListener('click', e.elm_click_trigger);
58+ }
59+}
60+
61+function addHover(e, handler) {
62+ e.style.pointerEvents = 'auto';
63+ e.elm_hover_handler = handler;
64+ e.elm_hover_count = 0;
65+
66+ function over() {
67+ if (e.elm_hover_count++ > 0) return;
68+ e.elm_hover_handler(true);
69+ }
70+ function out(evt) {
71+ if (e.contains(evt.toElement || evt.relatedTarget)) return;
72+ e.elm_hover_count = 0;
73+ e.elm_hover_handler(false);
74+ }
75+ e.elm_hover_over = over;
76+ e.elm_hover_out = out;
77+ e.addEventListener('mouseover', over);
78+ e.addEventListener('mouseout', out);
79+}
80+
81+function removeHover(e) {
82+ if (e.elm_hover_over) {
83+ e.removeEventListener('mouseover', e.elm_hover_over);
84+ }
85+ if (e.elm_hover_out) {
86+ e.removeEventListener('mouseout', e.elm_hover_out);
87+ }
88+}
89+
6090function image(props, img) {
6191 switch (img._0.ctor) {
6292 case 'Plain': return plainImage(img._3);
@@ -143,7 +173,12 @@ function toPos(pos) {
143173
144174// must clear right, left, top, bottom, and transform
145175// before calling this function
146−function setPos(pos,w,h,e) {
176+function setPos(pos,elem,e) {
177+ var element = elem.element;
178+ var props = elem.props;
179+ var w = props.width + (element.adjustWidth ? element.adjustWidth : 0);
180+ var h = props.height + (element.adjustHeight ? element.adjustHeight : 0);
181+
147182 e.style.position = 'absolute';
148183 e.style.margin = 'auto';
149184 var transform = '';
@@ -163,7 +198,7 @@ function setPos(pos,w,h,e) {
163198
164199function container(pos,elem) {
165200 var e = render(elem);
166− setPos(pos, elem.props.width, elem.props.height, e);
201+ setPos(pos, elem, e);
167202 var div = newElement('div');
168203 div.style.position = 'relative';
169204 div.style.overflow = 'hidden';
@@ -200,7 +235,7 @@ function rawHtml(elem) {
200235 return div;
201236}
202237
203−function render(elem) { return setProps(elem.props, makeElement(elem)); }
238+function render(elem) { return setProps(elem, makeElement(elem)); }
204239function makeElement(e) {
205240 var elem = e.element;
206241 switch(elem.ctor) {
@@ -294,7 +329,7 @@ function update(node, curr, next) {
294329 break;
295330 case "Container":
296331 update(node.firstChild, currE._1, nextE._1);
297− setPos(nextE._0, nextE._1.props.width, nextE._1.props.height, node.firstChild);
332+ setPos(nextE._0, nextE._1, node.firstChild);
298333 break;
299334 case "Custom":
300335 if (currE.type === nextE.type) {
@@ -308,10 +343,19 @@ function update(node, curr, next) {
308343}
309344
310345function updateProps(node, curr, next) {
311− var props = next.props, currP = curr.props, e = node;
312− if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
313− if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
314− if (props.opacity !== 1 && props.opacity !== currP.opacity) {
346+ var props = next.props;
347+ var currP = curr.props;
348+ var e = node;
349+ var element = next.element;
350+ var width = props.width - (element.adjustWidth || 0);
351+ var height = props.height - (element.adjustHeight || 0);
352+ if (width !== currP.width) {
353+ e.style.width = (width|0) + 'px';
354+ }
355+ if (height !== currP.height) {
356+ e.style.height = (height|0) + 'px';
357+ }
358+ if (props.opacity !== currP.opacity) {
315359 e.style.opacity = props.opacity;
316360 }
317361 var nextColor = (props.color.ctor === 'Just' ?
@@ -338,17 +382,21 @@ function updateProps(node, curr, next) {
338382 }
339383
340384 // update hover handlers
341− if (props.hover.ctor !== '_Tuple0') {
342− e.elm_hover_handler = props.hover;
385+ if (props.hover.ctor === '_Tuple0') {
386+ removeHover(e);
343387 } else if (e.elm_hover_handler) {
344− e.elm_hover_handler = null;
388+ e.elm_hover_handler = props.hover;
389+ } else {
390+ addHover(e, props.hover);
345391 }
346392
347393 // update click handlers
348− if (props.click.ctor !== '_Tuple0') {
349− e.elm_click_handler = props.click;
394+ if (props.click.ctor === '_Tuple0') {
395+ removeClick(e);
350396 } else if (e.elm_click_handler) {
351− e.elm_click_handler = null;
397+ e.elm_click_handler = props.click;
398+ } else {
399+ addClick(e, props.click);
352400 }
353401}
354402
server/LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c)2012-2013, Evan Czaplicki
1+Copyright (c)2012-2014, Evan Czaplicki
22
33All rights reserved.
44
server/Server.hs+112−99
@@ -1,114 +1,127 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
13module Main where
24
3−import Control.Monad (msum,guard,when)
5+import Control.Applicative ((<$>),(<|>))
6+import Control.Monad (guard)
47import Control.Monad.Trans (MonadIO(liftIO))
5−import Data.List (isPrefixOf, isSuffixOf, (\\))
6−import Data.Version (showVersion)
7−import Happstack.Server
8−import Happstack.Server.Compression
8+import qualified Data.ByteString as BS
9+import qualified Data.ByteString.Char8 as BSC
10+import qualified Data.HashMap.Strict as Map
11+import qualified Data.Version as Version
12+import System.Console.CmdArgs
913import System.Directory
10−import System.Environment
1114import System.Exit
1215import System.FilePath
1316import System.Process
14−import GHC.IO.Handle
15−import qualified Elm.Internal.Paths as ElmPaths
16−import Paths_elm_server
17−
18−runtime = "/elm-runtime.js"
19−
20−serve :: Int -> String -> IO ()
21−serve portNumber libLoc = do
22− putStrLn $ "Elm Server " ++ showVersion version ++
23− ": running at <http://localhost:" ++ show portNumber ++ ">"
24− putStrLn "Just refresh a page to recompile it!"
25− simpleHTTP httpConf $ do
26− _ <- compressedResponseFilter
27− msum [ uriRest serveElm
28− , uriRest (serveLib libLoc)
29− , serveDirectory EnableBrowsing [] "."
30− ]
31− where httpConf = nullConf { port = portNumber }
32−
33−pageTitle :: String -> String
34−pageTitle = dropExtension . takeBaseName
35−
36−serveElm :: FilePath -> ServerPartT IO Response
37−serveElm fp =
38− do fileExists <- liftIO $ doesFileExist file
39− guard (fileExists && takeExtension fp == ".elm")
40− onSuccess compile serve
17+import System.IO (hGetContents, Handle)
18+
19+import Paths_elm_server (version)
20+import qualified Elm.Internal.Paths as Elm
21+import Snap.Core
22+import Snap.Http.Server
23+import Snap.Util.FileServe
24+
25+data Flags = Flags
26+ { port :: Int
27+ , runtime :: Maybe FilePath
28+ } deriving (Data,Typeable,Show,Eq)
29+
30+flags :: Flags
31+flags = Flags
32+ { port = 8000 &= help "set the port of the server"
33+ , runtime = Nothing &= typFile
34+ &= help "Specify a custom location for Elm's runtime system."
35+ } &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
36+ \It serves static files and freshly recompiled Elm files."
37+ &= helpArg [explicit, name "help", name "h"]
38+ &= versionArg [ explicit, name "version", name "v"
39+ , summary (Version.showVersion version)
40+ ]
41+ &= summary ("Elm Server " ++ Version.showVersion version ++
42+ ", (c) Evan Czaplicki 2011-2014")
43+
44+
45+config :: Config Snap a
46+config = setAccessLog ConfigNoLog (setErrorLog ConfigNoLog defaultConfig)
47+
48+-- | Set up the server.
49+main :: IO ()
50+main = do
51+ cargs <- cmdArgs flags
52+ putStrLn $ "Elm Server " ++ Version.showVersion version ++
53+ ": Just refresh a page to recompile it!"
54+ httpServe (setPort (port cargs) config) $
55+ serveRuntime (maybe Elm.runtime id (runtime cargs))
56+ <|> serveElm
57+ <|> serveDirectoryWith directoryConfig "."
58+
59+directoryConfig :: MonadSnap m => DirectoryConfig m
60+directoryConfig =
61+ fancyDirectoryConfig
62+ { indexGenerator = defaultIndexGenerator defaultMimeTypes indexStyle
63+ , mimeTypes = Map.insert ".elm" "text/html" defaultMimeTypes
64+ }
65+
66+indexStyle :: BS.ByteString
67+indexStyle =
68+ "body { margin:0; font-family:sans-serif; background:rgb(245,245,245);\
69+ \ font-family: calibri, verdana, helvetica, arial; }\
70+ \div.header { padding: 40px 50px; font-size: 24px; }\
71+ \div.content { padding: 0 40px }\
72+ \div.footer { display:none; }\
73+ \table { width:100%; border-collapse:collapse; }\
74+ \td { padding: 6px 10px; }\
75+ \tr:nth-child(odd) { background:rgb(216,221,225); }\
76+ \td { font-family:monospace }\
77+ \th { background:rgb(90,99,120); color:white; text-align:left;\
78+ \ padding:10px; font-weight:normal; }"
79+
80+runtimeName :: String
81+runtimeName = "elm-runtime.js"
82+
83+serveRuntime :: FilePath -> Snap ()
84+serveRuntime runtimePath =
85+ do file <- BSC.unpack . rqPathInfo <$> getRequest
86+ guard (file == runtimeName)
87+ serveFileAs "application/javascript" runtimePath
88+
89+serveElm :: Snap ()
90+serveElm =
91+ do file <- BSC.unpack . rqPathInfo <$> getRequest
92+ exists <- liftIO $ doesFileExist file
93+ guard (exists && takeExtension file == ".elm")
94+ onSuccess (compile file) (serve file)
4195 where
42− file = tail fp
96+ compile file =
97+ let elmArgs = [ "--make", "--runtime=" ++ runtimeName, file ]
98+ in createProcess $ (proc "elm" elmArgs) { std_out = CreatePipe }
4399
44− compile = liftIO $ createProcess $ (proc "elm" args) { std_out = CreatePipe }
45− where args = [ "--make", "--runtime=" ++ runtime, file ]
100+ serve file =
101+ serveFileAs "text/html; charset=UTF-8" ("build" </> replaceExtension file "html")
46102
47− serve = serveFile (asContentType "text/html")
48− ("build" </> replaceExtension file "html")
103+failure :: String -> Snap ()
104+failure msg =
105+ do modifyResponse $ setResponseStatus 404 "Not found"
106+ writeBS $ BSC.pack msg
49107
50−onSuccess action success = do
51− (_, stdout, _, handle) <- action
52− exitCode <- liftIO $ waitForProcess handle
53− case (exitCode, stdout) of
54− (ExitFailure 127, _) ->
55− badRequest $ toResponse "Error: elm binary not found in your path."
56− (ExitFailure _, Just out) ->
57− do str <- liftIO $ hGetContents out
58− badRequest $ toResponse str
59− (ExitFailure _, Nothing) ->
60− badRequest $ toResponse "See command line for error message."
61− (ExitSuccess, _) -> success
108+onSuccess :: IO (t, Maybe Handle, t1, ProcessHandle) -> Snap () -> Snap ()
109+onSuccess action success =
110+ do (_, stdout, _, handle) <- liftIO action
111+ exitCode <- liftIO $ waitForProcess handle
112+ case (exitCode, stdout) of
113+ (ExitFailure 127, _) ->
114+ failure "Error: elm compiler not found in your path."
62115
116+ (ExitFailure _, Just out) ->
117+ failure =<< liftIO (hGetContents out)
63118
64−serveLib :: FilePath -> String -> ServerPartT IO Response
65−serveLib libLoc fp = do
66− guard (fp == runtime)
67− serveFile (asContentType "application/javascript") libLoc
119+ (ExitFailure _, Nothing) ->
120+ failure "See command line for error message."
68121
69−main :: IO ()
70−main = getArgs >>= parse
71−
72−parse :: [String] -> IO ()
73−parse ("--help":_) = putStrLn usage
74−parse ("--version":_) = putStrLn ("The Elm Server " ++ showVersion version)
75−parse args =
76− if null remainingArgs then
77− serve portNumber elmRuntime
78− else
79− putStrLn usageMini
122+ (ExitSuccess, _) -> success
80123
81− where
82− runtimeArg = filter (isPrefixOf "--runtime-location=") args
83− portArg = filter (isPrefixOf "--port=") args
84− remainingArgs = (args \\ runtimeArg) \\ portArg
85−
86− argValue arg = tail $ dropWhile (/= '=') (head arg)
87− portNumber = if null portArg then 8000 else read (argValue portArg) :: Int
88− elmRuntime = if null runtimeArg then
89− ElmPaths.runtime
90− else
91− argValue runtimeArg
92−
93−usageMini :: String
94−usageMini =
95− "Usage: elm-server [OPTIONS]\n\
96− \Try `elm-server --help' for more information."
97−
98−usage :: String
99−usage =
100− "Usage: elm-server [OPTIONS]\n\
101− \Compiles and serves .elm files from the current directory.\n\
102− \Example: elm-server\n\
103− \\n\
104− \Server configuration:\n\
105− \ --port set the port to listen on (default: 8000)\n\
106− \\n\
107− \Resource Locations:\n\
108− \ --runtime-location set the location of the Elm runtime\n\
109− \\n\
110− \Compiler Information:\n\
111− \ --version print the version information and exit\n\
112− \ --help display this help and exit\n\
113− \\n\
114− \Elm home page: <http://elm-lang.org>"
124+{--
125+pageTitle :: String -> String
126+pageTitle = dropExtension . takeBaseName
127+--}
server/elm-server.cabal+14−13
@@ -1,7 +1,9 @@
11Name: elm-server
2−Version: 0.10.1
3−Synopsis: The Elm language server.
4−Description: This package provides a standalone, Happstack-based Elm server.
2+Version: 0.11
3+Synopsis: Server for developing Elm projects
4+Description: Provides a standalone Snap server that serves static files.
5+ For Elm files, it recompiles them and serves them as HTML,
6+ so you can just refresh to see the new version.
57
68Homepage: http://elm-lang.org
79
@@ -10,7 +12,7 @@ License-file: LICENSE
1012
1113Author: Evan Czaplicki
1214Maintainer: info@elm-lang.org
13−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
15+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
1416
1517Category: Compiler, Language
1618
@@ -26,15 +28,14 @@ source-repository head
2628Executable elm-server
2729 Main-is: Server.hs
2830 Build-depends: base >=4.2 && <5,
31+ bytestring,
32+ cmdargs,
2933 containers >= 0.3,
3034 directory,
31− transformers >= 0.2,
32− mtl >= 2,
33− parsec >= 3.1.1,
34− blaze-html >= 0.5.1,
35− HTTP >= 4000,
36− happstack-server,
37− deepseq,
3835 filepath,
39− Elm >= 0.10.1,
40− process
36+ Elm >= 0.11,
37+ snap-core,
38+ snap-server,
39+ mtl,
40+ process,
41+ unordered-containers