on master help sign in
Merge branch 'dev' into json
09c91a9Evan Czaplicki
Elm.cabal+4−4
@@ -1,5 +1,5 @@
1Name: Elm
1Name: Elm
2−Version: 0.11
2+Version: 0.12
3Synopsis: The Elm language module.
3Synopsis: The Elm language module.
4Description: Elm aims to make client-side web-development more pleasant.
4Description: Elm aims to make client-side web-development more pleasant.
5 It is a statically/strongly typed, functional reactive
5 It is a statically/strongly typed, functional reactive
@@ -14,13 +14,13 @@ License-file: LICENSE
14
14
15Author: Evan Czaplicki
15Author: Evan Czaplicki
16Maintainer: info@elm-lang.org
16Maintainer: info@elm-lang.org
17−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
17+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
18
18
19Category: Compiler, Language
19Category: Compiler, Language
20
20
21Build-type: Custom
21Build-type: Custom
22
22
23−Extra-source-files: changelog.txt
23+Extra-source-files: changelog.md
24Data-dir: data
24Data-dir: data
25Data-files: elm-runtime.js interfaces.data docs.json
25Data-files: elm-runtime.js interfaces.data docs.json
26Cabal-version: >=1.9
26Cabal-version: >=1.9
@@ -251,7 +251,7 @@ Test-Suite test-elm
251 Elm,
251 Elm,
252 test-framework,
252 test-framework,
253 test-framework-hunit,
253 test-framework-hunit,
254− test-framework-quickcheck2,
254+ test-framework-quickcheck2 >= 0.3,
255 HUnit,
255 HUnit,
256 pretty,
256 pretty,
257 QuickCheck >= 2 && < 3,
257 QuickCheck >= 2 && < 3,
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2012-2013, Evan Czaplicki
1+Copyright (c) 2012-2014, Evan Czaplicki
2
2
3All rights reserved.
3All rights reserved.
4
4
changelog.txt → changelog.mdrenamed+10−7
@@ -1,25 +1,28 @@
1
1
2−## next
2+## 0.12
3
3
4−Breaking Changes:
4+#### Breaking Changes:
5
5
6− * Overhaul Graphics.Input 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
7− * Overhaul Regex library
9+ * Overhaul Regex library (inspired by Attila Gazso)
8 * Change syntax for "import open List" to "import List (..)"
10 * Change syntax for "import open List" to "import List (..)"
9 * Improved JSON format for types generated by elm-doc
11 * Improved JSON format for types generated by elm-doc
10 * Remove problematic Mouse.isClicked signal
12 * Remove problematic Mouse.isClicked signal
11 * Revise the semantics of keepWhen and dropWhen to only update when
13 * Revise the semantics of keepWhen and dropWhen to only update when
12 the filtered signal changes (thanks Max New and Janis Voigtländer)
14 the filtered signal changes (thanks Max New and Janis Voigtländer)
13
15
14−Improvements:
16+#### Improvements:
15
17
18+ * Add Graphics.Input.Field for customizable text fields
16 * Add Trampoline library (thanks to @maxsnew and @timthelion)
19 * Add Trampoline library (thanks to @maxsnew and @timthelion)
17 * Add Debug library (inspired by @timthelion)
20 * Add Debug library (inspired by @timthelion)
18 * Drastically improved performance on markdown parsing (thanks to @Dandandan)
21 * Drastically improved performance on markdown parsing (thanks to @Dandandan)
19 * Add Date.fromTime function
22 * Add Date.fromTime function
20 * Use pointer-events to detect hovers on layered elements (thanks to @Xashili)
23 * Use pointer-events to detect hovers on layered elements (thanks to @Xashili)
21− * Fix serious bugs in Bitwise library
24+ * Fix bugs in Bitwise library
22− * Fix bug in exporting Maybe values through ports
25+ * Fix bug when exporting Maybe values through ports
23
26
24## 0.11
27## 0.11
25
28
compiler/Build/Flags.hs+1−1
@@ -41,4 +41,4 @@ flags = Flags
41 } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
41 } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
42 &= helpArg [explicit, name "help", name "h"]
42 &= helpArg [explicit, name "help", name "h"]
43 &= versionArg [explicit, name "version", name "v", summary (show Version.elmVersion)]
43 &= 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 =
55expression :: Expr -> State Int (Expression ())
55expression :: Expr -> State Int (Expression ())
56expression (A region expr) =
56expression (A region expr) =
57 case expr of
57 case expr of
58− Var (V.Raw x) -> return $ ref x
58+ Var (V.Raw x) -> return $ obj x
59 Literal lit -> return $ literal lit
59 Literal lit -> return $ literal lit
60
60
61 Range lo hi ->
61 Range lo hi ->
@@ -87,10 +87,9 @@ expression (A region expr) =
87 do fields' <- forM fields $ \(f,e) -> do
87 do fields' <- forM fields $ \(f,e) -> do
88 (,) f <$> expression e
88 (,) f <$> expression e
89 let fieldMap = List.foldl' combine Map.empty fields'
89 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
91 where
91 where
92 combine r (k,v) = Map.insertWith (++) k [v] r
92 combine r (k,v) = Map.insertWith (++) k [v] r
93− prop = PropId () . var
94 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
93 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
95 Map.toList . Map.filter (not . null) $ Map.map tail fs
94 Map.toList . Map.filter (not . null) $ Map.map tail fs
96 visible fs = map (first prop) . Map.toList $ Map.map head fs
95 visible fs = map (first prop) . Map.toList $ Map.map head fs
@@ -243,7 +242,11 @@ match region mtch =
243 isLiteral p = case p of
242 isLiteral p = case p of
244 Case.Clause (Right _) _ _ -> True
243 Case.Clause (Right _) _ _ -> True
245 _ -> False
244 _ -> False
245+
246+ access name
246− access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
247+ | any isLiteral clauses = obj name
248+ | otherwise = dotSep (split name ++ ["ctor"])
249+
247 format isChars e
250 format isChars e
248 | or isChars = InfixExpr () OpAdd e (string "")
251 | or isChars = InfixExpr () OpAdd e (string "")
249 | otherwise = e
252 | otherwise = e
@@ -307,7 +310,7 @@ generate unsafeModule =
307 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
310 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
308 where
311 where
309 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
312 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)
311
314
312 assign path expr =
315 assign path expr =
313 case path of
316 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
20 Just _ -> []
20 Just _ -> []
21
21
22prelude :: [(String, ImportMethod)]
22prelude :: [(String, ImportMethod)]
23−prelude = string : text ++ map (\n -> (n, Hiding [])) modules
23+prelude = string ++ text ++ map (\n -> (n, Hiding [])) modules
24 where
24 where
25 text = map ((,) "Text") [ As "Text", Hiding ["link", "color", "height"] ]
25 text = map ((,) "Text") [ As "Text", Hiding ["link", "color", "height"] ]
26− string = ("String", As "String")
26+ string = map ((,) "String") [ As "String", Importing ["show"] ]
27− modules = [ "Basics", "Signal", "List", "Maybe", "Time", "Prelude"
27+ modules = [ "Basics", "Signal", "List", "Maybe", "Time", "Color"
28− , "Graphics.Element", "Color", "Graphics.Collage", "Native.Ports" ]
28+ , "Graphics.Element", "Graphics.Collage", "Native.Ports" ]
29
29
30interfaces :: Bool -> IO Interfaces
30interfaces :: Bool -> IO Interfaces
31interfaces noPrelude =
31interfaces noPrelude =
compiler/Type/Constrain/Expression.hs+4−4
@@ -36,10 +36,10 @@ constrain env (A region expr) tipe =
36 | otherwise -> return (name <? tipe)
36 | otherwise -> return (name <? tipe)
37
37
38 Range lo hi ->
38 Range lo hi ->
39− exists $ \x -> do
39+ existsNumber $ \n -> do
40− clo <- constrain env lo x
40+ clo <- constrain env lo n
41− chi <- constrain env hi x
41+ chi <- constrain env hi n
42− return $ and [clo, chi, list x === tipe]
42+ return $ and [clo, chi, list n === tipe]
43
43
44 ExplicitList exprs ->
44 ExplicitList exprs ->
45 exists $ \x -> do
45 exists $ \x -> do
compiler/Type/Type.hs+5
@@ -141,6 +141,11 @@ exists f = do
141 v <- liftIO $ var Flexible
141 v <- liftIO $ var Flexible
142 ex [v] <$> f (VarN v)
142 ex [v] <$> f (VarN v)
143
143
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+
144
149
145instance Show a => Show (UF.Point a) where
150instance Show a => Show (UF.Point a) where
146 show point = unsafePerformIO $ fmap show (UF.descriptor point)
151 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.
39# Polar Coordinates
39# Polar Coordinates
40@docs toPolar, fromPolar
40@docs toPolar, fromPolar
41
41
42+# Floating Point Checks
43+@docs isNaN, isInfinite
44+
42# Tuples
45# Tuples
43@docs fst, snd
46@docs fst, snd
44
47
@@ -271,6 +274,30 @@ ceiling = Native.Basics.ceiling
271toFloat : Int -> Float
274toFloat : Int -> Float
272toFloat = Native.Basics.toFloat
275toFloat = Native.Basics.toFloat
273
276
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
274
301
275-- Function Helpers
302-- Function Helpers
276
303
libraries/Char.elm+2−2
@@ -52,8 +52,8 @@ toLocaleLower = Native.Char.toLocaleLower
52
52
53type KeyCode = Int
53type KeyCode = Int
54
54
55−{-| Convert to unicode. Used with the [`Keyboard`](/docs/Keyboard.elm)
55+{-| Convert to unicode. Used with the `Keyboard` library, which expects the
56−library, which expects the input to be uppercase. -}
56+input to be uppercase. -}
57toCode : Char -> KeyCode
57toCode : Char -> KeyCode
58toCode = Native.Char.toCode
58toCode = Native.Char.toCode
59
59
libraries/Debug.elm+2
@@ -1,6 +1,8 @@
1module Debug where
1module Debug where
2{-| This library is for investigating bugs or performance problems. It should
2{-| This library is for investigating bugs or performance problems. It should
3*not* be used in production code.
3*not* be used in production code.
4+
5+@docs log
4-}
6-}
5
7
6import Native.Debug
8import Native.Debug
libraries/Dict.elm+1−2
@@ -35,7 +35,6 @@ import Basics (..)
35import Maybe (..)
35import Maybe (..)
36import Native.Error
36import Native.Error
37import List
37import List
38−import String
39import Native.Utils
38import Native.Utils
40
39
41-- BBlack and NBlack should only be used during the deletion
40-- BBlack and NBlack should only be used during the deletion
@@ -197,7 +196,7 @@ lessBlackTree t = case t of
197
196
198reportRemBug : String -> NColor -> String -> String -> a
197reportRemBug : String -> NColor -> String -> String -> a
199reportRemBug msg c lgot rgot =
198reportRemBug msg c lgot rgot =
200− Native.Error.raise . String.concat <| [
199+ Native.Error.raise <| List.concat [
201 "Internal red-black tree invariant violated, expected ",
200 "Internal red-black tree invariant violated, expected ",
202 msg,
201 msg,
203 "and got",
202 "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!
19@docs Input, input
19@docs Input, input
20
20
21# Basic Input Elements
21# 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+
23@docs button, customButton, checkbox, dropDown
26@docs button, customButton, checkbox, dropDown
24
27
25# Clicks and Hovers
28# Clicks and Hovers
26@docs clickable, hoverable
29@docs clickable, hoverable
27
30
28−# Text Fields
29−@docs field, password, email, noContent, FieldContent, Selection, Direction
30-}
31-}
31
32
32import Signal (Signal)
33import Signal (Signal)
@@ -45,14 +46,17 @@ type Input a = { signal : Signal a, handle : Handle a }
45
46
46data Handle a = Handle
47data Handle a = Handle
47
48
48−{-| Create a new `Input`. You just give the initial value for the
49+{-| This creates a new `Input`. You provide a single argument that will serve
49−input&rsquo;s `signal`.
50+as the initial value of the input&rsquo;s `signal`. For example:
51+
52+ numbers : Input Int
53+ numbers = input 42
50
54
51−Note for Advanced Users: creating an `Input` is an inherently imperative
55+The initial value of `numbers.signal` is 42, and you will be able
52−action, so this is one of very few impure functions in Elm. That means
56+to pipe updates to the input using `numbers.handle`.
57+
58+Note: This is an inherently impure function. Specifically, `(input ())` and
53−`(input ())` and `(input ())` are actually two different inputs with different
59+`(input ())` are actually two different inputs with different signals and handles.
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.
56-}
60-}
57input : a -> Input a
61input : a -> Input a
58input = Native.Graphics.Input.input
62input = Native.Graphics.Input.input
@@ -114,7 +118,7 @@ favorite British sport:
114
118
115 data Sport = Football | Cricket | Snooker
119 data Sport = Football | Cricket | Snooker
116
120
117− sport : Input Sport
121+ sport : Input (Maybe Sport)
118 sport = input Nothing
122 sport = input Nothing
119
123
120 sportDropDown : Element
124 sportDropDown : Element
@@ -172,57 +176,3 @@ distinguished with IDs or more complex data structures.
172-}
176-}
173clickable : Handle a -> a -> Element -> Element
177clickable : Handle a -> a -> Element -> Element
174clickable = Native.Graphics.Input.clickable
178clickable = 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 @@
1module Http where
1module Http where
2−
3−{-| A library for asynchronous HTTP requests. See the
2+{-| A library for asynchronous HTTP requests. See the `WebSocket`
4−[WebSocket](http://elm-lang.org/docs/WebSocket.elm) library if
5−you have very strict latency requirements.
3+library if you have very strict latency requirements.
6
4
7# Sending Requests
5# Sending Requests
8@docs send, sendGet
6@docs send, sendGet
libraries/Keyboard.elm+2−4
@@ -1,5 +1,4 @@
1module Keyboard where
1module Keyboard where
2−
3{-| Library for working with keyboard input.
2{-| Library for working with keyboard input.
4
3
5# Representing Keys
4# Representing Keys
@@ -20,9 +19,8 @@ import Signal (Signal)
20import Native.Keyboard
19import Native.Keyboard
21
20
22{-| Type alias to make it clearer what integers are supposed to represent
21{-| Type alias to make it clearer what integers are supposed to represent
23−in this library. Use [`Char.toCode`](docs/Char.elm#toCode) and
22+in this library. Use `Char.toCode` and `Char.fromCode` to convert key codes
24−[`Char.fromCode`](/docs/Char.elm#fromCode) to convert key codes to characters.
25−Use the uppercase character with `toCode`.
23+to characters. Use the uppercase character with `toCode`.
26-}
24-}
27type KeyCode = Int
25type KeyCode = Int
28
26
libraries/Native/Basics.js+3
@@ -19,6 +19,7 @@ Elm.Native.Basics.make = function(elm) {
19 return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n; }
19 return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n; }
20 function xor(a,b) { return a !== b; }
20 function xor(a,b) { return a !== b; }
21 function not(b) { return !b; }
21 function not(b) { return !b; }
22+ function isInfinite(n) { return n === Infinity || n === -Infinity }
22
23
23 function truncate(n) { return n|0; }
24 function truncate(n) { return n|0; }
24
25
@@ -53,6 +54,8 @@ Elm.Native.Basics.make = function(elm) {
53 floor:Math.floor,
54 floor:Math.floor,
54 round:Math.round,
55 round:Math.round,
55 toFloat:function(x) { return x; },
56 toFloat:function(x) { return x; },
57+ isNaN:isNaN,
58+ isInfinite:isInfinite
56 };
59 };
57
60
58 return elm.Native.Basics.values = basics;
61 return elm.Native.Basics.values = basics;
libraries/Native/Color.js+9−2
@@ -6,6 +6,12 @@ Elm.Native.Color.make = function(elm) {
6
6
7 var Utils = Elm.Native.Utils.make(elm);
7 var Utils = Elm.Native.Utils.make(elm);
8
8
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+
9 function complement(rgb) {
15 function complement(rgb) {
10 var hsv = toHSV(rgb);
16 var hsv = toHSV(rgb);
11 hsv.hue = (hsv.hue + 180) % 360;
17 hsv.hue = (hsv.hue + 180) % 360;
@@ -63,7 +69,8 @@ Elm.Native.Color.make = function(elm) {
63 return elm.Native.Color.values = {
69 return elm.Native.Color.values = {
64 hsva:F4(hsva),
70 hsva:F4(hsva),
65 hsv:F3(hsv),
71 hsv:F3(hsv),
66− complement:complement
72+ complement:complement,
73+ toCss:toCss
67 };
74 };
68
75
69−};
76+};
libraries/Native/Debug.js+5−3
@@ -7,10 +7,12 @@ Elm.Native.Debug.make = function(elm) {
7 var show = Elm.Native.Show.make(elm).show;
7 var show = Elm.Native.Show.make(elm).show;
8
8
9 function log(tag,value) {
9 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);
12 } else {
14 } else {
13− console.log(tag + ': ' + show(value));
15+ console.log(msg);
14 }
16 }
15 return value;
17 return value;
16 }
18 }
libraries/Native/Graphics/Input.js+118−39
@@ -8,6 +8,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
8 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
8 var Render = ElmRuntime.use(ElmRuntime.Render.Element);
9 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
9 var newNode = ElmRuntime.use(ElmRuntime.Render.Utils).newElement;
10
10
11+ var toCss = Elm.Native.Color.make(elm).toCss;
12+ var Text = Elm.Native.Text.make(elm);
11 var Signal = Elm.Signal.make(elm);
13 var Signal = Elm.Signal.make(elm);
12 var newElement = Elm.Graphics.Element.make(elm).newElement;
14 var newElement = Elm.Graphics.Element.make(elm).newElement;
13 var JS = Elm.Native.JavaScript.make(elm);
15 var JS = Elm.Native.JavaScript.make(elm);
@@ -78,7 +80,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
78
80
79 function updateButton(node, oldModel, newModel) {
81 function updateButton(node, oldModel, newModel) {
80 node.elm_signal = newModel.signal;
82 node.elm_signal = newModel.signal;
81− node.elm_value = nemModel.value;
83+ node.elm_value = newModel.value;
82 var txt = newModel.text;
84 var txt = newModel.text;
83 if (oldModel.text !== txt) node.innerHTML = txt;
85 if (oldModel.text !== txt) node.innerHTML = txt;
84 }
86 }
@@ -215,72 +217,149 @@ Elm.Native.Graphics.Input.make = function(elm) {
215 }
217 }
216 }
218 }
217
219
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+
218 function renderField(model) {
262 function renderField(model) {
219 var field = newNode('input');
263 var field = newNode('input');
264+ updateFieldStyle(field.style, model.style);
265+ field.style.borderStyle = 'solid';
220 field.style.pointerEvents = 'auto';
266 field.style.pointerEvents = 'auto';
221− field.elm_signal = model.signal;
222− field.elm_handler = model.handler;
223
267
224 field.type = model.type;
268 field.type = model.type;
225 field.placeholder = JS.fromString(model.placeHolder);
269 field.placeholder = JS.fromString(model.placeHolder);
226 field.value = JS.fromString(model.content.string);
270 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';
231
271
272+ field.elm_signal = model.signal;
273+ field.elm_handler = model.handler;
274+ field.elm_old_value = field.value;
275+
232− function update() {
276+ function inputUpdate(event) {
277+ var curr = field.elm_old_value;
278+ var next = field.value;
279+ if (curr === next) {
280+ return;
281+ }
282+
233− var direction = field.selectionDirection === 'backward' ? 'Backward' : 'Forward';
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+
234 elm.notify(field.elm_signal.id, field.elm_handler({
288 elm.notify(field.elm_signal.id, field.elm_handler({
235 _:{},
289 _:{},
236− string:JS.toString(field.value),
290+ string: JS.toString(next),
237 selection: {
291 selection: {
238− start:field.selectionStart,
292+ _:{},
239− end:field.selectionEnd,
293+ start: start,
294+ end: end,
240− direction: { ctor:direction }
295+ direction: { ctor: direction }
241 },
296 },
242 }));
297 }));
243 }
298 }
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+ }
244− function mousedown() {
313+ function mousedown(event) {
245− update();
314+ mouseUpdate(event);
246 elm.node.addEventListener('mouseup', mouseup);
315 elm.node.addEventListener('mouseup', mouseup);
247 }
316 }
248− function mouseup() {
317+ function mouseup(event) {
249− update();
318+ mouseUpdate(event);
250 elm.node.removeEventListener('mouseup', mouseup)
319 elm.node.removeEventListener('mouseup', mouseup)
251 }
320 }
252− field.addEventListener('keyup', update);
321+ field.addEventListener('input', inputUpdate);
253 field.addEventListener('mousedown', mousedown);
322 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+ });
254
329
255 return field;
330 return field;
256 }
331 }
257
332
258− function updateField(node, oldModel, newModel) {
333+ function updateField(field, oldModel, newModel) {
259− node.elmHandler = newModel.handler;
334+ if (oldModel.style !== newModel.style) {
260− var newStr = JS.fromString(newModel.content.string);
335+ updateFieldStyle(field.style, newModel.style);
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;
270 }
336 }
271−
337+ field.elm_signal = newModel.signal;
272− if (node.selectionStart !== start
338+ field.elm_handler = newModel.handler;
273− || node.selectionEnd !== end
339+
274− || node.selectionDirection !== direction) {
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';
275− setRange(node, start, end, direction);
348+ setRange(field, selection.start, selection.end, direction);
276 }
349 }
277 }
350 }
278
351
279 function mkField(type) {
352 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;
281 return A3(newElement, 200, 30, {
358 return A3(newElement, 200, 30, {
282 ctor: 'Custom',
359 ctor: 'Custom',
283− type: type + 'Input',
360+ type: type + 'Field',
361+ adjustWidth: adjustWidth,
362+ adjustHeight: adjustHeight,
284 render: renderField,
363 render: renderField,
285 update: updateField,
364 update: updateField,
286 model: {
365 model: {
@@ -288,14 +367,14 @@ Elm.Native.Graphics.Input.make = function(elm) {
288 handler:handler,
367 handler:handler,
289 placeHolder:placeHolder,
368 placeHolder:placeHolder,
290 content:content,
369 content:content,
370+ style:style,
291 type:type
371 type:type
292 }
372 }
293 });
373 });
294 }
374 }
295− return F4(field);
375+ return F5(field);
296 }
376 }
297
377
298−
299 function hoverable(signal, handler, elem) {
378 function hoverable(signal, handler, elem) {
300 function onHover(bool) {
379 function onHover(bool) {
301 elm.notify(signal.id, handler(bool));
380 elm.notify(signal.id, handler(bool));
libraries/Native/Regex.js+1−1
@@ -20,7 +20,7 @@ Elm.Native.Regex.make = function(elm) {
20 }
20 }
21
21
22 function contains(re, string) {
22 function contains(re, string) {
23− return re.test(JS.fromString(string));
23+ return JS.fromString(string).match(re) !== null;
24 }
24 }
25
25
26 function find(n, re, str) {
26 function find(n, re, str) {
libraries/Native/Show.js−3
@@ -6,10 +6,7 @@ Elm.Native.Show.make = function(elm) {
6
6
7 var NList = Elm.Native.List.make(elm);
7 var NList = Elm.Native.List.make(elm);
8 var List = Elm.List.make(elm);
8 var List = Elm.List.make(elm);
9− var Maybe = Elm.Maybe.make(elm);
10− var JS = Elm.JavaScript.make(elm);
11 var Dict = Elm.Dict.make(elm);
9 var Dict = Elm.Dict.make(elm);
12− var Json = Elm.Json.make(elm);
13 var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;
10 var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;
14
11
15 var toString = function(v) {
12 var toString = function(v) {
libraries/Native/Signal/Http.js+52−49
@@ -1,61 +1,64 @@
1Elm.Native.Http = {};
1Elm.Native.Http = {};
2Elm.Native.Http.make = function(elm) {
2Elm.Native.Http.make = function(elm) {
3
3
4− elm.Native = elm.Native || {};
4+ elm.Native = elm.Native || {};
5− elm.Native.Http = elm.Native.Http || {};
5+ elm.Native.Http = elm.Native.Http || {};
6− if (elm.Native.Http.values) return elm.Native.Http.values;
6+ if (elm.Native.Http.values) return elm.Native.Http.values;
7
7
8+ var JS = Elm.JavaScript.make(elm);
9+ var List = Elm.List.make(elm);
10+ var Signal = Elm.Signal.make(elm);
8
11
9− var JS = Elm.JavaScript.make(elm);
12+ function registerReq(queue,responses) {
10− var List = Elm.List.make(elm);
13+ return function(req) {
11− var Signal = Elm.Signal.make(elm);
14+ if (req.url.length > 0) {
15+ sendReq(queue,responses,req);
16+ }
17+ };
18+ }
12
19
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+ }
13
29
14− function registerReq(queue,responses) { return function(req) {
15− if (req.url.ctor !== '[]') { sendReq(queue,responses,req); }
30+ function sendReq(queue,responses,req) {
16− };
31+ var response = { value: { ctor:'Waiting' } };
17− }
32+ queue.push(response);
18
33
19− function updateQueue(queue,responses) {
34+ var request = (window.ActiveXObject
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+ ? new ActiveXObject("Microsoft.XMLHTTP")
35− if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
36+ : 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− }
51
37
52− function send(requests) {
38+ request.onreadystatechange = function(e) {
53− var responses = Signal.constant(elm.Http.values.Waiting);
39+ if (request.readyState === 4) {
54− var sender = A2( Signal.lift, registerReq([],responses), requests );
40+ response.value = (request.status >= 200 && request.status < 300 ?
55− function f(x) { return function(y) { return x; } }
41+ { ctor:'Success', _0:JS.toString(request.responseText) } :
56− return A3( Signal.lift2, f, responses, sender );
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));
57− }
52+ }
58
53
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+ }
60
60
61+ return elm.Native.Http.values = {
62+ send:send
63+ };
61};
64};
libraries/Native/String.js+3−2
@@ -144,7 +144,8 @@ Elm.Native.String.make = function(elm) {
144 return str.indexOf(sub) === 0;
144 return str.indexOf(sub) === 0;
145 }
145 }
146 function endsWith(sub, str) {
146 function endsWith(sub, str) {
147+ return str.length >= sub.length &&
147− return str.lastIndexOf(sub) === str.length - sub.length;
148+ str.lastIndexOf(sub) === str.length - sub.length;
148 }
149 }
149 function indexes(sub, str) {
150 function indexes(sub, str) {
150 var subLen = sub.length;
151 var subLen = sub.length;
@@ -248,4 +249,4 @@ Elm.Native.String.make = function(elm) {
248 toList: toList,
249 toList: toList,
249 fromList: fromList,
250 fromList: fromList,
250 };
251 };
251−};
252+};
libraries/Native/Text.js+58−42
@@ -4,11 +4,10 @@ Elm.Native.Text.make = function(elm) {
4 elm.Native.Text = elm.Native.Text || {};
4 elm.Native.Text = elm.Native.Text || {};
5 if (elm.Native.Text.values) return elm.Native.Text.values;
5 if (elm.Native.Text.values) return elm.Native.Text.values;
6
6
7− var JS = Elm.JavaScript.make(elm);
8− var Utils = Elm.Native.Utils.make(elm);
7+ var toCss = Elm.Native.Color.make(elm).toCss;
9− var Color = Elm.Native.Color.make(elm);
10 var Element = Elm.Graphics.Element.make(elm);
8 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);
12
11
13 function makeSpaces(s) {
12 function makeSpaces(s) {
14 if (s.length == 0) { return s; }
13 if (s.length == 0) { return s; }
@@ -51,13 +50,51 @@ Elm.Native.Text.make = function(elm) {
51 return arr.join('<br/>');
50 return arr.join('<br/>');
52 }
51 }
53
52
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+ }
55
71
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+ }
56 function height(px, text) {
93 function height(px, text) {
57 return { style: 'font-size:' + px + 'px;', text:text }
94 return { style: 'font-size:' + px + 'px;', text:text }
58 }
95 }
59− function typeface(name, text) {
96+ function typeface(names, text) {
60− return { style: 'font-family:' + name + ';', text:text }
97+ return { style: 'font-family:' + toTypefaces(names) + ';', text:text }
61 }
98 }
62 function monospace(text) {
99 function monospace(text) {
63 return { style: 'font-family:monospace;', text:text }
100 return { style: 'font-family:monospace;', text:text }
@@ -71,25 +108,16 @@ Elm.Native.Text.make = function(elm) {
71 function link(href, text) {
108 function link(href, text) {
72 return { href: toText(href), text:text };
109 return { href: toText(href), text:text };
73 }
110 }
74− function underline(text) {
111+ function line(line, text) {
75− return { line: ' underline', text:text };
112+ return { style: 'text-decoration:' + toLine(line) + ';', 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 };
82 }
113 }
83
114
84− function color(c, text) {
115+ function color(color, 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 };
116+ return { style: 'color:' + toCss(color) + ';', text:text };
89 }
117 }
90
118
91− function position(align) {
119+ function block(align) {
92− function create(text) {
120+ return function(text) {
93 var raw = {
121 var raw = {
94 ctor :'RawHtml',
122 ctor :'RawHtml',
95 html : Utils.makeText(text),
123 html : Utils.makeText(text),
@@ -100,7 +128,6 @@ Elm.Native.Text.make = function(elm) {
100 var pos = A2(Utils.htmlHeight, 0, raw);
128 var pos = A2(Utils.htmlHeight, 0, raw);
101 return A3(Element.newElement, pos._0, pos._1, raw);
129 return A3(Element.newElement, pos._0, pos._1, raw);
102 }
130 }
103− return create;
104 }
131 }
105
132
106 function markdown(text, guid) {
133 function markdown(text, guid) {
@@ -115,36 +142,25 @@ Elm.Native.Text.make = function(elm) {
115 return A3(Element.newElement, pos._0, pos._1, raw);
142 return A3(Element.newElement, pos._0, pos._1, raw);
116 }
143 }
117
144
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−
127 return elm.Native.Text.values = {
145 return elm.Native.Text.values = {
128 toText: toText,
146 toText: toText,
129
147
130 height : F2(height),
148 height : F2(height),
131 italic : italic,
149 italic : italic,
132 bold : bold,
150 bold : bold,
133− underline : underline,
151+ line : F2(line),
134− overline : overline,
135− strikeThrough : strikeThrough,
136 monospace : monospace,
152 monospace : monospace,
137 typeface : F2(typeface),
153 typeface : F2(typeface),
138 color : F2(color),
154 color : F2(color),
139 link : F2(link),
155 link : F2(link),
140
156
157+ leftAligned : block('left'),
158+ rightAligned : block('right'),
159+ centered : block('center'),
141− justified : position('justify'),
160+ justified : block('justify'),
142− centered : position('center'),
143− righted : position('right'),
144− text : text,
145− plainText : plainText,
146− markdown : markdown,
161+ markdown : markdown,
147
162
148− asText : asText,
163+ toTypefaces:toTypefaces,
164+ toLine:toLine,
149 };
165 };
150};
166};
libraries/Native/Utils.js+6−8
@@ -9,7 +9,12 @@ Elm.Native.Utils.make = function(elm) {
9 if (x === y) return true;
9 if (x === y) return true;
10 if (typeof x === "object") {
10 if (typeof x === "object") {
11 var c = 0;
11 var c = 0;
12+ for (var i in x) {
13+ ++c;
14+ if (!eq(x[i],y[i])) {
12− for (var i in x) { ++c; if (!eq(x[i],y[i])) return false; }
15+ return false;
16+ }
17+ }
13 return c === Object.keys(y).length;
18 return c === Object.keys(y).length;
14 }
19 }
15 if (typeof x === 'function') {
20 if (typeof x === 'function') {
@@ -76,14 +81,8 @@ Elm.Native.Utils.make = function(elm) {
76
81
77 function makeText(text) {
82 function makeText(text) {
78 var style = '';
83 var style = '';
79− var line = '';
80 var href = '';
84 var href = '';
81 while (true) {
85 while (true) {
82− if (text.line) {
83− line += text.line;
84− text = text.text;
85− continue;
86− }
87 if (text.style) {
86 if (text.style) {
88 style += text.style;
87 style += text.style;
89 text = text.text;
88 text = text.text;
@@ -95,7 +94,6 @@ Elm.Native.Utils.make = function(elm) {
95 continue;
94 continue;
96 }
95 }
97 if (href) text = '<a href="' + href + '">' + text + '</a>';
96 if (href) text = '<a href="' + href + '">' + text + '</a>';
98− if (line) style += 'text-decoration:' + line + ';';
99 if (style) text = '<span style="' + style + '">' + text + '</span>';
97 if (style) text = '<span style="' + style + '">' + text + '</span>';
100 return text;
98 return text;
101 }
99 }
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.
10
10
11Some useful functions for working with time (e.g. setting FPS) and combining
11Some useful functions for working with time (e.g. setting FPS) and combining
12signals and time (e.g. delaying updates, getting timestamps) can be found in
12signals and time (e.g. delaying updates, getting timestamps) can be found in
13−the [`Time`](/docs/Signal/Time.elm) library.
13+the `Time` library.
14
14
15# Combine
15# Combine
16@docs constant, lift, lift2, merge, merges, combine
16@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.
15@docs contains, startsWith, endsWith, indexes, indices
15@docs contains, startsWith, endsWith, indexes, indices
16
16
17# Conversions
17# Conversions
18−@docs toInt, toFloat, toList, fromList
18+@docs show, toInt, toFloat, toList, fromList
19
19
20# Formatting
20# Formatting
21Cosmetic operations such as padding with extra characters or trimming whitespace.
21Cosmetic 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
28@docs map, filter, foldl, foldr, any, all
28@docs map, filter, foldl, foldr, any, all
29-}
29-}
30
30
31+import Native.Show
31import Native.String
32import Native.String
32import Maybe (Maybe)
33import Maybe (Maybe)
33
34
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+-}
35isEmpty : String -> Bool
40isEmpty : String -> Bool
36isEmpty = Native.String.isEmpty
41isEmpty = Native.String.isEmpty
37
42
@@ -63,7 +68,11 @@ append = Native.String.append
63concat : [String] -> String
68concat : [String] -> String
64concat = Native.String.concat
69concat = Native.String.concat
65
70
66−{-| Get the length of a string `(length "innumerable" == 11)` -}
71+{-| Get the length of a string.
72+
73+ length "innumerable" == 11
74+
75+-}
67length : String -> Int
76length : String -> Int
68length = Native.String.length
77length = Native.String.length
69
78
@@ -81,7 +90,10 @@ map = Native.String.map
81filter : (Char -> Bool) -> String -> String
90filter : (Char -> Bool) -> String -> String
82filter = Native.String.filter
91filter = Native.String.filter
83
92
93+{-| Reverse a string.
94+
84−{-| Reverse a string. `(reverse "stressed" == "desserts")` -}
95+ reverse "stressed" == "desserts"
96+-}
85reverse : String -> String
97reverse : String -> String
86reverse = Native.String.reverse
98reverse = Native.String.reverse
87
99
@@ -117,7 +129,10 @@ split = Native.String.split
117join : String -> [String] -> String
129join : String -> [String] -> String
118join = Native.String.join
130join = Native.String.join
119
131
120−{-| Repeat a string N times `(repeat 3 "ha" == "hahaha")` -}
132+{-| Repeat a string N times.
133+
134+ repeat 3 "ha" == "hahaha"
135+-}
121repeat : Int -> String -> String
136repeat : Int -> String -> String
122repeat = Native.String.repeat
137repeat = Native.String.repeat
123
138
@@ -159,18 +174,18 @@ pad = Native.String.pad
159
174
160{-| Pad a string on the left until it has a given length.
175{-| Pad a string on the left until it has a given length.
161
176
162− pad 5 '.' "1" == "....1"
177+ padLeft 5 '.' "1" == "....1"
163− pad 5 '.' "11" == "...11"
178+ padLeft 5 '.' "11" == "...11"
164− pad 5 '.' "121" == "..121"
179+ padLeft 5 '.' "121" == "..121"
165-}
180-}
166padLeft : Int -> Char -> String -> String
181padLeft : Int -> Char -> String -> String
167padLeft = Native.String.padLeft
182padLeft = Native.String.padLeft
168
183
169{-| Pad a string on the right until it has a given length.
184{-| Pad a string on the right until it has a given length.
170
185
171− pad 5 '.' "1" == "1...."
186+ padRight 5 '.' "1" == "1...."
172− pad 5 '.' "11" == "11..."
187+ padRight 5 '.' "11" == "11..."
173− pad 5 '.' "121" == "121.."
188+ padRight 5 '.' "121" == "121.."
174-}
189-}
175padRight : Int -> Char -> String -> String
190padRight : Int -> Char -> String -> String
176padRight = Native.String.padRight
191padRight = Native.String.padRight
@@ -278,6 +293,14 @@ indexes = Native.String.indexes
278indices : String -> String -> [Int]
293indices : String -> String -> [Int]
279indices = Native.String.indexes
294indices = Native.String.indexes
280
295
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+
281{-| Try to convert a string into an int, failing on improperly formatted strings.
304{-| Try to convert a string into an int, failing on improperly formatted strings.
282
305
283 toInt "123" == Just 123
306 toInt "123" == Just 123
libraries/Text.elm+156−44
@@ -1,98 +1,209 @@
1module Text where
1module 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.
4
6
5# Creating Text
7# Creating Text
6@docs toText
8@docs toText
7
9
8# Creating Elements
10# Creating Elements
9−@docs plainText, asText, text, centered, justified, righted
10
11
11−# Formatting
12+Each of the following functions places `Text` into a box. The function you use
12−@docs color, typeface, height, link
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:
13
24
14−# Simple Formatting
25+@docs plainText, asText
26+
27+There are also a bunch of functions to set parts of a `Style` individually:
28+
15−@docs monospace, bold, italic, underline, overline, strikeThrough
29+@docs typeface, monospace, height, color, bold, italic, line
16
30
17-}
31-}
18
32
19import Basics (..)
33import Basics (..)
34+import String
20−import Color (Color)
35+import Color (Color, black)
21import Graphics.Element (Element, Three, Pos, ElementPrim, Properties)
36import Graphics.Element (Element, Three, Pos, ElementPrim, Properties)
22−import Maybe (Maybe)
37+import Maybe (Maybe, Nothing)
38+import Native.Show
23import Native.Text
39import Native.Text
24
40
25data Text = Text
41data Text = Text
26
42
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+
27−{-| Convert a string into text which can be styled and displayed. -}
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+-}
28toText : String -> Text
101toText : String -> Text
29toText = Native.Text.toText
102toText = Native.Text.toText
30
103
31−{-| Set the typeface of some text. The first argument should be a comma
104+{-| Set the style of some text. For example, if you design a `Style` called
32−separated listing of the desired typefaces:
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.
33
114
34− "helvetica, arial, sans-serif"
115+ ["helvetica","arial","sans-serif"]
35
116
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
36−Works the same as the CSS font-family property.
120+font-family property.
37-}
121-}
38−typeface : String -> Text -> Text
122+typeface : [String] -> Text -> Text
39typeface = Native.Text.typeface
123typeface = Native.Text.typeface
40
124
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+-}
42monospace : Text -> Text
129monospace : Text -> Text
43monospace = Native.Text.monospace
130monospace = Native.Text.monospace
44
131
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+-}
46link : String -> Text -> Text
136link : String -> Text -> Text
47link = Native.Text.link
137link = Native.Text.link
48
138
49−{-| Set the height of text in pixels. -}
139+{-| Set the height of some text:
140+
141+ height 40 (toText "Title")
142+-}
50height : Float -> Text -> Text
143height : Float -> Text -> Text
51height = Native.Text.height
144height = Native.Text.height
52
145
53−{-| Set the color of a string. -}
146+{-| Set the color of some text:
147+
148+ color red (toText "Red")
149+-}
54color : Color -> Text -> Text
150color : Color -> Text -> Text
55color = Native.Text.color
151color = Native.Text.color
56
152
57−{-| Make a string bold. -}
153+{-| Make text bold:
154+
155+ toText "sometimes you want " ++ bold (toText "emphasis")
156+-}
58bold : Text -> Text
157bold : Text -> Text
59bold = Native.Text.bold
158bold = Native.Text.bold
60
159
61−{-| Italicize a string. -}
160+{-| Make text italic:
161+
162+ toText "make it " ++ italic (toText "important")
163+-}
62italic : Text -> Text
164italic : Text -> Text
63italic = Native.Text.italic
165italic = Native.Text.italic
64
166
65−{-| Draw a line above a string. -}
167+{-| Put lines on text:
66−overline : Text -> Text
67−overline = Native.Text.overline
68
168
69−{-| Underline a string. -}
169+ line Under (toText "underlined")
170+ line Over (toText "overlined")
171+ line Through (toText "strike out")
172+-}
70−underline : Text -> Text
173+line : Line -> Text -> Text
71−underline = Native.Text.underline
174+line = Native.Text.line
72
175
73−{-| Draw a line through a string. -}
176+{-| `Text` is aligned along the left side of the text block. This is sometimes
74−strikeThrough : Text -> Text
177+known as *ragged right*.
75−strikeThrough = Native.Text.strikeThrough
178+-}
179+leftAligned : Text -> Element
180+leftAligned = Native.Text.leftAligned
76
181
77−{-| Display justified, styled text. -}
182+{-| `Text` is aligned along the right side of the text block. This is sometimes
183+known as *ragged left*.
184+-}
78−justified : Text -> Element
185+rightAligned : Text -> Element
79−justified = Native.Text.justified
186+rightAligned = Native.Text.rightAligned
80
187
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+-}
82centered : Text -> Element
191centered : Text -> Element
83centered = Native.Text.centered
192centered = Native.Text.centered
84
193
85−{-| Display right justified, styled text. -}
194+{-| `Text` is aligned along the left and right sides of the text block. Word
195+spacing is adjusted to make this possible.
196+-}
86−righted : Text -> Element
197+justified : Text -> Element
87−righted = Native.Text.righted
198+justified = Native.Text.justified
88
199
89−{-| Display styled text. -}
200+{-| Display a string with no styling:
90−text : Text -> Element
91−text = Native.Text.text
92
201
93−{-| Display a plain string. -}
202+ plainText string = leftAligned (toText string)
203+-}
94plainText : String -> Element
204plainText : String -> Element
95−plainText = Native.Text.plainText
205+plainText str =
206+ leftAligned (toText str)
96
207
97{-| for internal use only -}
208{-| for internal use only -}
98markdown : Element
209markdown : Element
@@ -101,9 +212,10 @@ markdown = Native.Text.markdown
101{-| Convert anything to its textual representation and make it displayable in
212{-| Convert anything to its textual representation and make it displayable in
102the browser:
213the browser:
103
214
104− asText == text . monospace . show
215+ asText value = text (monospace (show value))
105
216
106Excellent for debugging.
217Excellent for debugging.
107-}
218-}
108asText : a -> Element
219asText : 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"
2, "summary": "Elm's standard libraries"
2, "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."
4, "license": "BSD3"
4, "license": "BSD3"
5, "repository": "http://github.com/evancz/Elm.git"
5, "repository": "http://github.com/evancz/Elm.git"
6−, "elm-version": "0.11"
6+, "elm-version": "0.12"
7, "dependencies": {}
7, "dependencies": {}
8, "exposed-modules":
8, "exposed-modules":
9 [ "Basics"
9 [ "Basics"
@@ -11,11 +11,13 @@
11 , "Char"
11 , "Char"
12 , "Color"
12 , "Color"
13 , "Date"
13 , "Date"
14+ , "Debug"
14 , "Dict"
15 , "Dict"
15 , "Either"
16 , "Either"
16 , "Graphics.Element"
17 , "Graphics.Element"
17 , "Graphics.Collage"
18 , "Graphics.Collage"
18 , "Graphics.Input"
19 , "Graphics.Input"
20+ , "Graphics.Input.Field"
19 , "Http"
21 , "Http"
20 , "JavaScript"
22 , "JavaScript"
21 , "JavaScript.Experimental"
23 , "JavaScript.Experimental"
@@ -32,6 +34,7 @@
32 , "Text"
34 , "Text"
33 , "Time"
35 , "Time"
34 , "Touch"
36 , "Touch"
37+ , "Trampoline"
35 , "Transform2D"
38 , "Transform2D"
36 , "WebSocket"
39 , "WebSocket"
37 , "Window"
40 , "Window"
runtime/HotSwap.js+2−2
@@ -5,8 +5,8 @@
5// structure.
5// structure.
6ElmRuntime.swap = function(from, to) {
6ElmRuntime.swap = function(from, to) {
7 function similar(nodeOld,nodeNew) {
7 function similar(nodeOld,nodeNew) {
8− idOkay = nodeOld.id === nodeNew.id;
8+ var idOkay = nodeOld.id === nodeNew.id;
9− lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
9+ var lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
10 return idOkay && lengthOkay;
10 return idOkay && lengthOkay;
11 }
11 }
12 function swap(nodeOld,nodeNew) {
12 function swap(nodeOld,nodeNew) {
runtime/Init.js+1−1
@@ -91,7 +91,7 @@ function init(display, container, module, ports, moduleToReplace) {
91 checkPorts(elm);
91 checkPorts(elm);
92 } catch(e) {
92 } catch(e) {
93 var directions = "<br/>&nbsp; &nbsp; Open the developer console for more details."
93 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>');
95 reportAnyErrors = function() { throw e; }
95 reportAnyErrors = function() { throw e; }
96 }
96 }
97 inputs = ElmRuntime.filterDeadInputs(inputs);
97 inputs = ElmRuntime.filterDeadInputs(inputs);
runtime/Render/Element.js+89−41
@@ -7,10 +7,16 @@ var newElement = Utils.newElement, extract = Utils.extract,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
7 addTransform = Utils.addTransform, removeTransform = Utils.removeTransform,
8 fromList = Utils.fromList, eq = Utils.eq;
8 fromList = Utils.fromList, eq = Utils.eq;
9
9
10−function setProps(props, e) {
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);
11− e.style.width = (props.width |0) + 'px';
15+ e.style.width = (width |0) + 'px';
12− e.style.height = (props.height|0) + 'px';
16+ e.style.height = (height|0) + 'px';
17+ if (props.opacity !== 1) {
13− if (props.opacity !== 1) { e.style.opacity = props.opacity; }
18+ e.style.opacity = props.opacity;
19+ }
14 if (props.color.ctor === 'Just') {
20 if (props.color.ctor === 'Just') {
15 e.style.backgroundColor = extract(props.color._0);
21 e.style.backgroundColor = extract(props.color._0);
16 }
22 }
@@ -28,35 +34,59 @@ function setProps(props, e) {
28 e.appendChild(a);
34 e.appendChild(a);
29 }
35 }
30 if (props.hover.ctor !== '_Tuple0') {
36 if (props.hover.ctor !== '_Tuple0') {
31− e.style.pointerEvents = 'auto';
32− e.elm_hover_handler = props.hover;
37+ addHover(e, 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− });
49 }
38 }
50 if (props.click.ctor !== '_Tuple0') {
39 if (props.click.ctor !== '_Tuple0') {
51− e.style.pointerEvents = 'auto';
52− e.elm_click_handler = props.click;
40+ addClick(e, props.click);
53− e.addEventListener('click', function() {
54− e.elm_click_handler(Tuple0);
55− });
56 }
41 }
57 return e;
42 return e;
58}
43}
59
44
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+
60function image(props, img) {
90function image(props, img) {
61 switch (img._0.ctor) {
91 switch (img._0.ctor) {
62 case 'Plain': return plainImage(img._3);
92 case 'Plain': return plainImage(img._3);
@@ -143,7 +173,12 @@ function toPos(pos) {
143
173
144// must clear right, left, top, bottom, and transform
174// must clear right, left, top, bottom, and transform
145// before calling this function
175// 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+
147 e.style.position = 'absolute';
182 e.style.position = 'absolute';
148 e.style.margin = 'auto';
183 e.style.margin = 'auto';
149 var transform = '';
184 var transform = '';
@@ -163,7 +198,7 @@ function setPos(pos,w,h,e) {
163
198
164function container(pos,elem) {
199function container(pos,elem) {
165 var e = render(elem);
200 var e = render(elem);
166− setPos(pos, elem.props.width, elem.props.height, e);
201+ setPos(pos, elem, e);
167 var div = newElement('div');
202 var div = newElement('div');
168 div.style.position = 'relative';
203 div.style.position = 'relative';
169 div.style.overflow = 'hidden';
204 div.style.overflow = 'hidden';
@@ -200,7 +235,7 @@ function rawHtml(elem) {
200 return div;
235 return div;
201}
236}
202
237
203−function render(elem) { return setProps(elem.props, makeElement(elem)); }
238+function render(elem) { return setProps(elem, makeElement(elem)); }
204function makeElement(e) {
239function makeElement(e) {
205 var elem = e.element;
240 var elem = e.element;
206 switch(elem.ctor) {
241 switch(elem.ctor) {
@@ -294,7 +329,7 @@ function update(node, curr, next) {
294 break;
329 break;
295 case "Container":
330 case "Container":
296 update(node.firstChild, currE._1, nextE._1);
331 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);
298 break;
333 break;
299 case "Custom":
334 case "Custom":
300 if (currE.type === nextE.type) {
335 if (currE.type === nextE.type) {
@@ -308,10 +343,19 @@ function update(node, curr, next) {
308}
343}
309
344
310function updateProps(node, curr, next) {
345function updateProps(node, curr, next) {
311− var props = next.props, currP = curr.props, e = node;
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) {
312− if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
353+ e.style.width = (width|0) + 'px';
354+ }
355+ if (height !== currP.height) {
313− if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
356+ e.style.height = (height|0) + 'px';
357+ }
314− if (props.opacity !== 1 && props.opacity !== currP.opacity) {
358+ if (props.opacity !== currP.opacity) {
315 e.style.opacity = props.opacity;
359 e.style.opacity = props.opacity;
316 }
360 }
317 var nextColor = (props.color.ctor === 'Just' ?
361 var nextColor = (props.color.ctor === 'Just' ?
@@ -338,17 +382,21 @@ function updateProps(node, curr, next) {
338 }
382 }
339
383
340 // update hover handlers
384 // update hover handlers
341− if (props.hover.ctor !== '_Tuple0') {
385+ if (props.hover.ctor === '_Tuple0') {
342− e.elm_hover_handler = props.hover;
386+ removeHover(e);
343 } else if (e.elm_hover_handler) {
387 } 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);
345 }
391 }
346
392
347 // update click handlers
393 // update click handlers
348− if (props.click.ctor !== '_Tuple0') {
394+ if (props.click.ctor === '_Tuple0') {
349− e.elm_click_handler = props.click;
395+ removeClick(e);
350 } else if (e.elm_click_handler) {
396 } 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);
352 }
400 }
353}
401}
354
402
server/LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c)2012-2013, Evan Czaplicki
1+Copyright (c)2012-2014, Evan Czaplicki
2
2
3All rights reserved.
3All rights reserved.
4
4
server/Server.hs+112−99
@@ -1,114 +1,127 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
1module Main where
3module Main where
2
4
3−import Control.Monad (msum,guard,when)
5+import Control.Applicative ((<$>),(<|>))
6+import Control.Monad (guard)
4import Control.Monad.Trans (MonadIO(liftIO))
7import Control.Monad.Trans (MonadIO(liftIO))
5−import Data.List (isPrefixOf, isSuffixOf, (\\))
8+import qualified Data.ByteString as BS
6−import Data.Version (showVersion)
9+import qualified Data.ByteString.Char8 as BSC
7−import Happstack.Server
10+import qualified Data.HashMap.Strict as Map
8−import Happstack.Server.Compression
11+import qualified Data.Version as Version
12+import System.Console.CmdArgs
9import System.Directory
13import System.Directory
10−import System.Environment
11import System.Exit
14import System.Exit
12import System.FilePath
15import System.FilePath
13import System.Process
16import System.Process
14−import GHC.IO.Handle
17+import System.IO (hGetContents, Handle)
18+
19+import Paths_elm_server (version)
15−import qualified Elm.Internal.Paths as ElmPaths
20+import qualified Elm.Internal.Paths as Elm
16−import Paths_elm_server
21+import Snap.Core
17−
22+import Snap.Http.Server
18−runtime = "/elm-runtime.js"
23+import Snap.Util.FileServe
19−
24+
20−serve :: Int -> String -> IO ()
25+data Flags = Flags
21−serve portNumber libLoc = do
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
22− putStrLn $ "Elm Server " ++ showVersion version ++
52+ putStrLn $ "Elm Server " ++ Version.showVersion version ++
23− ": running at <http://localhost:" ++ show portNumber ++ ">"
24− putStrLn "Just refresh a page to recompile it!"
53+ ": Just refresh a page to recompile it!"
25− simpleHTTP httpConf $ do
54+ httpServe (setPort (port cargs) config) $
26− _ <- compressedResponseFilter
55+ serveRuntime (maybe Elm.runtime id (runtime cargs))
27− msum [ uriRest serveElm
56+ <|> serveElm
28− , uriRest (serveLib libLoc)
57+ <|> serveDirectoryWith directoryConfig "."
29− , serveDirectory EnableBrowsing [] "."
58+
30− ]
59+directoryConfig :: MonadSnap m => DirectoryConfig m
31− where httpConf = nullConf { port = portNumber }
60+directoryConfig =
32−
61+ fancyDirectoryConfig
33−pageTitle :: String -> String
62+ { indexGenerator = defaultIndexGenerator defaultMimeTypes indexStyle
34−pageTitle = dropExtension . takeBaseName
63+ , mimeTypes = Map.insert ".elm" "text/html" defaultMimeTypes
35−
64+ }
36−serveElm :: FilePath -> ServerPartT IO Response
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+
37−serveElm fp =
89+serveElm :: Snap ()
90+serveElm =
91+ do file <- BSC.unpack . rqPathInfo <$> getRequest
38− do fileExists <- liftIO $ doesFileExist file
92+ exists <- liftIO $ doesFileExist file
39− guard (fileExists && takeExtension fp == ".elm")
93+ guard (exists && takeExtension file == ".elm")
40− onSuccess compile serve
94+ onSuccess (compile file) (serve file)
41 where
95 where
42− file = tail fp
96+ compile file =
97+ let elmArgs = [ "--make", "--runtime=" ++ runtimeName, file ]
98+ in createProcess $ (proc "elm" elmArgs) { std_out = CreatePipe }
43
99
44− compile = liftIO $ createProcess $ (proc "elm" args) { std_out = CreatePipe }
100+ serve file =
45− where args = [ "--make", "--runtime=" ++ runtime, file ]
101+ serveFileAs "text/html; charset=UTF-8" ("build" </> replaceExtension file "html")
46
102
47− serve = serveFile (asContentType "text/html")
103+failure :: String -> Snap ()
48− ("build" </> replaceExtension file "html")
104+failure msg =
105+ do modifyResponse $ setResponseStatus 404 "Not found"
106+ writeBS $ BSC.pack msg
49
107
108+onSuccess :: IO (t, Maybe Handle, t1, ProcessHandle) -> Snap () -> Snap ()
50−onSuccess action success = do
109+onSuccess action success =
51− (_, stdout, _, handle) <- action
110+ do (_, stdout, _, handle) <- liftIO action
52− exitCode <- liftIO $ waitForProcess handle
111+ exitCode <- liftIO $ waitForProcess handle
53− case (exitCode, stdout) of
112+ case (exitCode, stdout) of
54− (ExitFailure 127, _) ->
113+ (ExitFailure 127, _) ->
55− badRequest $ toResponse "Error: elm binary not found in your path."
114+ failure "Error: elm compiler 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
62
115
116+ (ExitFailure _, Just out) ->
117+ failure =<< liftIO (hGetContents out)
63
118
64−serveLib :: FilePath -> String -> ServerPartT IO Response
119+ (ExitFailure _, Nothing) ->
65−serveLib libLoc fp = do
120+ failure "See command line for error message."
66− guard (fp == runtime)
67− serveFile (asContentType "application/javascript") libLoc
68
121
69−main :: IO ()
122+ (ExitSuccess, _) -> success
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
80
123
81− where
124+{--
82− runtimeArg = filter (isPrefixOf "--runtime-location=") args
125+pageTitle :: String -> String
83− portArg = filter (isPrefixOf "--port=") args
126+pageTitle = dropExtension . takeBaseName
84− remainingArgs = (args \\ runtimeArg) \\ portArg
127+--}
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>"
server/elm-server.cabal+14−13
@@ -1,7 +1,9 @@
1Name: elm-server
1Name: elm-server
2−Version: 0.10.1
2+Version: 0.11
3−Synopsis: The Elm language server.
3+Synopsis: Server for developing Elm projects
4−Description: This package provides a standalone, Happstack-based Elm server.
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.
5
7
6Homepage: http://elm-lang.org
8Homepage: http://elm-lang.org
7
9
@@ -10,7 +12,7 @@ License-file: LICENSE
10
12
11Author: Evan Czaplicki
13Author: Evan Czaplicki
12Maintainer: info@elm-lang.org
14Maintainer: info@elm-lang.org
13−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
15+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
14
16
15Category: Compiler, Language
17Category: Compiler, Language
16
18
@@ -26,15 +28,14 @@ source-repository head
26Executable elm-server
28Executable elm-server
27 Main-is: Server.hs
29 Main-is: Server.hs
28 Build-depends: base >=4.2 && <5,
30 Build-depends: base >=4.2 && <5,
31+ bytestring,
32+ cmdargs,
29 containers >= 0.3,
33 containers >= 0.3,
30 directory,
34 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,
38 filepath,
35 filepath,
39− Elm >= 0.10.1,
36+ Elm >= 0.11,
37+ snap-core,
38+ snap-server,
39+ mtl,
40− process
40+ process,
41+ unordered-containers