on master help sign in
merge, and also bump elm-server to 0.12
commit5a5f12f663e479fc27f505c7e8da731c3d1d7740 authorJohn P Mayer Jr <john.p.mayer.jr@gmail.com> dateThu 27 Mar 2014, 23:06 (13 years ago) parente7e6e9e15d87f2
31 files changed+500−584
Elm.cabal+4−4 LICENSE+1−1 changelog.txt → changelog.md+7−4 compiler/Build/Flags.hs+1−1 compiler/Generate/JavaScript.hs+8−5 compiler/Parse/Helpers.hs+5−3 libraries/Automaton.elm.hidden−123 libraries/AutomatonV2.elm.hidden−149 libraries/Basics.elm+27 libraries/Char.elm+2−2 libraries/Debug.elm+2 libraries/Graphics/Input.elm+1−1 libraries/Graphics/Input/Field.elm+63−23 libraries/Http.elm+2−4 libraries/Keyboard.elm+2−4 libraries/Native/Basics.js+3 libraries/Native/Debug.js+5−3 libraries/Native/Graphics/Input.js+3−20 libraries/Native/Regex.js+1−1 libraries/Native/Signal/Http.js+52−49 libraries/Native/Text.js+1−1 libraries/Native/Utils.js+6−1 libraries/Signal.elm+1−1 libraries/String.elm+6−6 libraries/Text.elm+86−13 libraries/elm_dependencies.json+4−3 runtime/HotSwap.js+2−2 runtime/Render/Element.js+78−47 server/LICENSE+1−1 server/Server.hs+112−99 server/elm-server.cabal+14−13
Elm.cabal+4−4
@@ -1,5 +1,5 @@
11Name: Elm
2−Version: 0.11
2+Version: 0.12
33Synopsis: The Elm language module.
44Description: Elm aims to make client-side web-development more pleasant.
55 It is a statically/strongly typed, functional reactive
@@ -14,13 +14,13 @@ License-file: LICENSE
1414
1515Author: Evan Czaplicki
1616Maintainer: info@elm-lang.org
17−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
17+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
1818
1919Category: Compiler, Language
2020
2121Build-type: Custom
2222
23−Extra-source-files: changelog.txt
23+Extra-source-files: changelog.md
2424Data-dir: data
2525Data-files: elm-runtime.js interfaces.data docs.json
2626Cabal-version: >=1.9
@@ -254,7 +254,7 @@ Test-Suite test-elm
254254 Elm,
255255 test-framework,
256256 test-framework-hunit,
257− test-framework-quickcheck2,
257+ test-framework-quickcheck2 >= 0.3,
258258 HUnit,
259259 pretty,
260260 QuickCheck >= 2 && < 3,
LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c) 2012-2013, Evan Czaplicki
1+Copyright (c) 2012-2014, Evan Czaplicki
22
33All rights reserved.
44
changelog.txt → changelog.mdrenamed+7−4
@@ -1,9 +1,11 @@
11
2−## next
2+## 0.12
33
4−Breaking Changes:
4+#### Breaking Changes:
55
6− * Overhaul Graphics.Input library (inspired by @seliopou and Jeff Smits)
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
79 * Overhaul Regex library (inspired by Attila Gazso)
810 * Change syntax for "import open List" to "import List (..)"
911 * Improved JSON format for types generated by elm-doc
@@ -11,8 +13,9 @@ Breaking Changes:
1113 * Revise the semantics of keepWhen and dropWhen to only update when
1214 the filtered signal changes (thanks Max New and Janis Voigtländer)
1315
14−Improvements:
16+#### Improvements:
1517
18+ * Add Graphics.Input.Field for customizable text fields
1619 * Add Trampoline library (thanks to @maxsnew and @timthelion)
1720 * Add Debug library (inspired by @timthelion)
1821 * Drastically improved performance on markdown parsing (thanks to @Dandandan)
compiler/Build/Flags.hs+1−1
@@ -41,4 +41,4 @@ flags = Flags
4141 } &= help "Compile Elm programs to HTML, CSS, and JavaScript."
4242 &= helpArg [explicit, name "help", name "h"]
4343 &= versionArg [explicit, name "version", name "v", summary (show Version.elmVersion)]
44− &= summary ("The Elm Compiler " ++ show Version.elmVersion ++ ", (c) Evan Czaplicki 2011-2013")
44+ &= summary ("The Elm Compiler " ++ show Version.elmVersion ++ ", (c) Evan Czaplicki 2011-2014")
compiler/Generate/JavaScript.hs+8−5
@@ -55,7 +55,7 @@ literal lit =
5555expression :: Expr -> State Int (Expression ())
5656expression (A region expr) =
5757 case expr of
58− Var (V.Raw x) -> return $ ref x
58+ Var (V.Raw x) -> return $ obj x
5959 Literal lit -> return $ literal lit
6060
6161 Range lo hi ->
@@ -87,10 +87,9 @@ expression (A region expr) =
8787 do fields' <- forM fields $ \(f,e) -> do
8888 (,) f <$> expression e
8989 let fieldMap = List.foldl' combine Map.empty fields'
90− return $ ObjectLit () $ (PropId () (var "_"), hidden fieldMap) : visible fieldMap
90+ return $ ObjectLit () $ (prop "_", hidden fieldMap) : visible fieldMap
9191 where
9292 combine r (k,v) = Map.insertWith (++) k [v] r
93− prop = PropId () . var
9493 hidden fs = ObjectLit () . map (prop *** ArrayLit ()) .
9594 Map.toList . Map.filter (not . null) $ Map.map tail fs
9695 visible fs = map (first prop) . Map.toList $ Map.map head fs
@@ -246,7 +245,11 @@ match region mtch =
246245 isLiteral p = case p of
247246 Case.Clause (Right _) _ _ -> True
248247 _ -> False
249− access name = if any isLiteral clauses then ref name else dotSep [name,"ctor"]
248+
249+ access name
250+ | any isLiteral clauses = obj name
251+ | otherwise = dotSep (split name ++ ["ctor"])
252+
250253 format isChars e
251254 | or isChars = InfixExpr () OpAdd e (string "")
252255 | otherwise = e
@@ -310,7 +313,7 @@ generate unsafeModule =
310313 jsExports = assign ("_elm" : names modul ++ ["values"]) (ObjectLit () exs)
311314 where
312315 exs = map entry . filter (not . Help.isOp) $ "_op" : exports modul
313− entry x = (PropId () (var x), ref x)
316+ entry x = (prop x, ref x)
314317
315318 assign path expr =
316319 case path of
compiler/Parse/Helpers.hs+5−3
@@ -82,9 +82,11 @@ innerVarChar :: IParser Char
8282innerVarChar = alphaNum <|> char '_' <|> char '\'' <?> ""
8383
8484makeVar :: IParser Char -> IParser String
85−makeVar p = do v <- (:) <$> p <*> many innerVarChar
86− guard (v `notElem` reserveds)
87− return v
85+makeVar p = do
86+ v <- (:) <$> p <*> many innerVarChar
87+ if v `elem` reserveds
88+ then fail $ "unexpected keyword '" ++ v ++ "', variables cannot be keywords"
89+ else return v
8890
8991reserved :: String -> IParser String
9092reserved word =
libraries/Automaton.elm.hiddendeleted−123
@@ -1,123 +0,0 @@
1−module Automaton ( pure, state, hiddenState, run, step
2− , (<<<), (>>>), combine, count, average
3− ) where
4−
5−{-| This library is a way to package up dynamic behavior. It makes it easier to
6−dynamically create dynamic components. See the [original release
7−notes](http://elm-lang.org/blog/announce/0.5.0.elm) on this library to get a feel for how
8−it can be used.
9−
10−# Create
11−@docs pure, state, hiddenState
12−
13−# Evaluate
14−@docs run, step
15−
16−# Combine
17−@docs (>>>), (<<<), combine
18−
19−# Common Automatons
20−@docs count, average
21−-}
22−
23−import open Basics
24−import Signal (lift,foldp,Signal)
25−import open List
26−import Maybe (Just, Nothing)
27−
28−data Automaton a b = Step (a -> (Automaton a b, b))
29−
30−{-| Run an automaton on a given signal. The automaton steps forward whenever the
31−input signal updates.
32−-}
33−run : Automaton a b -> b -> Signal a -> Signal b
34−run auto base inputs =
35− let step a (Step f, _) = f a
36− in lift (\(x,y) -> y) (foldp step (auto,base) inputs)
37−
38−{-| Step an automaton forward once with a given input. -}
39−step : a -> Automaton a b -> (Automaton a b, b)
40−step a (Step f) = f a
41−
42−{-| Compose two automatons, chaining them together. -}
43−(>>>) : Automaton a b -> Automaton b c -> Automaton a c
44−f >>> g =
45− Step (\a -> let (f', b) = step a f
46− (g', c) = step b g
47− in (f' >>> g', c))
48−
49−{-| Compose two automatons, chaining them together. -}
50−(<<<) : Automaton b c -> Automaton a b -> Automaton a c
51−g <<< f = f >>> g
52−
53−{-| Combine a list of automatons into a single automaton that produces a
54−list.
55−-}
56−combine : [Automaton a b] -> Automaton a [b]
57−combine autos =
58− Step (\a -> let (autos', bs) = unzip (map (step a) autos)
59− in (combine autos', bs))
60−
61−{-| Create an automaton with no memory. It just applies the given function to
62−every input.
63−-}
64−pure : (a -> b) -> Automaton a b
65−pure f = Step (\x -> (pure f, f x))
66−
67−
68−{-| Create an automaton with state. Requires an initial state and a step
69−function to step the state forward. For example, an automaton that counted
70−how many steps it has taken would look like this:
71−
72− count = Automaton a Int
73− count = state 0 (\\_ c -> c+1)
74−
75−It is a stateful automaton. The initial state is zero, and the step function
76−increments the state on every step.
77−-}
78−state : b -> (a -> b -> b) -> Automaton a b
79−state s f = Step (\x -> let s' = f x s
80− in (state s' f, s'))
81−
82−{-| Create an automaton with hidden state. Requires an initial state and a
83−step function to step the state forward and produce an output.
84−-}
85−hiddenState : s -> (a -> s -> (s,b)) -> Automaton a b
86−hiddenState s f = Step (\x -> let (s',out) = f x s
87− in (hiddenState s' f, out))
88−
89−{-| Count the number of steps taken. -}
90−count : Automaton a Int
91−count = state 0 (\_ c -> c + 1)
92−
93−type Queue t = ([t],[t])
94−empty = ([],[])
95−enqueue x (en,de) = (x::en, de)
96−dequeue q = case q of
97− ([],[]) -> Nothing
98− (en,[]) -> dequeue ([], reverse en)
99− (en,hd::tl) -> Just (hd, (en,tl))
100−
101−{-| Computes the running average of the last `n` inputs. -}
102−average : Int -> Automaton Float Float
103−average k =
104− let step n (ns,len,sum) =
105− if len == k then stepFull n (ns,len,sum)
106− else ((enqueue n ns, len+1, sum+n), (sum+n) / (toFloat len+1))
107− stepFull n (ns,len,sum) =
108− case dequeue ns of
109− Nothing -> ((ns,len,sum), 0)
110− Just (m,ns') -> let sum' = sum + n - m
111− in ((enqueue n ns', len, sum'), sum' / toFloat len)
112− in hiddenState (empty,0,0) step
113−
114−
115−{-- TODO(evancz): See the following papers for ideas on how to make this
116−library faster and better:
117−
118−- Functional Reactive Programming, Continued
119−- Causal commutative arrows and their optimization
120−
121−Speeding things up is a really low priority. Language features and
122−libraries with nice APIs and are way more important!
123−--}
libraries/AutomatonV2.elm.hiddendeleted−149
@@ -1,149 +0,0 @@
1−module AutomatonV2 where
2−{-| This library is a way to package up dynamic behavior. It makes it easier to
3−dynamically create dynamic components. See the [original release
4−notes](/blog/announce/version-0.5.0.elm) on this library to get a feel for how
5−it can be used.
6−-}
7−
8−import open Basics
9−import Signal (lift,foldp,Signal)
10−import open List
11−import Maybe (Just, Nothing)
12−
13−data Automaton input output = Pure (input -> output)
14− | Stateful state (input -> state -> (output,state))
15−
16−-- The basics
17−
18−-- AFRP name: arr
19−pure: (i -> o) -> Automaton i o
20−pure = Pure
21−
22−-- AFRP name: >>>
23−andThen: Automaton i inter -> Automaton inter o -> Automaton i o
24−andThen first second =
25− case first of
26− Pure f -> case second of -- f is the function from first
27− Pure s -> Pure (s . f) -- s is the function from second
28− Stateful b s -> Stateful b (\i -> s (f i)) -- b is the base state from second
29− Stateful fb f -> case second of -- fb is the base state from first
30− Pure s -> Stateful fb (\i st -> -- sb is the base state from second
31− let (inter, st') = f i st -- i is the input
32− in (s inter, st')) -- st is the input state
33− Stateful sb s -> Stateful (fb, sb) (\i (fst, sst) -> -- inter is the intermediate value
34− let (inter, fst') = f i fst -- st' is the new state
35− (o, sst') = s inter sst -- fst and sst are the state of first and second
36− in (o, (fst', sst'))) -- ect...
37−
38−-- AFRP name: first
39−extendDown: Automaton i o -> Automaton (i,extra) (o,extra)
40−extendDown auto = case auto of
41− Pure fun -> Pure (\(i,extra) -> (fun i, extra))
42− Stateful base fun -> Stateful base (\(i,extra) s -> (fun i s, extra))
43−
44−-- AFRP name: loop
45−loop: s -> Automaton (i,s) (o,s) -> Automaton i o
46−loop base auto = case auto of
47− Pure fun -> Stateful base (curry fun)
48− Stateful base2 fun -> -- fun: (i, s) -> s2 -> ((o, s), s2)
49− let newFun = (\i (s,s2) ->
50− let ((o, s'), s2') = fun (i, s) s2
51− in (o, (s', s2'))) -- newFun: i -> (s, s2) -> (o, (s, s2))
52− in Stateful (base, base2) newFun
53−
54−-- Run an automaton on a given signal
55−run: Automaton i o -> o -> Signal i -> Signal o
56−run auto baseOut input = case auto of
57− Pure fun -> lift fun input
58− Stateful base fun -> lift fst
59− (foldp (\i (o, s) -> fun i s)
60− (baseOut, base) input)
61−
62−
63−-- Other frequently used functions/operators
64−
65−-- Create an automaton with state. Requires an initial state and a step
66−-- function to step the state forward. For example, an automaton that counted
67−-- how many steps it has taken would look like this:
68−--
69−-- count = Automaton a Int
70−-- count = state 0 (\\_ c -> c+1)
71−--
72−-- It is a stateful automaton. The initial state is zero, and the step function
73−-- increments the state on every step.
74−state : s -> (i -> s -> s) -> Automaton i s
75−state base fun = loop base (pure (\(i,s) ->
76− let s' = fun i s
77− in (s',s')))
78−
79−-- Create an automaton with hidden state. Requires an initial state and a
80−-- step function to step the state forward and produce an output.
81−hiddenState : s -> (i -> s -> (s,o)) -> Automaton i o
82−hiddenState base fun = loop base (pure (\(i,s) ->
83− let (o,s') = fun i s
84− in (s',o)))
85−
86−-- AFRP name: second
87−extendUp: Automaton i o -> Automaton (extra,i) (extra,o)
88−extendUp auto =
89− let swap (a, b) = (b, a)
90− in pure swap `andThen` extendDown auto `andThen` pure swap
91−
92−-- (parallel composition)
93−pair: Automaton i1 o1 -> Automaton i2 o2 -> Automaton (i1,i2) (o1,o2)
94−pair f g = extendDown f `andThen` extendUp g
95−
96−branch : Automaton i o1 -> Automaton i o2 -> Automaton i (o1,o2)
97−branch f g =
98− let double = pure (\i -> (i,i))
99− in double `andThen` pair f g
100−
101−combi: Automaton i o -> Automaton i [o] -> Automaton i [o]
102−combi a1 a2 = (a1 `branch` a2) `andThen` pure (uncurry (::))
103−
104−-- Combine a list of automatons into a single automaton that produces a list.
105−combine : [Automaton i o] -> Automaton i [o]
106−combine autos =
107− let l = length autos
108− in if l == 0
109− then pure (\_ -> [])
110− else foldr combi (last autos `andThen` pure (\a -> [a])) (take (l-1) autos)
111−
112−
113−-- Examples of automata
114−
115−-- Count the number of steps taken.
116−count : Automaton a Int
117−count = state 0 (\_ c -> c + 1)
118−
119−type Queue t = ([t],[t])
120−empty = ([],[])
121−enqueue x (en,de) = (x::en, de)
122−dequeue q = case q of
123− ([],[]) -> Nothing
124− (en,[]) -> dequeue ([], reverse en)
125− (en,hd::tl) -> Just (hd, (en,tl))
126−
127−-- Computes the running average of the last `n` inputs.
128−average : Int -> Automaton Float Float
129−average k =
130− let step n (ns,len,sum) =
131− if len == k then stepFull n (ns,len,sum)
132− else ((enqueue n ns, len+1, sum+n), (sum+n) / (toFloat len+1))
133− stepFull n (ns,len,sum) =
134− case dequeue ns of
135− Nothing -> ((ns,len,sum), 0)
136− Just (m,ns') -> let sum' = sum + n - m
137− in ((enqueue n ns', len, sum'), sum' / toFloat len)
138− in hiddenState (empty,0,0) step
139−
140−
141−{-- TODO(evancz): See the following papers for ideas on how to make this
142−library faster and better:
143−
144−- Functional Reactive Programming, Continued -- took some inspirations from this paper (Apanatshka)
145−- Causal commutative arrows and their optimization
146−
147−Speeding things up is a really low priority. Language features and
148−libraries with nice APIs and are way more important!
149−--}
libraries/Basics.elm+27
@@ -39,6 +39,9 @@ which happen to be radians.
3939# Polar Coordinates
4040@docs toPolar, fromPolar
4141
42+# Floating Point Checks
43+@docs isNaN, isInfinite
44+
4245# Tuples
4346@docs fst, snd
4447
@@ -271,6 +274,30 @@ ceiling = Native.Basics.ceiling
271274toFloat : Int -> Float
272275toFloat = Native.Basics.toFloat
273276
277+{- | Determines whether a float is an undefined or unrepresentable number.
278+NaN stands for *not a number* and it is [a standardized part of floating point
279+numbers](http://en.wikipedia.org/wiki/NaN).
280+
281+ isNaN (0/0) == True
282+ isNaN (sqrt -1) == True
283+ isNaN (1/0) == False -- infinity is a number
284+ isNaN 1 == False
285+-}
286+isNaN : Float -> Bool
287+isNaN = Native.Basics.isNaN
288+
289+{- | Determines whether a float is positive or negative infinity.
290+
291+ isInfinite (0/0) == False
292+ isInfinite (sqrt -1) == False
293+ isInfinite (1/0) == True
294+ isInfinite 1 == False
295+
296+Notice that NaN is not infinite! For float `n` to be finite implies that
297+`not (isInfinite n || isNaN n)` evaluates to `True`.
298+-}
299+isInfinite : Float -> Bool
300+isInfinite = Native.Basics.isInfinite
274301
275302-- Function Helpers
276303
libraries/Char.elm+2−2
@@ -52,8 +52,8 @@ toLocaleLower = Native.Char.toLocaleLower
5252
5353type KeyCode = Int
5454
55−{-| Convert to unicode. Used with the [`Keyboard`](/docs/Keyboard.elm)
56−library, which expects the input to be uppercase. -}
55+{-| Convert to unicode. Used with the `Keyboard` library, which expects the
56+input to be uppercase. -}
5757toCode : Char -> KeyCode
5858toCode = Native.Char.toCode
5959
libraries/Debug.elm+2
@@ -1,6 +1,8 @@
11module Debug where
22{-| This library is for investigating bugs or performance problems. It should
33*not* be used in production code.
4+
5+@docs log
46-}
57
68import Native.Debug
libraries/Graphics/Input.elm+1−1
@@ -118,7 +118,7 @@ favorite British sport:
118118
119119 data Sport = Football | Cricket | Snooker
120120
121− sport : Input Sport
121+ sport : Input (Maybe Sport)
122122 sport = input Nothing
123123
124124 sportDropDown : Element
libraries/Graphics/Input/Field.elm+63−23
@@ -1,7 +1,8 @@
11module Graphics.Input.Field where
2−{-| This library specifically addresses text fields. It uses the same general
3−approach as the [`Graphics.Input`](Graphics-Input) module for describing an
4−`Input`, so this library focuses on creating and styling text fields.
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.
56
67# Create Fields
78@docs field, password, email
@@ -20,21 +21,33 @@ import Graphics.Input (Input, Handle)
2021import Native.Graphics.Input
2122import Text
2223
23−{-| Easily create uniform dimensions:
24+{-| Create uniform dimensions:
2425
2526 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
2632-}
2733uniformly : Int -> Dimensions
2834uniformly n = Dimensions n n n n
2935
3036{-| For setting dimensions of a fields padding or border. The left, right, top,
31−and bottom may all have different sizes.
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 }
3242-}
3343type Dimensions = { left:Int, right:Int, top:Int, bottom:Int }
3444
3545{-| A field can have a outline around it. This lets you set its color, width,
3646and radius. The radius allows you to round the corners of your field. Set the
37−width to zero to make it invisible.
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 }
3851-}
3952type Outline = { color:Color, width:Dimensions, radius:Int }
4053
@@ -42,8 +55,12 @@ type Outline = { color:Color, width:Dimensions, radius:Int }
4255noOutline : Outline
4356noOutline = Outline Color.grey (uniformly 0) 0
4457
45−{-| When a field is selected, it has an highlight around it by default. Set the
46−width of the `Highlight` to zero to make it go away.
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 }
4764-}
4865type Highlight = { color:Color, width:Int }
4966
@@ -51,10 +68,18 @@ type Highlight = { color:Color, width:Int }
5168noHighlight : Highlight
5269noHighlight = Highlight Color.blue 0
5370
54−{-| Describes the style of a text box. The `style` field describes the style
55−of the text itself. The `outline` field describes the glowing blue outline that
56−shows up when the field has focus. Turn off `outline` by setting its width to
57−zero. The
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).
5883-}
5984type Style =
6085 { padding : Dimensions
@@ -77,27 +102,32 @@ defaultStyle =
77102
78103{-| Represents the current content of a text field. For example:
79104
80− Content "She sells sea shells" (Selection 0 3 Backward)
105+ content = Content "She sells sea shells" (Selection 0 3 Backward)
81106
82−This means the user highlighted the substring `"She"` backwards.
107+This means the user highlighted the substring `"She"` backwards. The value of
108+`content.string` is `"She sells sea shells"`.
83109-}
84110type Content = { string:String, selection:Selection }
85111
86112{-| The selection within a text field. `start` is never greater than `end`:
87113
88− Selection 0 0 Forward -- cursor precedes all characters
114+ Selection 0 0 Forward -- cursor precedes all characters
89115
90− Selection 5 9 Backward -- highlighting characters starting after
91− -- the 5th and ending after the 9th
116+ Selection 5 9 Backward -- highlighting characters starting after
117+ -- the 5th and ending after the 9th
92118-}
93119type Selection = { start:Int, end:Int, direction:Direction }
94120
95−{-| The direction of selection.-}
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+-}
96126data Direction = Forward | Backward
97127
98128{-| A field with no content:
99129
100− Content "" (Selection 0 0 Forward)
130+ Content "" (Selection 0 0 Forward)
101131-}
102132noContent : Content
103133noContent = Content "" (Selection 0 0 Forward)
@@ -110,20 +140,30 @@ to match what they have entered.
110140 name = input noContent
111141
112142 nameField : Signal Element
113− nameField = field name.handle id defaultStyle "Name" <~ name.signal
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.
114154-}
115−field : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
155+field : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
116156field = Native.Graphics.Input.field
117157
118158{-| Same as `field` but the UI element blocks out each characters. -}
119−password : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
159+password : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
120160password = Native.Graphics.Input.password
121161
122162{-| Same as `field` but it adds an annotation that this field is for email
123163addresses. This is helpful for auto-complete and for mobile users who may
124164get a custom keyboard with an `@` and `.com` button.
125165-}
126−email : Handle a -> (Content -> a) -> Style -> String -> Content -> Element
166+email : Style -> Handle a -> (Content -> a) -> String -> Content -> Element
127167email = Native.Graphics.Input.email
128168
129169-- area : Handle a -> (Content -> a) -> Handle b -> ((Int,Int) -> b) -> (Int,Int) -> String -> Content -> Element
libraries/Http.elm+2−4
@@ -1,8 +1,6 @@
11module Http where
2−
3−{-| A library for asynchronous HTTP requests. See the
4−[WebSocket](http://elm-lang.org/docs/WebSocket.elm) library if
5−you have very strict latency requirements.
2+{-| A library for asynchronous HTTP requests. See the `WebSocket`
3+library if you have very strict latency requirements.
64
75# Sending Requests
86@docs send, sendGet
libraries/Keyboard.elm+2−4
@@ -1,5 +1,4 @@
11module Keyboard where
2−
32{-| Library for working with keyboard input.
43
54# Representing Keys
@@ -20,9 +19,8 @@ import Signal (Signal)
2019import Native.Keyboard
2120
2221{-| Type alias to make it clearer what integers are supposed to represent
23−in this library. Use [`Char.toCode`](docs/Char.elm#toCode) and
24−[`Char.fromCode`](/docs/Char.elm#fromCode) to convert key codes to characters.
25−Use the uppercase character with `toCode`.
22+in this library. Use `Char.toCode` and `Char.fromCode` to convert key codes
23+to characters. Use the uppercase character with `toCode`.
2624-}
2725type KeyCode = Int
2826
libraries/Native/Basics.js+3
@@ -19,6 +19,7 @@ Elm.Native.Basics.make = function(elm) {
1919 return Utils.cmp(n,lo) < 0 ? lo : Utils.cmp(n,hi) > 0 ? hi : n; }
2020 function xor(a,b) { return a !== b; }
2121 function not(b) { return !b; }
22+ function isInfinite(n) { return n === Infinity || n === -Infinity }
2223
2324 function truncate(n) { return n|0; }
2425
@@ -53,6 +54,8 @@ Elm.Native.Basics.make = function(elm) {
5354 floor:Math.floor,
5455 round:Math.round,
5556 toFloat:function(x) { return x; },
57+ isNaN:isNaN,
58+ isInfinite:isInfinite
5659 };
5760
5861 return elm.Native.Basics.values = basics;
libraries/Native/Debug.js+5−3
@@ -7,10 +7,12 @@ Elm.Native.Debug.make = function(elm) {
77 var show = Elm.Native.Show.make(elm).show;
88
99 function log(tag,value) {
10− if (process && process.stdout) {
11− process.stdout.write(tag + ': ' + show(value));
10+ var msg = tag + ': ' + show(value);
11+ var process = process || {};
12+ if (process.stdout) {
13+ process.stdout.write(msg);
1214 } else {
13− console.log(tag + ': ' + show(value));
15+ console.log(msg);
1416 }
1517 return value;
1618 }
libraries/Native/Graphics/Input.js+3−20
@@ -273,24 +273,6 @@ Elm.Native.Graphics.Input.make = function(elm) {
273273 field.elm_handler = model.handler;
274274 field.elm_old_value = field.value;
275275
276− function keyUpdate(event) {
277− var curr = field.value;
278− var next = (curr.slice(0, field.selectionStart) +
279− String.fromCharCode(event.keyCode) +
280− curr.slice(field.selectionEnd));
281− var pos = field.selectionEnd + 1;
282− elm.notify(field.elm_signal.id, field.elm_handler({
283− _:{},
284− string: JS.toString(next),
285− selection: {
286− start: pos,
287− end: pos,
288− direction: { ctor:'Forward' }
289− },
290− }));
291− event.preventDefault();
292− }
293−
294276 function inputUpdate(event) {
295277 var curr = field.elm_old_value;
296278 var next = field.value;
@@ -307,6 +289,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
307289 _:{},
308290 string: JS.toString(next),
309291 selection: {
292+ _:{},
310293 start: start,
311294 end: end,
312295 direction: { ctor: direction }
@@ -320,6 +303,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
320303 _:{},
321304 string: field.value,
322305 selection: {
306+ _:{},
323307 start: field.selectionStart,
324308 end: field.selectionEnd,
325309 direction: { ctor: direction }
@@ -334,7 +318,6 @@ Elm.Native.Graphics.Input.make = function(elm) {
334318 mouseUpdate(event);
335319 elm.node.removeEventListener('mouseup', mouseup)
336320 }
337− field.addEventListener('keypress', keyUpdate);
338321 field.addEventListener('input', inputUpdate);
339322 field.addEventListener('mousedown', mousedown);
340323 field.addEventListener('focus', function() {
@@ -367,7 +350,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
367350 }
368351
369352 function mkField(type) {
370− function field(signal, handler, style, placeHolder, content) {
353+ function field(style, signal, handler, placeHolder, content) {
371354 var padding = style.padding;
372355 var outline = style.outline.width;
373356 var adjustWidth = padding.left + padding.right + outline.left + outline.right;
libraries/Native/Regex.js+1−1
@@ -20,7 +20,7 @@ Elm.Native.Regex.make = function(elm) {
2020 }
2121
2222 function contains(re, string) {
23− return re.test(JS.fromString(string));
23+ return JS.fromString(string).match(re) !== null;
2424 }
2525
2626 function find(n, re, str) {
libraries/Native/Signal/Http.js+52−49
@@ -1,61 +1,64 @@
11Elm.Native.Http = {};
22Elm.Native.Http.make = function(elm) {
33
4− elm.Native = elm.Native || {};
5− elm.Native.Http = elm.Native.Http || {};
6− if (elm.Native.Http.values) return elm.Native.Http.values;
4+ elm.Native = elm.Native || {};
5+ elm.Native.Http = elm.Native.Http || {};
6+ if (elm.Native.Http.values) return elm.Native.Http.values;
77
8+ var JS = Elm.JavaScript.make(elm);
9+ var List = Elm.List.make(elm);
10+ var Signal = Elm.Signal.make(elm);
811
9− var JS = Elm.JavaScript.make(elm);
10− var List = Elm.List.make(elm);
11− var Signal = Elm.Signal.make(elm);
12+ function registerReq(queue,responses) {
13+ return function(req) {
14+ if (req.url.length > 0) {
15+ sendReq(queue,responses,req);
16+ }
17+ };
18+ }
1219
20+ function updateQueue(queue,responses) {
21+ if (queue.length > 0) {
22+ elm.notify(responses.id, queue[0].value);
23+ if (queue[0].value.ctor !== 'Waiting') {
24+ queue.shift();
25+ setTimeout(function() { updateQueue(queue,responses); }, 0);
26+ }
27+ }
28+ }
1329
14− function registerReq(queue,responses) { return function(req) {
15− if (req.url.ctor !== '[]') { sendReq(queue,responses,req); }
16− };
17− }
30+ function sendReq(queue,responses,req) {
31+ var response = { value: { ctor:'Waiting' } };
32+ queue.push(response);
1833
19− function updateQueue(queue,responses) {
20− if (queue.length > 0) {
21− elm.notify(responses.id, queue[0].value);
22− if (queue[0].value.ctor !== 'Waiting') {
23− queue.shift();
24− setTimeout(function() { updateQueue(queue,responses); }, 0);
25− }
26− }
27− }
28−
29− function sendReq(queue,responses,req) {
30− var response = { value: { ctor:'Waiting' } };
31− queue.push(response);
32−
33− var request = null;
34− if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
35− if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
36− request.onreadystatechange = function(e) {
37− if (request.readyState === 4) {
38− response.value = (request.status >= 200 && request.status < 300 ?
39− { ctor:'Success', _0:JS.toString(request.responseText) } :
40− { ctor:'Failure', _0:request.status, _1:JS.toString(request.statusText) });
41− setTimeout(function() { updateQueue(queue,responses); }, 0);
42− }
43− };
44− request.open(JS.fromString(req.verb), JS.fromString(req.url), true);
45− function setHeader(pair) {
46− request.setRequestHeader( JS.fromString(pair._0), JS.fromString(pair._1) );
47− }
48− List.map(setHeader)(req.headers);
49− request.send(JS.fromString(req.body));
50− }
34+ var request = (window.ActiveXObject
35+ ? new ActiveXObject("Microsoft.XMLHTTP")
36+ : new XMLHttpRequest());
5137
52− function send(requests) {
53− var responses = Signal.constant(elm.Http.values.Waiting);
54− var sender = A2( Signal.lift, registerReq([],responses), requests );
55− function f(x) { return function(y) { return x; } }
56− return A3( Signal.lift2, f, responses, sender );
57− }
38+ request.onreadystatechange = function(e) {
39+ if (request.readyState === 4) {
40+ response.value = (request.status >= 200 && request.status < 300 ?
41+ { ctor:'Success', _0:JS.toString(request.responseText) } :
42+ { ctor:'Failure', _0:request.status, _1:JS.toString(request.statusText) });
43+ setTimeout(function() { updateQueue(queue,responses); }, 0);
44+ }
45+ };
46+ request.open(JS.fromString(req.verb), JS.fromString(req.url), true);
47+ function setHeader(pair) {
48+ request.setRequestHeader( JS.fromString(pair._0), JS.fromString(pair._1) );
49+ }
50+ List.map(setHeader)(req.headers);
51+ request.send(JS.fromString(req.body));
52+ }
5853
59− return elm.Native.Http.values = {send:send};
54+ function send(requests) {
55+ var responses = Signal.constant(elm.Http.values.Waiting);
56+ var sender = A2( Signal.lift, registerReq([],responses), requests );
57+ function f(x) { return function(y) { return x; } }
58+ return A3( Signal.lift2, f, responses, sender );
59+ }
6060
61+ return elm.Native.Http.values = {
62+ send:send
63+ };
6164};
libraries/Native/Text.js+1−1
@@ -57,7 +57,7 @@ Elm.Native.Text.make = function(elm) {
5757 var typefaces = List.toArray(list);
5858 for (var i = typefaces.length; i--; ) {
5959 var typeface = typefaces[i];
60− if (typeface.contains(' ')) {
60+ if (typeface.indexOf(' ') > -1) {
6161 typefaces[i] = "'" + typeface + "'";
6262 }
6363 }
libraries/Native/Utils.js+6−1
@@ -9,7 +9,12 @@ Elm.Native.Utils.make = function(elm) {
99 if (x === y) return true;
1010 if (typeof x === "object") {
1111 var c = 0;
12− for (var i in x) { ++c; if (!eq(x[i],y[i])) return false; }
12+ for (var i in x) {
13+ ++c;
14+ if (!eq(x[i],y[i])) {
15+ return false;
16+ }
17+ }
1318 return c === Object.keys(y).length;
1419 }
1520 if (typeof x === 'function') {
libraries/Signal.elm+1−1
@@ -10,7 +10,7 @@ past-dependence.
1010
1111Some useful functions for working with time (e.g. setting FPS) and combining
1212signals and time (e.g. delaying updates, getting timestamps) can be found in
13−the [`Time`](/docs/Signal/Time.elm) library.
13+the `Time` library.
1414
1515# Combine
1616@docs constant, lift, lift2, merge, merges, combine
libraries/String.elm+6−6
@@ -174,18 +174,18 @@ pad = Native.String.pad
174174
175175{-| Pad a string on the left until it has a given length.
176176
177− pad 5 '.' "1" == "....1"
178− pad 5 '.' "11" == "...11"
179− pad 5 '.' "121" == "..121"
177+ padLeft 5 '.' "1" == "....1"
178+ padLeft 5 '.' "11" == "...11"
179+ padLeft 5 '.' "121" == "..121"
180180-}
181181padLeft : Int -> Char -> String -> String
182182padLeft = Native.String.padLeft
183183
184184{-| Pad a string on the right until it has a given length.
185185
186− pad 5 '.' "1" == "1...."
187− pad 5 '.' "11" == "11..."
188− pad 5 '.' "121" == "121.."
186+ padRight 5 '.' "1" == "1...."
187+ padRight 5 '.' "11" == "11..."
188+ padRight 5 '.' "121" == "121.."
189189-}
190190padRight : Int -> Char -> String -> String
191191padRight = Native.String.padRight
libraries/Text.elm+86−13
@@ -1,15 +1,21 @@
11module Text where
2−
3−{-| Functions for displaying text
2+{-| A library for styling and displaying text. Whlie the `String` library
3+focuses on representing and manipulating strings of character strings, the
4+`Text` library focuses on how those strings should look on screen. It lets
5+you make text bold or italic, set the typeface, set the text size, etc.
46
57# Creating Text
68@docs toText
79
810# Creating Elements
11+
12+Each of the following functions places `Text` into a box. The function you use
13+determines the alignment of the text.
14+
915@docs leftAligned, rightAligned, centered, justified
1016
1117# Links and Style
12−@docs link, Style, style, Line, defaultStyle
18+@docs link, Style, style, defaultStyle, Line
1319
1420# Convenience Functions
1521
@@ -35,9 +41,28 @@ import Native.Text
3541
3642data Text = Text
3743
44+{-| Styles for lines on text. This allows you to add an underline, an overline,
45+or a strike out text:
46+
47+ line Under (toText "underline")
48+ line Over (toText "overline")
49+ line Through (toText "strike out")
50+-}
3851data Line = Under | Over | Through
3952
40−{-| Representation of all the ways you can style `Text`.
53+{-| Representation of all the ways you can style `Text`. If the `typeface` list
54+is empty or the `height` is `Nothing`, the users will fall back on their
55+browser's default settings. The following `Style` is black, 16 pixel tall,
56+underlined, and Times New Roman (assuming that typeface is available on the
57+user's computer):
58+
59+ { typeface = [ "Times New Roman", "serif" ]
60+ , height = Just 16
61+ , color = black
62+ , bold = False
63+ , italic = False
64+ , line = Just Under
65+ }
4166-}
4267type Style =
4368 { typeface : [String]
@@ -69,11 +94,19 @@ defaultStyle =
6994 , line = Nothing
7095 }
7196
72−{-| Convert a string into text which can be styled and displayed. -}
97+{-| Convert a string into text which can be styled and displayed. To show the
98+string `"Hello World!"` on screen in italics, you could say:
99+
100+ main = leftAligned (italic (toText "Hello World!"))
101+-}
73102toText : String -> Text
74103toText = Native.Text.toText
75104
76−{-| Set the style of some text.
105+{-| Set the style of some text. For example, if you design a `Style` called
106+`footerStyle` that is specifically for the bottom of your page, you could apply
107+it to text like this:
108+
109+ style footerStyle (toText "the old prince / 2007")
77110-}
78111style : Style -> Text -> Text
79112style = Native.Text.style
@@ -82,53 +115,93 @@ style = Native.Text.style
82115
83116 ["helvetica","arial","sans-serif"]
84117
85−Not everyone has access to the same typefaces, so rendering will use the first
86−typeface in the list that is found on the user's computer. If there are no
87−matches, it will use their default typeface. Works the same as the CSS
118+Not every browser has access to the same typefaces, so rendering will use the
119+first typeface in the list that is found on the user's computer. If there are
120+no matches, it will use their default typeface. This works the same as the CSS
88121font-family property.
89122-}
90123typeface : [String] -> Text -> Text
91124typeface = Native.Text.typeface
92125
93−{-| Switch to a monospace typeface. Good for code snippets. -}
126+{-| Switch to a monospace typeface. Good for code snippets.
127+
128+ monospace (toText "foldl (+) 0 [1,2,3]")
129+-}
94130monospace : Text -> Text
95131monospace = Native.Text.monospace
96132
97−{-| Create a link.
133+{-| Create a link by providing a URL and the text of the link:
98134
99135 link "http://elm-lang.org" (toText "Elm Website")
100136-}
101137link : String -> Text -> Text
102138link = Native.Text.link
103139
140+{-| Set the height of some text:
141+
142+ height 40 (toText "Title")
143+-}
104144height : Float -> Text -> Text
105145height = Native.Text.height
106146
147+{-| Set the color of some text:
148+
149+ color red (toText "Red")
150+-}
107151color : Color -> Text -> Text
108152color = Native.Text.color
109153
154+{-| Make text bold:
155+
156+ toText "sometimes you want " ++ bold (toText "emphasis")
157+-}
110158bold : Text -> Text
111159bold = Native.Text.bold
112160
161+{-| Make text italic:
162+
163+ toText "make it " ++ italic (toText "important")
164+-}
113165italic : Text -> Text
114166italic = Native.Text.italic
115167
168+{-| Put lines on text:
169+
170+ line Under (toText "underlined")
171+ line Over (toText "overlined")
172+ line Through (toText "strike out")
173+-}
116174line : Line -> Text -> Text
117175line = Native.Text.line
118176
177+{-| `Text` is aligned along the left side of the text block. This is sometimes
178+known as *ragged right*.
179+-}
119180leftAligned : Text -> Element
120181leftAligned = Native.Text.leftAligned
121182
183+{-| `Text` is aligned along the right side of the text block. This is sometimes
184+known as *ragged left*.
185+-}
122186rightAligned : Text -> Element
123187rightAligned = Native.Text.rightAligned
124188
189+{-| `Text` is centered in the text block. There is equal spacing on either side
190+of a line of text.
191+-}
125192centered : Text -> Element
126193centered = Native.Text.centered
127194
195+{-| `Text` is aligned along the left and right sides of the text block. Word
196+spacing is adjusted to make this possible.
197+-}
128198justified : Text -> Element
129199justified = Native.Text.justified
130200
131−{-| Display a plain string. -}
201+{-| Display a string with no styling:
202+
203+ plainText string = leftAligned (toText string)
204+-}
132205plainText : String -> Element
133206plainText str =
134207 leftAligned (toText str)
@@ -140,7 +213,7 @@ markdown = Native.Text.markdown
140213{-| Convert anything to its textual representation and make it displayable in
141214the browser:
142215
143− asText == text . monospace . show
216+ asText value = text (monospace (show value))
144217
145218Excellent for debugging.
146219-}
libraries/elm_dependencies.json+4−3
@@ -1,9 +1,9 @@
1−{ "version": "0.11"
1+{ "version": "0.12"
22, "summary": "Elm's standard libraries"
3−, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.11, you should be using version 0.11 of the standard libraries."
3+, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.12, you should be using version 0.12 of the standard libraries."
44, "license": "BSD3"
55, "repository": "http://github.com/evancz/Elm.git"
6−, "elm-version": "0.11"
6+, "elm-version": "0.12"
77, "dependencies": {}
88, "exposed-modules":
99 [ "Basics"
@@ -18,6 +18,7 @@
1818 , "Graphics.Collage"
1919 , "Graphics.Input"
2020 , "Graphics.WebGL"
21+ , "Graphics.Input.Field"
2122 , "Http"
2223 , "JavaScript"
2324 , "JavaScript.Experimental"
runtime/HotSwap.js+2−2
@@ -5,8 +5,8 @@
55// structure.
66ElmRuntime.swap = function(from, to) {
77 function similar(nodeOld,nodeNew) {
8− idOkay = nodeOld.id === nodeNew.id;
9− lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
8+ var idOkay = nodeOld.id === nodeNew.id;
9+ var lengthOkay = nodeOld.kids.length === nodeNew.kids.length;
1010 return idOkay && lengthOkay;
1111 }
1212 function swap(nodeOld,nodeNew) {
runtime/Render/Element.js+78−47
@@ -10,15 +10,13 @@ var newElement = Utils.newElement, extract = Utils.extract,
1010function setProps(elem, e) {
1111 var props = elem.props;
1212 var element = elem.element;
13− if (element.adjustWidth) {
14− props.width -= element.adjustWidth;
15− }
16− if (element.adjustHeight) {
17− props.height -= element.adjustHeight;
13+ var width = props.width - (element.adjustWidth || 0);
14+ var height = props.height - (element.adjustHeight || 0);
15+ e.style.width = (width |0) + 'px';
16+ e.style.height = (height|0) + 'px';
17+ if (props.opacity !== 1) {
18+ e.style.opacity = props.opacity;
1819 }
19− e.style.width = (props.width |0) + 'px';
20− e.style.height = (props.height|0) + 'px';
21− if (props.opacity !== 1) { e.style.opacity = props.opacity; }
2220 if (props.color.ctor === 'Just') {
2321 e.style.backgroundColor = extract(props.color._0);
2422 }
@@ -36,35 +34,59 @@ function setProps(elem, e) {
3634 e.appendChild(a);
3735 }
3836 if (props.hover.ctor !== '_Tuple0') {
39− e.style.pointerEvents = 'auto';
40− e.elm_hover_handler = props.hover;
41− e.elm_hover_count = 0;
42− e.addEventListener('mouseover', function() {
43− if (e.elm_hover_count++ > 0) return;
44− var handler = e.elm_hover_handler;
45− if (handler !== null) {
46− handler(true);
47− }
48− });
49− e.addEventListener('mouseout', function(evt) {
50− if (e.contains(evt.toElement || evt.relatedTarget)) return;
51− e.elm_hover_count = 0;
52− var handler = e.elm_hover_handler;
53− if (handler !== null) {
54− handler(false);
55− }
56− });
37+ addHover(e, props.hover);
5738 }
5839 if (props.click.ctor !== '_Tuple0') {
59− e.style.pointerEvents = 'auto';
60− e.elm_click_handler = props.click;
61− e.addEventListener('click', function() {
62− e.elm_click_handler(Tuple0);
63− });
40+ addClick(e, props.click);
6441 }
6542 return e;
6643}
6744
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+
6890function image(props, img) {
6991 switch (img._0.ctor) {
7092 case 'Plain': return plainImage(img._3);
@@ -151,7 +173,12 @@ function toPos(pos) {
151173
152174// must clear right, left, top, bottom, and transform
153175// before calling this function
154−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+
155182 e.style.position = 'absolute';
156183 e.style.margin = 'auto';
157184 var transform = '';
@@ -171,7 +198,7 @@ function setPos(pos,w,h,e) {
171198
172199function container(pos,elem) {
173200 var e = render(elem);
174− setPos(pos, elem.props.width, elem.props.height, e);
201+ setPos(pos, elem, e);
175202 var div = newElement('div');
176203 div.style.position = 'relative';
177204 div.style.overflow = 'hidden';
@@ -302,7 +329,7 @@ function update(node, curr, next) {
302329 break;
303330 case "Container":
304331 update(node.firstChild, currE._1, nextE._1);
305− setPos(nextE._0, nextE._1.props.width, nextE._1.props.height, node.firstChild);
332+ setPos(nextE._0, nextE._1, node.firstChild);
306333 break;
307334 case "Custom":
308335 if (currE.type === nextE.type) {
@@ -320,15 +347,15 @@ function updateProps(node, curr, next) {
320347 var currP = curr.props;
321348 var e = node;
322349 var element = next.element;
323− if (element.adjustWidth) {
324− props.width -= element.adjustWidth;
350+ var width = props.width - (element.adjustWidth || 0);
351+ var height = props.height - (element.adjustHeight || 0);
352+ if (width !== currP.width) {
353+ e.style.width = (width|0) + 'px';
325354 }
326− if (element.adjustHeight) {
327− props.height -= element.adjustHeight;
355+ if (height !== currP.height) {
356+ e.style.height = (height|0) + 'px';
328357 }
329− if (props.width !== currP.width) e.style.width = (props.width |0) + 'px';
330− if (props.height !== currP.height) e.style.height = (props.height|0) + 'px';
331− if (props.opacity !== 1 && props.opacity !== currP.opacity) {
358+ if (props.opacity !== currP.opacity) {
332359 e.style.opacity = props.opacity;
333360 }
334361 var nextColor = (props.color.ctor === 'Just' ?
@@ -355,17 +382,21 @@ function updateProps(node, curr, next) {
355382 }
356383
357384 // update hover handlers
358− if (props.hover.ctor !== '_Tuple0') {
359− e.elm_hover_handler = props.hover;
385+ if (props.hover.ctor === '_Tuple0') {
386+ removeHover(e);
360387 } else if (e.elm_hover_handler) {
361− e.elm_hover_handler = null;
388+ e.elm_hover_handler = props.hover;
389+ } else {
390+ addHover(e, props.hover);
362391 }
363392
364393 // update click handlers
365− if (props.click.ctor !== '_Tuple0') {
366− e.elm_click_handler = props.click;
394+ if (props.click.ctor === '_Tuple0') {
395+ removeClick(e);
367396 } else if (e.elm_click_handler) {
368− e.elm_click_handler = null;
397+ e.elm_click_handler = props.click;
398+ } else {
399+ addClick(e, props.click);
369400 }
370401}
371402
server/LICENSE+1−1
@@ -1,4 +1,4 @@
1−Copyright (c)2012-2013, Evan Czaplicki
1+Copyright (c)2012-2014, Evan Czaplicki
22
33All rights reserved.
44
server/Server.hs+112−99
@@ -1,114 +1,127 @@
1+{-# OPTIONS_GHC -Wall #-}
2+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
13module Main where
24
3−import Control.Monad (msum,guard,when)
5+import Control.Applicative ((<$>),(<|>))
6+import Control.Monad (guard)
47import Control.Monad.Trans (MonadIO(liftIO))
5−import Data.List (isPrefixOf, isSuffixOf, (\\))
6−import Data.Version (showVersion)
7−import Happstack.Server
8−import Happstack.Server.Compression
8+import qualified Data.ByteString as BS
9+import qualified Data.ByteString.Char8 as BSC
10+import qualified Data.HashMap.Strict as Map
11+import qualified Data.Version as Version
12+import System.Console.CmdArgs
913import System.Directory
10−import System.Environment
1114import System.Exit
1215import System.FilePath
1316import System.Process
14−import GHC.IO.Handle
15−import qualified Elm.Internal.Paths as ElmPaths
16−import Paths_elm_server
17−
18−runtime = "/elm-runtime.js"
19−
20−serve :: Int -> String -> IO ()
21−serve portNumber libLoc = do
22− putStrLn $ "Elm Server " ++ showVersion version ++
23− ": running at <http://localhost:" ++ show portNumber ++ ">"
24− putStrLn "Just refresh a page to recompile it!"
25− simpleHTTP httpConf $ do
26− _ <- compressedResponseFilter
27− msum [ uriRest serveElm
28− , uriRest (serveLib libLoc)
29− , serveDirectory EnableBrowsing [] "."
30− ]
31− where httpConf = nullConf { port = portNumber }
32−
33−pageTitle :: String -> String
34−pageTitle = dropExtension . takeBaseName
35−
36−serveElm :: FilePath -> ServerPartT IO Response
37−serveElm fp =
38− do fileExists <- liftIO $ doesFileExist file
39− guard (fileExists && takeExtension fp == ".elm")
40− onSuccess compile serve
17+import System.IO (hGetContents, Handle)
18+
19+import Paths_elm_server (version)
20+import qualified Elm.Internal.Paths as Elm
21+import Snap.Core
22+import Snap.Http.Server
23+import Snap.Util.FileServe
24+
25+data Flags = Flags
26+ { port :: Int
27+ , runtime :: Maybe FilePath
28+ } deriving (Data,Typeable,Show,Eq)
29+
30+flags :: Flags
31+flags = Flags
32+ { port = 8000 &= help "set the port of the server"
33+ , runtime = Nothing &= typFile
34+ &= help "Specify a custom location for Elm's runtime system."
35+ } &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
36+ \It serves static files and freshly recompiled Elm files."
37+ &= helpArg [explicit, name "help", name "h"]
38+ &= versionArg [ explicit, name "version", name "v"
39+ , summary (Version.showVersion version)
40+ ]
41+ &= summary ("Elm Server " ++ Version.showVersion version ++
42+ ", (c) Evan Czaplicki 2011-2014")
43+
44+
45+config :: Config Snap a
46+config = setAccessLog ConfigNoLog (setErrorLog ConfigNoLog defaultConfig)
47+
48+-- | Set up the server.
49+main :: IO ()
50+main = do
51+ cargs <- cmdArgs flags
52+ putStrLn $ "Elm Server " ++ Version.showVersion version ++
53+ ": Just refresh a page to recompile it!"
54+ httpServe (setPort (port cargs) config) $
55+ serveRuntime (maybe Elm.runtime id (runtime cargs))
56+ <|> serveElm
57+ <|> serveDirectoryWith directoryConfig "."
58+
59+directoryConfig :: MonadSnap m => DirectoryConfig m
60+directoryConfig =
61+ fancyDirectoryConfig
62+ { indexGenerator = defaultIndexGenerator defaultMimeTypes indexStyle
63+ , mimeTypes = Map.insert ".elm" "text/html" defaultMimeTypes
64+ }
65+
66+indexStyle :: BS.ByteString
67+indexStyle =
68+ "body { margin:0; font-family:sans-serif; background:rgb(245,245,245);\
69+ \ font-family: calibri, verdana, helvetica, arial; }\
70+ \div.header { padding: 40px 50px; font-size: 24px; }\
71+ \div.content { padding: 0 40px }\
72+ \div.footer { display:none; }\
73+ \table { width:100%; border-collapse:collapse; }\
74+ \td { padding: 6px 10px; }\
75+ \tr:nth-child(odd) { background:rgb(216,221,225); }\
76+ \td { font-family:monospace }\
77+ \th { background:rgb(90,99,120); color:white; text-align:left;\
78+ \ padding:10px; font-weight:normal; }"
79+
80+runtimeName :: String
81+runtimeName = "elm-runtime.js"
82+
83+serveRuntime :: FilePath -> Snap ()
84+serveRuntime runtimePath =
85+ do file <- BSC.unpack . rqPathInfo <$> getRequest
86+ guard (file == runtimeName)
87+ serveFileAs "application/javascript" runtimePath
88+
89+serveElm :: Snap ()
90+serveElm =
91+ do file <- BSC.unpack . rqPathInfo <$> getRequest
92+ exists <- liftIO $ doesFileExist file
93+ guard (exists && takeExtension file == ".elm")
94+ onSuccess (compile file) (serve file)
4195 where
42− file = tail fp
96+ compile file =
97+ let elmArgs = [ "--make", "--runtime=" ++ runtimeName, file ]
98+ in createProcess $ (proc "elm" elmArgs) { std_out = CreatePipe }
4399
44− compile = liftIO $ createProcess $ (proc "elm" args) { std_out = CreatePipe }
45− where args = [ "--make", "--runtime=" ++ runtime, file ]
100+ serve file =
101+ serveFileAs "text/html; charset=UTF-8" ("build" </> replaceExtension file "html")
46102
47− serve = serveFile (asContentType "text/html")
48− ("build" </> replaceExtension file "html")
103+failure :: String -> Snap ()
104+failure msg =
105+ do modifyResponse $ setResponseStatus 404 "Not found"
106+ writeBS $ BSC.pack msg
49107
50−onSuccess action success = do
51− (_, stdout, _, handle) <- action
52− exitCode <- liftIO $ waitForProcess handle
53− case (exitCode, stdout) of
54− (ExitFailure 127, _) ->
55− badRequest $ toResponse "Error: elm binary not found in your path."
56− (ExitFailure _, Just out) ->
57− do str <- liftIO $ hGetContents out
58− badRequest $ toResponse str
59− (ExitFailure _, Nothing) ->
60− badRequest $ toResponse "See command line for error message."
61− (ExitSuccess, _) -> success
108+onSuccess :: IO (t, Maybe Handle, t1, ProcessHandle) -> Snap () -> Snap ()
109+onSuccess action success =
110+ do (_, stdout, _, handle) <- liftIO action
111+ exitCode <- liftIO $ waitForProcess handle
112+ case (exitCode, stdout) of
113+ (ExitFailure 127, _) ->
114+ failure "Error: elm compiler not found in your path."
62115
116+ (ExitFailure _, Just out) ->
117+ failure =<< liftIO (hGetContents out)
63118
64−serveLib :: FilePath -> String -> ServerPartT IO Response
65−serveLib libLoc fp = do
66− guard (fp == runtime)
67− serveFile (asContentType "application/javascript") libLoc
119+ (ExitFailure _, Nothing) ->
120+ failure "See command line for error message."
68121
69−main :: IO ()
70−main = getArgs >>= parse
71−
72−parse :: [String] -> IO ()
73−parse ("--help":_) = putStrLn usage
74−parse ("--version":_) = putStrLn ("The Elm Server " ++ showVersion version)
75−parse args =
76− if null remainingArgs then
77− serve portNumber elmRuntime
78− else
79− putStrLn usageMini
122+ (ExitSuccess, _) -> success
80123
81− where
82− runtimeArg = filter (isPrefixOf "--runtime-location=") args
83− portArg = filter (isPrefixOf "--port=") args
84− remainingArgs = (args \\ runtimeArg) \\ portArg
85−
86− argValue arg = tail $ dropWhile (/= '=') (head arg)
87− portNumber = if null portArg then 8000 else read (argValue portArg) :: Int
88− elmRuntime = if null runtimeArg then
89− ElmPaths.runtime
90− else
91− argValue runtimeArg
92−
93−usageMini :: String
94−usageMini =
95− "Usage: elm-server [OPTIONS]\n\
96− \Try `elm-server --help' for more information."
97−
98−usage :: String
99−usage =
100− "Usage: elm-server [OPTIONS]\n\
101− \Compiles and serves .elm files from the current directory.\n\
102− \Example: elm-server\n\
103− \\n\
104− \Server configuration:\n\
105− \ --port set the port to listen on (default: 8000)\n\
106− \\n\
107− \Resource Locations:\n\
108− \ --runtime-location set the location of the Elm runtime\n\
109− \\n\
110− \Compiler Information:\n\
111− \ --version print the version information and exit\n\
112− \ --help display this help and exit\n\
113− \\n\
114− \Elm home page: <http://elm-lang.org>"
124+{--
125+pageTitle :: String -> String
126+pageTitle = dropExtension . takeBaseName
127+--}
server/elm-server.cabal+14−13
@@ -1,7 +1,9 @@
11Name: elm-server
2−Version: 0.10.1
3−Synopsis: The Elm language server.
4−Description: This package provides a standalone, Happstack-based Elm server.
2+Version: 0.12
3+Synopsis: Server for developing Elm projects
4+Description: Provides a standalone Snap server that serves static files.
5+ For Elm files, it recompiles them and serves them as HTML,
6+ so you can just refresh to see the new version.
57
68Homepage: http://elm-lang.org
79
@@ -10,7 +12,7 @@ License-file: LICENSE
1012
1113Author: Evan Czaplicki
1214Maintainer: info@elm-lang.org
13−Copyright: Copyright: (c) 2011-2013 Evan Czaplicki
15+Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
1416
1517Category: Compiler, Language
1618
@@ -26,15 +28,14 @@ source-repository head
2628Executable elm-server
2729 Main-is: Server.hs
2830 Build-depends: base >=4.2 && <5,
31+ bytestring,
32+ cmdargs,
2933 containers >= 0.3,
3034 directory,
31− transformers >= 0.2,
32− mtl >= 2,
33− parsec >= 3.1.1,
34− blaze-html >= 0.5.1,
35− HTTP >= 4000,
36− happstack-server,
37− deepseq,
3835 filepath,
39− Elm >= 0.10.1,
40− process
36+ Elm >= 0.12,
37+ snap-core,
38+ snap-server,
39+ mtl,
40+ process,
41+ unordered-containers