Merge branch 'dev'

Conflicts:
	Elm.cabal
	Examples/elm-js/GameSkeleton/GameSkeleton.elm
	Examples/elm-js/Pong/Pong.elm
	core-js/Signal/Input.js
This commit is contained in:
evancz 2013-05-22 00:26:23 +02:00
commit af75972665
197 changed files with 6764 additions and 13408 deletions

View file

@ -1,6 +1,5 @@
Name: Elm
Version: 0.7.2
Version: 0.8
Synopsis: The Elm language module.
Description: Elm aims to make client-side web-development more pleasant.
It is a statically/strongly typed, functional reactive
@ -19,10 +18,11 @@ Copyright: Copyright: (c) 2011-2012 Evan Czaplicki
Category: Compiler, Language
Build-type: Simple
Build-type: Custom
Extra-source-files: changelog.txt
Data-files: elm-runtime-0.7.2.js
Data-dir: dist/data
Data-files: elm-runtime.js docs.json
Cabal-version: >=1.8
source-repository head
@ -32,7 +32,7 @@ source-repository head
Library
exposed-modules: Language.Elm,
Language.Elm.Quasi
Hs-Source-Dirs: src, src/Gen
Hs-Source-Dirs: compiler, compiler/Gen, compiler/Model, compiler/Transform
other-modules: Ast,
Context,
CompileToJS,
@ -41,6 +41,8 @@ Library
ExtractNoscript,
GenerateHtml,
Guid,
Libraries,
LoadLibraries,
Optimize,
Initialize,
Rename,
@ -65,10 +67,10 @@ Library
containers >= 0.3,
transformers >= 0.2,
mtl >= 2,
deepseq,
parsec >= 3.1.1,
blaze-html == 0.5.* || == 0.6.*,
blaze-markup == 0.5.1.*,
deepseq,
text,
template-haskell,
shakespeare >= 1,
@ -76,11 +78,16 @@ Library
bytestring,
hjsmin,
indents,
filepath
filepath,
template-haskell,
json,
directory
Executable elm
Main-is: Compiler.hs
Hs-Source-Dirs: src, src/Gen
Hs-Source-Dirs: compiler, compiler/Gen, compiler/Model, compiler/Transform
extensions: CPP
cpp-options: -DELM_COMPILEDATADIR="dist/data"
other-modules: Ast,
Context,
CompileToJS,
@ -89,6 +96,8 @@ Executable elm
ExtractNoscript,
GenerateHtml,
Guid,
Libraries,
LoadLibraries,
Optimize,
Initialize,
Rename,
@ -113,15 +122,16 @@ Executable elm
containers >= 0.3,
transformers >= 0.2,
mtl >= 2,
deepseq,
parsec >= 3.1.1,
blaze-html == 0.5.* || == 0.6.*,
blaze-markup == 0.5.1.*,
deepseq,
cmdargs,
pandoc >= 1.10,
bytestring,
hjsmin,
indents,
<<<<<<< .mine
filepath
@ -154,3 +164,37 @@ test-suite Main
hs-source-dirs: src, tests
main-is: Main.hs
=======
filepath,
template-haskell,
json,
directory
Executable elm-doc
Main-is: Docs.hs
Hs-Source-Dirs: compiler, compiler/Model, compiler/Transform
other-modules: Ast,
Context,
Parse.Library,
Parse.Modules,
Rename
Build-depends: base >=4.2 && <5,
containers >= 0.3,
transformers >= 0.2,
mtl >= 2,
parsec >= 3.1.1,
pandoc >= 1.10,
cmdargs,
indents
>>>>>>> .theirs

View file

@ -1,71 +0,0 @@
{-# LANGUAGE QuasiQuotes, TemplateHaskell #-}
import Control.Monad (msum)
import Happstack.Server
import Language.Elm
import Language.Elm.Quasi
elmRuntime="elm-runtime.js"
elmRTPath='/':elmRuntime
-- elmResponse is a "nice to have" helper function for compiling
-- Elm code when using Elm with Happstack. At some point this might
-- be moved to an elm-happstack package.
elmResponse :: ElmSource a
=> String -- ^ Page title
-> a -- ^ elm source
-> Response
elmResponse title = toResponse . toHtml elmRTPath title
-- embedding variables (in this case URLs)
rootHandler :: ServerPart Response
rootHandler = ok $ elmResponse "Welcome!" $ elmIndex
where
mouse = "/mouse" -- all three of these variables are used in elm_source/index.elm
clock = "/clock"
shapes = "/shapes"
elmIndex = $(elmFile "elm_source/index.elm")
-- loading elm source from file
mouseHandler :: ServerPart Response
mouseHandler = ok $ elmResponse "Mouse position demo"
$(elmFile "elm_source/mouse.elm")
clockHandler :: ServerPart Response
clockHandler = ok $ elmResponse "A clock" $(elmFile "elm_source/clock.elm")
-- embedding elm code inside Haskell using the QuasiQuoter:
shapesPage = [elm|
square = rect 200 200 (150,150)
circle = oval 140 140 (150,150)
pentagon = ngon 5 60 (150,150)
main = collage 300 300
[ outlined black square
, filled green pentagon
, customOutline [8,4] blue circle
]
|]
shapesHandler :: ServerPart Response
shapesHandler = ok $ elmResponse "Simple shapes" $ shapesPage
-- routing
elmExample :: String -> ServerPart Response
elmExample elmLoc = do
msum [ nullDir >> rootHandler
, dir elmRuntime $ nullDir >>
serveFile (guessContentTypeM mimeTypes) elmLoc
, dir "mouse" $ nullDir >>
mouseHandler
, dir "clock" $ nullDir >>
clockHandler
, dir "shapes" $ nullDir >>
shapesHandler
]
main :: IO ()
main = do
elmLoc <- Language.Elm.runtimeLocation
simpleHTTP nullConf {port = 3000} $ elmExample elmLoc

View file

@ -1,13 +0,0 @@
import Time (every)
hand clr len time =
let t = pi * time / 30 - pi / 2 in
solid clr $ line [(200,200), (200 + len * cos t, 200 + len * sin t)]
clock t = collage 400 400 [ filled (rgb 96 176 224) $ ngon 12 110 (200, 200)
, hand red 100 t
, hand black 100 (t/60)
, hand black 60 (t/720) ]
main = lift clock (every 1)

View file

@ -1,44 +0,0 @@
title w = size w 60 . text . header . toText $ "Elm-Happstack"
lightGrey = rgb 240 241 244
mediumGrey = rgb 216 221 225
heading outer inner =
color mediumGrey . size outer 61 .
color lightGrey . size outer 60 .
size inner 60 $ title inner
skeleton body outer =
let inner = if outer < 820 then outer - 20 else 800 in
flow down [ heading outer inner
, body outer inner
]
----------------------
section = text . bold . Text.height (5/4) . toText
-- splicing of values works but literal substitution of strings
-- for this reason, we need to have quotes.
mouseLink = "#{mouse}"
clockLink = "#{clock}"
shapesLink = "#{shapes}"
info w = List.map (\f -> f ()) . List.intersperse (\x -> plainText "&nbsp;") . List.map ((\e x -> e) . width w) $
[ section "Written in Elm, served with Happstack"
, text $ toText "This page is written in " ++ Text.link "http://elm-lang.org/" (toText "Elm") ++
toText " and served using the " ++
Text.link "http://happstack.com/" (toText "Happstack Web Framework") ++
toText ". Since you are looking at this page it is safe to assume that you already have the example code. "
, text $ toText "URLs are rendered using simple QuasiQuoter variable interpolation."
, section "More examples:"
, text $ toText "- " ++ Text.link mouseLink (toText "A simple mouse input example")
, text $ toText "- " ++ Text.link clockLink (toText "An animated analog clock")
, text $ toText "- " ++ Text.link shapesLink (toText "Some simple rendered shapes")
]
-- The following line does not parse, replaced it something slightly different
-- body outer inner = width outer . flow down $ (:) (plainText "&nbsp;") $ info inner
body outer inner = width outer . flow down $ info inner
main = lift (skeleton body) Window.width

View file

@ -1,15 +0,0 @@
-- Move your mouse around above the canvas!
import Mouse (position)
import Window (dimensions)
myBlue = rgb 0 85 170
myGreen = rgba 28 267 85 (1/2)
scene (x,y) (w,h) =
collage w h [ rotate (toFloat (x+y) / 1000) $ filled myBlue $ ngon 4 100 (200,200)
, filled myGreen $ ngon 5 30 (x,y)
]
main = lift2 scene Mouse.position Window.dimensions

View file

@ -1,82 +0,0 @@
module GameSkeleton where
{-- Part 1: Model the user input ----------------------------------------------
What information do you need to represent all relevant user input?
Task: Redefine `UserInput` to include all of the information you need.
Redefine `userInput` to be a signal that correctly models the user
input as described by `UserInput`.
------------------------------------------------------------------------------}
data UserInput = UserInput
userInput = constant UserInput
data Input = Input Float UserInput
{-- Part 2: Model the game ----------------------------------------------------
What information do you need to represent the entire game?
Tasks: Redefine `GameState` to represent your particular game.
Redefine `defaultGame` to represent your initial game state.
For example, if you want to represent many objects that just have a position,
your GameState might just be a list of coordinates and your default game might
be an empty list (no objects at the start):
data GameState = GameState [(Float,Float)]
defaultGame = GameState []
------------------------------------------------------------------------------}
data GameState = GameState
defaultGame = GameState
{-- Part 3: Update the game ---------------------------------------------------
How does the game step from one state to another based on user input?
Task: redefine `stepGame` to use the UserInput and GameState
you defined in parts 1 and 2. Maybe use some helper functions
to break up the work, stepping smaller parts of the game.
------------------------------------------------------------------------------}
stepGame (Input delta (UserInput)) (GameState) = GameState
{-- Part 4: Display the game --------------------------------------------------
How should the GameState be displayed to the user?
Task: redefine `display` to use the GameState you defined in part 2.
------------------------------------------------------------------------------}
display (w,h) gameState = asText gameState
{-- That's all folks! ---------------------------------------------------------
The following code puts it all together and show it on screen.
------------------------------------------------------------------------------}
delta = fps 60
input = sampleOn delta (lift2 Input delta userInput)
gameState = foldp stepGame defaultGame input
main = lift2 display Window.dimensions gameState

View file

@ -1,17 +0,0 @@
module Log where
import Foreign.JavaScript
import Signal.Input
foreign export jsevent "logMessage"
messages :: Signal JSString
(field, message) = textField ""
(butn , pressed) = button " Log "
messages =
lift castStringToJSString $ keepWhen pressed "" message
main = field `beside` butn

View file

@ -1,3 +0,0 @@
document.addEventListener('logMessage', function(e) {
console.log(e.value);
});

View file

@ -1,16 +0,0 @@
To compile this example yourself use:
elm --import-js="LogHelp.js" Log.elm
This compiles the Elm file and includes the necessary JavaScript code.
It produces a self-contained HTML file.
You can see the logged messages in the developer console of your browser.
The keyboard shortcut is F12 in many browsers.
Note: Not all browsers like reading the elm-runtime-x.y.z.js file from
an absolute path, so you may have to specify a relative path with
the --runtime flag (e.g. --runtime="../../../elm/elm-runtime-0.3.5.js").

View file

@ -1,14 +0,0 @@
module Map where
import JavaScript.Experimental
foreign import jsevent "provideMap"
(castElementToJSElement $ spacer 640 360)
jsMaps :: Signal JSElement
center (w,h) elem = container w h middle elem
maps = lift (castJSElementToElement 640 360) jsMaps
main = lift2 center Window.dimensions maps

View file

@ -1,25 +0,0 @@
document.body.onload = function() {
var lib = document.createElement('script');
lib.addEventListener('load', function() {
var div = document.createElement('div');
div.id = "demoMap";
div.style.width = "640px";
div.style.height = "360px";
var e = document.createEvent('Event');
e.initEvent('provideMap', true, true);
e.value = div;
document.dispatchEvent(e);
setTimeout(function() {
var map = new OpenLayers.Map("demoMap");
map.addLayer(new OpenLayers.Layer.OSM());
map.zoomToMaxExtent();
}, 0);
});
lib.src = "http://www.openlayers.org/api/OpenLayers.js";
document.head.appendChild(lib);
}

View file

@ -1,15 +0,0 @@
To compile this example yourself use:
elm --import-js="MapHelp.js" Map.elm
This compiles the Elm file and includes the necessary JavaScript code.
It produces a self-contained HTML file.
Note: Not all browsers like reading the elm-runtime-x.y.z.js file from
an absolute path, so you may have to specify a relative path with
the --runtime flag (e.g. --runtime="../../../elm/elm-runtime-0.3.5.js").
Warning: I am not sure how stable the OpenLayers API is. This is for
example only!

View file

@ -1,263 +0,0 @@
{----- Overview ------------------------------------------------------
This game displays some of the strengths of Functional Reactive
Programming (FRP). By the end of this file we will have written an
entire GUI/game without any imperative code! No global mutable state,
no flipping pixels, no destructive updates. In fact, Elm disallows all
of these things at the language level. So good design and safe coding
practices are a requirement, not just self-inforced suggestions.
This code neatly divides Pong into three major parts: modeling the
game, updating the game, and viewing the game. It may be helpful to
think of it as a functional variation on the Model-View-Controller
paradigm.
The code for Pong is structured as follows:
1. MODEL:
First we need to define Pong. We do this by modelling Pong with
simple data structures. We need two categories of model:
- Inputs to the game. For Pong, this is keyboard input from
users and clock-ticks from the frame rate manager.
- A model of the game itself: paddles, ball, score, etc.
Without a model of the game we would have nothing to update
or display!
These models are the basis for the next two sections, holding
all of the information about Pong that we will need.
2. UPDATE:
When new inputs come in, we need to update the current state
of the game. Without updates, this version of Pong would be very
very boring! This section defines a number of 'step functions'
that step the game forward based on our inputs. By separating
this from the model and display code, we can change how the game
works (how it steps forward) without changing anything else: the
underlying model and the display code need not be touched.
3. VIEW:
Finally, we need a display function that defines the user's view
of the game. This code is separate from the game logic, so like
the update logic, it can be modified without affecting any other
part of the program. We can also define many different views
of the same underlying model. In Pong there's not much need for
this, but as your model becomes more complex this may be very
useful!
If you would like to make a game or larger application in Elm, use
this structure! Maybe even use this file as a starting point for
playing around with your own ideas.
Let's get started!
-----------------------------------------------------------------------}
module Pong where
import JavaScript
-- Set the frames per second (FPS) to 60, calculate the deltas (the
-- difference between the two latest times, the amount of time since
-- the last frame), and convert the time into a number of seconds.
delta = lift inSeconds (fps 60)
------------------------------------------------------------------------
------ Modelling User Input ------
------------------------------------------------------------------------
-- During gameplay, all keyboard input is about the position of the
-- two paddles. So the keyboard input can be reduced to two directions,
-- each represented by an integer in {-1,0,1}. Furthermore, the SPACE
-- key is used to start the game between rounds, so we also need a
-- boolean value to represent whether it is pressed.
data KeyInput = KeyInput Bool Int Int
defaultKeyInput = KeyInput False 0 0
keyInput = lift3 KeyInput Keyboard.space
(lift .y Keyboard.wasd)
(lift .y Keyboard.arrows)
------------------------------------------------------------------------
------ Combining all inputs ------
------------------------------------------------------------------------
-- The inputs to this game include a timestep (which we extracted from
-- JavaScript) and the keyboard input from the users.
data Input = Input Float KeyInput
-- Combine both kinds of inputs and filter out keyboard events. We only
-- want the game to refresh on clock-ticks, not key presses too.
input = sampleOn delta (lift2 Input delta keyInput)
------------------------------------------------------------------------
------ Modelling Pong / a State Machine ------
------------------------------------------------------------------------
-- Pong has two obvious components: the ball and two paddles.
data Paddle = Paddle Float -- y-position
data Ball = Ball (Float,Float) (Float,Float) -- position and velocity
-- But we also want to keep track of the current score and whether
-- the ball is currently in play. This will allow us to have rounds
-- of play rather than just having the ball move around continuously.
data Score = Score Int Int
data State = Play | BetweenRounds
-- Together, this information makes up the state of the game. We model
-- Pong by using the inputs (defined above) to update the state of the
-- game!
data GameState = GameState State Score Ball Paddle Paddle
-- I have chosen to parameterize the size of the board, so it can
-- be changed with minimal effort.
gameWidth = 600
gameHeight = 400
halfWidth = gameWidth / 2
halfHeight = gameHeight / 2
-- Before we can update anything, we must first define the default
-- configuration of the game. In our case we want to start between
-- rounds with a score of zero to zero.
defaultGame = GameState BetweenRounds
(Score 0 0)
(Ball (halfWidth, halfHeight) (150,150))
(Paddle halfHeight)
(Paddle halfHeight)
------------------------------------------------------------------------
------ Stepping from State to State ------
------------------------------------------------------------------------
-- Now to step the game from one state to another. We can break this up
-- into smaller components.
-- First, we define a step function for updating the position of
-- paddles. It depends on our timestep and a desired direction (given
-- by keyboard input).
stepPaddle delta dir (Paddle y) =
Paddle $ clamp 20 (gameHeight-20) (y - toFloat dir * 200 * delta)
-- We must also step the ball forward. This is more complicated due to
-- the many kinds of collisions that can happen. All together, this
-- function figures out the new velocity of the ball based on
-- collisions with the top and bottom borders and collisions with the
-- paddles. This new velocity is used to calculate a new position.
-- This function also determines whether a point has been scored and
-- who receives the point. Thus, its output is a new Ball and points
-- to be added to each player.
makePositive n = if n > 0 then n else 0-n
makeNegative n = if n > 0 then 0-n else n
within epsilon n x = x > n - epsilon && x < n + epsilon
stepVelocity velocity lowerCollision upperCollision =
if lowerCollision then makePositive velocity else
if upperCollision then makeNegative velocity else velocity
stepBall delta (Ball (x,y) (vx,vy)) (Paddle y1) (Paddle y2) =
let hitPaddle1 = within 20 y1 y && within 8 25 x
hitPaddle2 = within 20 y2 y && within 8 (gameWidth - 25) x
vx' = stepVelocity vx hitPaddle1 hitPaddle2
vy' = stepVelocity vy (y < 7) (y > gameHeight - 7)
scored = x > gameWidth || x < 0
x' = if scored then halfWidth else x + vx' * delta
y' = if scored then halfHeight else y + vy' * delta
in ( Ball (x',y') (vx',vy')
, if x > gameWidth then 1 else 0
, if x < 0 then 1 else 0 )
-- Finally, we define a step function for the entire game. This steps from state to
-- state based on the inputs to the game.
stepGame (Input delta (KeyInput space dir1 dir2))
(GameState state (Score s1 s2) ball paddle1 paddle2) =
let (ball',s1',s2') = if state == Play then stepBall delta ball paddle1 paddle2
else (ball, 0, 0)
state' = case state of
Play -> if s1' /= s2' then BetweenRounds else state
BetweenRounds -> if space then Play else state
in GameState state'
(Score (s1+s1') (s2+s2'))
ball'
(stepPaddle delta dir1 paddle1)
(stepPaddle delta dir2 paddle2)
-- Now we put it all together. We have a signal of inputs that changes whenever there
-- is a clock tick. This input signal carries the all the information we need about
-- the keyboard. We also have a step function that steps from one game-state to the
-- next based on some inputs.
-- The `gameState` signal steps forward every time a new input comes in. It starts
-- as the default game and progresses based on user behavior.
gameState = foldp stepGame defaultGame input
------------------------------------------------------------------------
------ Displaying the Game ------
------------------------------------------------------------------------
-- These functions take a GameState and turn it into something a user
-- can see and understand. It is totally independent of how the game
-- updates, it only needs to know the current game state. This allows us
-- to change how the game looks without changing any of the logic of the
-- game.
-- This function displays the current score and directions.
scoreBoard w inPlay p1 p2 =
let code = text . monospace . toText
stack top bottom = flow down [ code " ", code top, code bottom ]
msg = width w . centeredText . monospace $ toText "Press SPACE to begin"
board = flow right [ stack "W" "S", spacer 20 1
, text . Text.height 4 . toText $ show p1 ++ " " ++ show p2
, spacer 20 1, stack "&uarr;" "&darr;" ]
score = container w (heightOf board) midTop board
in if inPlay then score else score `above` msg
-- This function displays the entire GameState.
display (w,h) (GameState state (Score p1 p2) (Ball pos _) (Paddle y1) (Paddle y2)) =
layers
[ let pongGreen = rgb 60 100 60 in
container w h middle $ collage gameWidth gameHeight
[ filled pongGreen (rect gameWidth gameHeight (halfWidth,halfHeight))
, filled white (oval 15 15 pos) -- ball
, filled white (rect 10 40 ( 20, y1)) -- first paddle
, filled white (rect 10 40 (gameWidth - 20, y2)) -- second paddle
]
, scoreBoard w (state == Play) p1 p2
]
-- We can now define a view of the game (a signal of Elements) that changes
-- as the GameState changes. This is what the users will see.
main = lift2 display Window.dimensions gameState

View file

@ -1,11 +0,0 @@
To compile this example yourself use:
elm Pong.elm
It produces a self-contained HTML file called Pong.html.
As of version 0.6 of the compiler, no external code is necessary
to make this work, so the code is slightly cleaner and simpler than
the "Pong in Elm" walkthrough.

View file

@ -1,12 +0,0 @@
To compile this example yourself use:
elm --import-js="RedirectHelp.js" Redirect.elm
This compiles the Elm file and includes the necessary JavaScript code.
It produces a self-contained HTML file.
Note: Not all browsers like reading the elm-runtime-x.y.z.js file from
an absolute path, so you may have to specify a relative path with
the --runtime flag (e.g. --runtime="../../../elm/elm-runtime-0.3.5.js").

View file

@ -1,17 +0,0 @@
module Redirect where
import Foreign.JavaScript
import Signal.Input
foreign export jsevent "redirect"
redirectTo :: Signal JSString
(butn, pressed) = button " Redirect to elm-lang.org "
redirectTo =
lift castStringToJSString $
keepWhen pressed "" (constant "http://elm-lang.org/")
main = butn

View file

@ -1,3 +0,0 @@
document.addEventListener('redirect', function(e) {
if (e.value.length > 0) { window.location = e.value; }
});

View file

@ -1,14 +0,0 @@
module ChangeTitle where
import Foreign.JavaScript
import Signal.Input
foreign export jsevent "changeTitle"
title :: Signal JSString
(field, title) = let (f,t) = textField "" in
(f, lift castStringToJSString t)
main = plainText "Change this page's title to: " `beside` field

View file

@ -1,3 +0,0 @@
document.addEventListener('changeTitle', function(e) {
document.title = e.value;
});

View file

@ -1,7 +0,0 @@
To compile this example yourself use:
elm --import-js="ChangeTitleHelp.js" ChangeTitle.elm
This compiles the Elm file and includes the necessary JavaScript code.
It produces a self-contained HTML file.

View file

@ -1,81 +0,0 @@
{-# LANGUAGE QuasiQuotes, TemplateHaskell, OverloadedStrings, TypeFamilies, MultiParamTypeClasses #-}
import Language.Elm
import Language.Elm.Quasi
import Language.Elm.Yesod
import Yesod
import Text.Hamlet
data ElmTest = ElmTest
-- loading external elm code
mousePage = $(elmFile "elm_source/mouse.elm")
clockPage = $(elmFile "elm_source/clock.elm")
-- embedding elm code inside Haskell using the QuasiQuoter:
shapesPage = [elm|
square = rect 200 200 (150,150)
circle = oval 140 140 (150,150)
pentagon = ngon 5 60 (150,150)
main = collage 300 300
[ outlined black square
, filled green pentagon
, customOutline [8,4] blue circle
]
|]
-- our Yesod App
mkYesod "ElmTest" [parseRoutes|
/ RootR GET
/mouse MouseR GET
/clock ClockR GET
/shapes ShapesR GET
|]
-- Your App data type needs to have an instance of YesodElm (see line 75&76)
-- so that toWidget can work with QuasiQuoted elm code. All URL interpolation
-- is done automatically. (e.g. lines 28-30 in elm_source/index.elm)
getMouseR :: Handler RepHtml
getMouseR =
defaultLayout $ do
setTitle "Mouse position demo"
toWidget mousePage
getClockR :: Handler RepHtml
getClockR =
defaultLayout $ do
setTitle "A clock"
toWidget clockPage
getShapesR :: Handler RepHtml
getShapesR =
defaultLayout $ do
setTitle "Simple shapes"
toWidget shapesPage
getRootR :: Handler RepHtml
getRootR =
defaultLayout $ do
setTitle "Welcome!"
toWidget $(elmFile "elm_source/index.elm")
-- Our Yesod instance contains the default layout, which inserts the elm-min.js
-- file in the site's <head> tag. The YesodElm instance defines the location of
-- elm-min.js
instance Yesod ElmTest where
jsLoader _ = BottomOfHeadBlocking
defaultLayout widget = do
mmsg <- getMessage
pc <- widgetToPageContent $ do
$(whamletFile "templates/default-layout.hamlet")
hamletToRepHtml $(hamletFile "templates/default-layout-wrapper.hamlet")
instance YesodElm ElmTest where
urlElmJs _ = Right $ "https://raw.github.com/evancz/Elm/master/elm/elm-runtime-0.7.2.js"
main :: IO ()
main = warpDebug 3000 ElmTest

View file

@ -1,11 +0,0 @@
hand clr len time =
let t = pi * inSeconds time / 30 - pi / 2 in
solid clr $ line [(200,200), (200 + len * cos t, 200 + len * sin t)]
clock t = collage 400 400 [ filled (rgb 96 176 224) $ ngon 12 110 (200, 200)
, hand red 100 t
, hand black 100 (t/60)
, hand black 60 (t/720) ]
main = lift clock (every second)

View file

@ -1,35 +0,0 @@
title w = size w 60 . text . header . toText $ "Elm-Yesod"
lightGrey = rgb 240 241 244
mediumGrey = rgb 216 221 225
heading outer inner =
color mediumGrey . size outer 61 .
color lightGrey . size outer 60 .
size inner 60 $ title inner
skeleton body outer =
let inner = if outer < 820 then outer - 20 else 800 in
flow down [ heading outer inner
, body outer inner
]
----------------------
section = text . bold . Text.height (5/4) . toText
info w = List.map (\f -> f ()) . List.intersperse (\x -> plainText "&nbsp;") . List.map ((\e x -> e) . width w) $
[ section "Written in Elm, served with Yesod"
, text $ toText "This page is written in " ++ Text.link "http://elm-lang.org/" (toText "Elm") ++
toText " and served using the " ++
Text.link "http://yesodweb.com/" (toText "Yesod Web Framework") ++
toText ". Since you are looking at this page it is safe to assume that you already have the example code. "
, text $ toText "Type-safe URLs are rendered using simple QuasiQuoter variable interpolation."
, section "More examples:"
, text $ toText "- " ++ Text.link "@{MouseR}" (toText "A simple mouse input example")
, text $ toText "- " ++ Text.link "@{ClockR}" (toText "An animated analog clock")
, text $ toText "- " ++ Text.link "@{ShapesR}" (toText "Some simple rendered shapes")
]
body outer inner = width outer . flow down . (:) (plainText "&nbsp;") $ info inner
main = lift (skeleton body) Window.width

View file

@ -1,13 +0,0 @@
-- Move your mouse around above the canvas!
myBlue = rgb 0 85 170
myGreen = rgba 28 267 85 0.5
scene (x,y) (w,h) =
collage w h
[ rotate (toFloat (x+y) / 1000) (filled myBlue (ngon 4 100 (200,200)))
, filled myGreen (ngon 5 30 (x,y))
]
main = lift2 scene Mouse.position Window.dimensions

View file

@ -1,8 +0,0 @@
$doctype 5
<html>
<head>
<meta charset="UTF-8">
<title>#{pageTitle pc}
^{pageHead pc}
<body>
^{pageBody pc}

View file

@ -1,3 +0,0 @@
$maybe msg <- mmsg
<div .message>#{msg}
^{widget}

View file

@ -1,2 +0,0 @@
*.html
mario_mp

View file

@ -1,6 +0,0 @@
module Clicks where
import WebSocket
msgs = show <~ count Mouse.clicks
main = asText <~ open "ws://localhost:8080/ws" msgs

View file

@ -1,91 +0,0 @@
module Mario where
import Dict
import JavaScript
import JSON
import Random
import WebSocket
{- INPUT -}
jumpStep isJump obj = if isJump && obj.y == 0 then { obj | vy <- 5 } else obj
gravityStep t obj = { obj | vy <- if obj.y > 0 then obj.vy - t/4 else obj.vy }
timeStep t obj = let {x,y,vx,vy} = obj in
{ obj | x <- x + t * vx , y <- max 0 $ y + t * vy }
walkStep dir obj = { obj | vx <- dir, dir <- if | dir < 0 -> "left"
| dir > 0 -> "right"
| otherwise -> obj.dir }
step t d j = timeStep t . gravityStep t . jumpStep j . walkStep d
delta = lift (flip (/) 20) (fps 25)
leftRight = toFloat . .x <~ Keyboard.arrows
jump = (\{y} -> y > 0) <~ Keyboard.arrows
steps = sampleOn delta (lift3 step delta leftRight jump)
{- LOCAL STATE -}
initialMario = { x = 0, y = 0, vx = 0, vy = 0, dir = "right" }
stateSignal = foldp ($) initialMario steps
encode obj id =
castJSStringToString . (toPrettyJSString "") . JSON.fromList $
[ ("id", JsonNumber id)
, ("x", JsonNumber obj.x)
, ("y", JsonNumber obj.y)
, ("vx", JsonNumber obj.vx)
, ("dir", JsonString obj.dir) ]
--encode obj id = show id ++ " " ++ show obj.x ++ " " ++ show obj.y
clientID = inRange 0 99999
myStream = encode <~ stateSignal ~ clientID
{- NETWORK LAYER -}
worldMessageStream = open "ws://localhost:8080/ws" myStream
-- :: String -> Maybe (Float, Record)
parsePlayer msg =
case fromString msg of
Nothing -> Nothing
Just json ->
let id = findNumber "id" json
x = findNumber "x" json
y = findNumber "y" json
vx = findNumber "vx" json
dir = findString "dir" json
in Just (id, { x = x, y = y, vx = vx, vy = 0, dir = dir })
-- :: Maybe (Float, Record) -> Dict String Record -> Dict String Record
updateWorldPositions maybeMario marioDict = case maybeMario of
Just (id, mario) -> (Dict.insert) (show id) mario marioDict
Nothing -> marioDict
-- :: Signal (Dict String Record)
worldPositions = foldp updateWorldPositions Dict.empty (parsePlayer <~ worldMessageStream)
--worldPositions = constant empty
marios = Dict.values <~ worldPositions
{- RENDER CODE -}
-- :: Record -> Form
mario2Form (w,h) mario =
let verb = if mario.vx /= 0 then "walk" else "stand"
src = "/imgs/mario/" ++ verb ++ "/" ++ mario.dir ++ ".gif"
in toForm (mario.x, (h-63)-mario.y) (image 35 35 src)
-- :: (Int,Int) -> [Record] -> Element
render (w,h) marios =
collage w h ( (filled cyan $ rect w h (w `div` 2, h `div` 2))
: (filled green $ rect w 50 (w `div` 2,h-25))
: List.map (mario2Form (w,h)) marios )
{- PUTTING IT TOGETHER -}
-- :: Signal Element
main = render <~ Window.dimensions ~ marios
--main = above <~ ((plainText . show) <~ (marios)) ~ (render <~ Window.dimensions ~ marios)
--main = (plainText . show) <~ (marios)

View file

@ -1,8 +0,0 @@
module Object where
import JavaScript
import JSON
main = plainText . castJSStringToString . (toPrettyJSString "") . fromList $ [ ("answer", JsonNumber 42) ]

View file

@ -1,7 +0,0 @@
module Values where
import Dict
main = constant . plainText . show . values $ empty

View file

@ -1,43 +0,0 @@
package main
import (
"code.google.com/p/go.net/websocket"
)
type connection struct {
// The websocket connection.
ws *websocket.Conn
// Buffered channel of outbound messages.
send chan string
}
func (c *connection) reader() {
for {
var message string
err := websocket.Message.Receive(c.ws, &message)
if err != nil {
break
}
h.broadcast <- message
}
c.ws.Close()
}
func (c *connection) writer() {
for message := range c.send {
err := websocket.Message.Send(c.ws, message)
if err != nil {
break
}
}
c.ws.Close()
}
func wsHandler(ws *websocket.Conn) {
c := &connection{send: make(chan string, 256), ws: ws}
h.register <- c
defer func() { h.unregister <- c }()
go c.writer()
c.reader()
}

View file

@ -1 +0,0 @@
../../elm/elm-runtime.js

View file

@ -1,44 +0,0 @@
package main
type hub struct {
// Registered connections.
connections map[*connection]bool
// Inbound messages from the connections.
broadcast chan string
// Register requests from the connections.
register chan *connection
// Unregister requests from connections.
unregister chan *connection
}
var h = hub{
broadcast: make(chan string),
register: make(chan *connection),
unregister: make(chan *connection),
connections: make(map[*connection]bool),
}
func (h *hub) run() {
for {
select {
case c := <-h.register:
h.connections[c] = true
case c := <-h.unregister:
delete(h.connections, c)
close(c.send)
case m := <-h.broadcast:
for c := range h.connections {
select {
case c.send <- m:
default:
delete(h.connections, c)
close(c.send)
go c.ws.Close()
}
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -1,24 +0,0 @@
package main
import (
"code.google.com/p/go.net/websocket"
"flag"
"log"
"net/http"
)
var addr = flag.String("addr", ":8080", "http service address")
func fileHandler(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, r.URL.Path[1:])
}
func main() {
flag.Parse()
go h.run()
http.HandleFunc("/", fileHandler)
http.Handle("/ws", websocket.Handler(wsHandler))
if err := http.ListenAndServe(*addr, nil); err != nil {
log.Fatal("ListenAndServe:", err)
}
}

View file

@ -1,19 +0,0 @@
all: mario_mp Mario.html Clicks.html Object.html Values.html
mario_mp: *.go
go build
Mario.html: Mario.elm
elm -r elm-runtime.js Mario.elm
Clicks.html: Clicks.elm
elm -r elm-runtime.js Clicks.elm
Object.html: Object.elm
elm -r elm-runtime.js Object.elm
Values.html: Values.elm
elm -r elm-runtime.js Values.elm
clean:
rm -rf *.html mario_mp

156
Setup.hs Normal file
View file

@ -0,0 +1,156 @@
import Distribution.Simple
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Setup
import Distribution.PackageDescription
import System.Cmd
import System.Directory
import System.FilePath
import System.IO
import System.Process
import Control.Monad
-- Part 1
-- ------
-- Add a build callout
-- We need to build elm-doc and run it because that generates the file "docs.json" needs by Libraries.hs
-- which is part of the elm library and executable
-- Unfort. there seems to be no way to tell cabal that:
-- (a) elm-doc generates docs.json, and
-- (b) elm (library) depends on docs.json
-- Therefore, we either use make instead (or a script), or hack around in cabal
-- Part 2
-- ------
-- Add a post-build callout.
-- We need to build the runtime.js after we've built elm (because we use elm to generate some of the JavaScript),
-- but before cabal does the install file copy step
-- Assumptions
-- Elm.cabal expects the generated files to end up in dist/data
rtsDir lbi = (buildDir lbi) </> ".." </> "data" -- git won't look in dist + cabal will clean it
jsDir lbi = (buildDir lbi) </> ".." </> "js"
-- The runtime is called:
rts lbi = (rtsDir lbi) </> "elm-runtime.js"
-- The json file is called:
-- The elm-docs executable is called:
elmDoc = "elm-doc"
elm_doc lbi = (buildDir lbi) </> elmDoc </> elmDoc
types lbi = (rtsDir lbi) </> "docs.json"
-- buildDir with LocalBuildInfo points to "dist/build" (usually)
elm lbi = (buildDir lbi) </> "elm" </> "elm"
-- Care! This appears to be based on an unstable API
-- See: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html#2
main :: IO ()
main = defaultMainWithHooks simpleUserHooks { {-- buildHook = myBuild, --} postBuild = myPostBuildWithTypes }
-- Build
-- Not currently used. buildTypes is in 'myPostBuildWithTypes'
-- If using this again, change postBuild to 'myPostBuild'
-- Purpose is to make sure docs.json was built before elm exec was (as elm exec depended on it)
-- This is no longer true and the code below seems to affect cabal's build dependencies
myBuild :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()
myBuild pd lbi uh bf = do
putStrLn $ "Custom build step started: compile " ++ elmDoc
withExe pd (\x -> putStrLn (exeName x))
buildHook simpleUserHooks (filterExe elmDoc pd) (filterLBI elmDoc lbi) uh bf
putStrLn "Custom build step started: build docs.json"
buildTypes lbi -- see note(1) below
putStrLn "Custom build step started: compile everything"
buildHook simpleUserHooks pd lbi uh bf
-- note(1): We use to include docs.json directly into LoadLibraries at compile time
-- If docs.json is used in other (template) haskell files, they should be copied
-- and compiled in a separate directory (eg, dist/copiedSrc).
-- This is to make sure they are re-compiled on docs.json changes.
-- Copying is a better solution than 'touch'ing the source files
-- (touch is non-portable and confusing wrt RCS).
-- In the PackageDescription, the list of stuff to build is held in library (in a Maybe)
-- and the executables list. We want a PackageDescription that only mentions the executable 'name'
filterExe name pd = pd {
library = Nothing,
executables = filter (\x -> (exeName x == name)) (executables pd)
}
-- It's not enough to fix the PackageDescription, we also have to fix the LocalBuildInfo.
-- This includes the component build order (data ComponentName) which is horribly internal.
filterLBI name lbi = lbi {
libraryConfig = Nothing,
compBuildOrder = [CExeName name],
executableConfigs = filter (\a -> (fst a == name)) (executableConfigs lbi)
}
buildTypes lbi = do
createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
files <- getFiles ".elm" "libraries"
system (elm_doc lbi ++ " " ++ unwords files ++ " > " ++ (types lbi))
putStrLn $ "Custom build step completed: " ++ elmDoc
-- Post Build
myPostBuildWithTypes :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
myPostBuildWithTypes as bfs pd lbi = do
putStrLn "Custom build step started: build docs.json"
buildTypes lbi -- see note(1) below
myPostBuild as bfs pd lbi
myPostBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()
myPostBuild as bfs pd lbi = do
putStrLn "Custom post build step started: build elm-runtime.js"
buildRuntime lbi
postBuild simpleUserHooks as bfs pd lbi
getFiles ext dir = do
contents <- map (dir </>) `fmap` getDirectoryContents dir
let files = filter (\f -> takeExtension f == ext) contents
dirs = filter (not . hasExtension) contents
filess <- mapM (getFiles ext) dirs
return (files ++ concat filess)
appendJS lbi file = do
putStrLn (dropExtension file)
str <- readFile file
length str `seq` return ()
appendFile (rts lbi) str
appendElm lbi file = do
jsFile <- runElm lbi file
appendJS lbi jsFile
-- replace 'system' call with 'runProcess' which handles args better and allows env variable
-- "Elm_datadir" which is used by LoadLibraries to find docs.json
runElm :: LocalBuildInfo -> String -> IO FilePath
runElm lbi file = do
rts_c <- canonicalizePath (rts lbi) -- dist/data/elm-runtime.js
let js = jsDir lbi -- dist/js
let j = dropFileName (js </> file) -- dist/js/libraries/
createDirectoryIfMissing True j -- must do before any canonicalization
out_c <- canonicalizePath js -- dist/js (root folder)
elm_c <- canonicalizePath (elm lbi) -- dist/build/elm/elm
rtd_c <- canonicalizePath (rtsDir lbi) -- dist/data (for docs.json)
handle <- runProcess elm_c ["--only-js", "--no-prelude", "--output-directory="++out_c, file]
Nothing (Just [("Elm_datadir", rtd_c)]) Nothing Nothing Nothing
exitCode <- waitForProcess handle
return $ j </> replaceExtension (takeFileName file) ".js"
buildRuntime lbi = do
createDirectoryIfMissing False (rtsDir lbi) -- dist should already exist
writeFile (rts lbi) "Elm = {}; Elm.Native = {}; Elm.Native.Graphics = {};\n\
\Elm.Graphics = {}; ElmRuntime = {}; ElmRuntime.Render = {}\n"
mapM_ (appendJS lbi) =<< getFiles ".js" "libraries"
mapM_ (appendElm lbi) =<< getFiles ".elm" "libraries"
mapM_ (appendJS lbi) =<< getFiles ".js" "runtime"

View file

@ -2,10 +2,11 @@
module Main where
import Data.Either (lefts, rights)
import Data.List (intersect, intercalate)
import Data.List (intersect, intercalate,lookup)
import Data.Maybe (fromMaybe)
import Data.Version (showVersion)
import System.Console.CmdArgs
import System.Exit
import System.FilePath
import Text.Blaze.Html.Renderer.String (renderHtml)
@ -16,6 +17,7 @@ import Ast
import Initialize
import CompileToJS
import GenerateHtml
import qualified Libraries as Libraries
import Paths_Elm
data ELM =
@ -25,7 +27,8 @@ data ELM =
, separate_js :: Bool
, only_js :: Bool
, import_js :: [FilePath]
, generate_noscript :: Bool
, no_prelude :: Bool
, noscript :: Bool
, minify :: Bool
, output_directory :: Maybe FilePath
}
@ -38,7 +41,8 @@ elm = ELM { make = False &= help "automatically compile dependencies."
, separate_js = False &= help "Compile to separate HTML and JS files."
, only_js = False &= help "Compile only to JavaScript."
, import_js = [] &= typFile &= help "Include a JavaScript file before the body of the Elm program. Can be used many times. Files will be included in the given order."
, generate_noscript = True &= help "Add generated <noscript> tag to HTML output."
, no_prelude = False &= help "Do not import Prelude by default, used only when compiling standard libraries."
, noscript = True &= help "Add generated <noscript> tag to HTML output."
, minify = False &= help "Minify generated JavaScript"
, output_directory = Nothing &= typFile &= help "Output files to directory specified. Defaults to the location of original file."
} &=
@ -47,37 +51,41 @@ elm = ELM { make = False &= help "automatically compile dependencies."
main = do
args <- cmdArgs elm
mini <- getDataFileName ("elm-runtime-" ++ showVersion version ++ ".js")
mini <- getDataFileName "elm-runtime.js"
compileArgs mini args
compileArgs mini (ELM _ [] _ _ _ _ _ _ _) =
putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
compileArgs mini (ELM make files rtLoc split only js nscrpt isMini outputDir) =
mapM_ (fileTo isMini make what js nscrpt outputDir loc) files
where loc = fromMaybe mini rtLoc
what | only = JS
| split = Split
| otherwise = HTML
compileArgs mini flags =
case files flags of
[] -> putStrLn "Usage: elm [OPTIONS] [FILES]\nFor more help: elm --help"
fs -> mapM_ (fileTo flags what loc) fs
where loc = fromMaybe mini (runtime flags)
what | only_js flags = JS
| separate_js flags = Split
| otherwise = HTML
data What = JS | HTML | Split
fileTo isMini make what jsFiles noscript outputDir rtLoc file = do
let jsStyle = if isMini then Minified else Readable
formatJS = if isMini then BS.unpack . JS.minify . BS.pack else id
ems <- build make file
jss <- concat `fmap` mapM readFile jsFiles
fileTo flags what rtLoc file = do
let jsStyle = if minify flags then Minified else Readable
formatJS = if minify flags then BS.unpack . JS.minify . BS.pack else id
prelude = not (no_prelude flags)
ems <- if make flags then build prelude file
else do src <- readFile file
return (fmap (:[]) (buildFromSource prelude src))
jss <- concat `fmap` mapM readFile (import_js flags)
case ems of
Left err -> putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
Right ms ->
let path = fromMaybe "" outputDir </> file
Left err -> do putStrLn $ "Error while compiling " ++ file ++ ":\n" ++ err
exitFailure
Right ms' ->
let path = fromMaybe "" (output_directory flags) </> file
js = replaceExtension path ".js"
html = replaceExtension path ".html"
pairs = map ((,) (fst ms)) (snd ms)
txt = jss ++ concatMap jsModule pairs
ms = if no_prelude flags then ms' else map Libraries.addPrelude ms'
txt = jss ++ concatMap jsModule ms
in case what of
JS -> writeFile js (formatJS txt)
HTML -> writeFile html . renderHtml $
modulesToHtml jsStyle "" rtLoc jss noscript pairs
modulesToHtml jsStyle "" rtLoc jss (noscript flags) ms
Split ->
do writeFile html . renderHtml $ linkedHtml rtLoc js pairs
do writeFile html . renderHtml $ linkedHtml rtLoc js ms
writeFile js (formatJS txt)

65
compiler/Docs.hs Normal file
View file

@ -0,0 +1,65 @@
module Main where
import Ast
import Control.Applicative ((<$>), (<*>))
import Data.List (intercalate)
import Data.Maybe (catMaybes)
import Parse.Library
import Parse.Modules (moduleDef)
import Text.Parsec hiding (newline,spaces)
import System.Environment
import System.Exit
main = do
srcs <- mapM readFile =<< getArgs
case mapM docParse srcs of
Left err -> putStrLn err >> exitFailure
Right ms -> putStrLn (toModules ms)
toModules ms = wrap (intercalate ",\n " (map toModule ms))
where wrap s = "{ \"modules\" : [\n " ++ s ++ "\n ]\n}"
toModule (name, values) =
"{ \"name\" : " ++ show name ++ ",\n " ++
"\"values\" : [\n " ++ vs ++ "\n ]\n }"
where vs = intercalate ",\n " (map toValue values)
toValue (name, tipe, desc) =
"{ \"name\" : " ++ show name ++
",\n \"type\" : " ++ show tipe ++
",\n \"desc\" : " ++ show desc ++ "\n }"
docParse :: String -> Either String (String, [(String, String, String)])
docParse = setupParser $ do
optional freshLine
(names, exports) <- option (["Main"],[]) moduleDef
info <- many (annotation exports <|> try skip <|> end)
return (intercalate "." names, catMaybes info)
where
skip = manyTill anyChar simpleNewline >> return Nothing
end = many1 anyChar >> return Nothing
annotation :: [String] -> IParser (Maybe (String, String, String))
annotation exports =
try ((\c n t -> export (n,t,c)) <$> comment <*> (try adt <|> name) <*> tipe)
where
comment = concatMap clip <$> many lineComment
clip str = case str of { ' ':rest -> rest ; _ -> str } ++ "\n"
name = do v <- lowVar <|> parens symOp
whitespace ; hasType ; whitespace ; return v
tipe = manyTill anyChar (try (simpleNewline >> notFollowedBy (string " ")))
export info@(name,_,_) =
if null exports || name `elem` exports then Just info else Nothing
adt = lookAhead ((string "data" <|> string "type") >> whitespace >> capVar)
setupParser p source =
case iParse p "" source of
Right result -> Right result
Left err -> Left $ "Parse error at " ++ show err

View file

@ -57,8 +57,15 @@ match vs cs def
matchVar :: [String] -> [([Pattern],CExpr)] -> Match -> GuidCounter Match
matchVar (v:vs) cs def = match vs (map subVar cs) def
where subVar (PVar x : ps, C t s e) = (ps, C t s $ subst x (Var v) e)
subVar (PAnything : ps, ce) = (ps, ce)
where
subVar (p:ps, ce@(C t s e)) =
let ctx = C t s in
(ps, case p of
PVar x -> C t s $ subst x (Var v) e
PAnything -> ce
PRecord fs ->
ctx $ foldr (\x -> subst x (Access (ctx (Var v)) x)) e fs
)
matchCon :: [String] -> [([Pattern],CExpr)] -> Match -> GuidCounter Match
matchCon (v:vs) cs def = (flip (Match v) def) `liftM` mapM toClause css

338
compiler/Gen/CompileToJS.hs Normal file
View file

@ -0,0 +1,338 @@
module CompileToJS (showErr, jsModule) where
import Control.Arrow (first,second)
import Control.Monad (liftM,(<=<),join,ap)
import Data.Char (isAlpha,isDigit)
import Data.List (intercalate,sortBy,inits,foldl')
import qualified Data.Map as Map
import Data.Either (partitionEithers)
import qualified Text.Pandoc as Pan
import Ast
import Context
import Rename (derename)
import Cases
import Guid
import Rename (deprime)
import Types.Types ( Type(RecordT) )
showErr :: String -> String
showErr err = globalAssign "Elm.Main" (jsFunc "elm" body)
where msg = show . concatMap (++"<br>") . lines $ err
body = "var T = Elm.Text(elm);\n\
\return { main : T.text(T.monospace(" ++ msg ++ ")) };"
indent = concatMap f
where f '\n' = "\n "
f c = [c]
internalImports =
[ ("N" , "Elm.Native"),
("_N", "N.Utils(elm)"),
("_L", "N.List(elm)"),
("_E", "N.Error(elm)"),
("_str", "N.JavaScript(elm).toString")
]
parens s = "(" ++ s ++ ")"
brackets s = "{" ++ s ++ "}"
jsObj = brackets . intercalate ", "
jsList ss = "["++ intercalate "," ss ++"]"
jsFunc args body = "function(" ++ args ++ "){" ++ indent body ++ "}"
assign x e = "\nvar " ++ x ++ " = " ++ e ++ ";"
ret e = "\nreturn "++ e ++";"
iff a b c = a ++ "?" ++ b ++ ":" ++ c
quoted s = "'" ++ concatMap f s ++ "'"
where f '\n' = "\\n"
f '\'' = "\\'"
f '\t' = "\\t"
f '\"' = "\\\""
f '\\' = "\\\\"
f c = [c]
globalAssign n e = "\n" ++ assign' n e ++ ";"
assign' n e = n ++ " = " ++ e
jsModule (Module names exports imports stmts) =
setup ("Elm":modNames) ++ globalAssign ("Elm." ++ modName) (jsFunc "elm" program)
where
modNames = if null names then ["Main"] else names
modName = intercalate "." modNames
includes = concatMap jsImport imports
body = stmtsToJS stmts
defs = assign "$op" "{}"
program = "\nvar " ++ usefulFuncs ++ ";" ++ defs ++ includes ++ body ++
setup ("elm":"Native":modNames) ++
assign "_" ("elm.Native." ++ modName ++ "||{}") ++
getExports exports stmts ++ setup ("elm":modNames) ++
ret (assign' ("elm." ++ modName) "_") ++ "\n"
setup modNames = concatMap (\n -> globalAssign n $ n ++ "||{}") .
map (intercalate ".") . drop 2 . inits $ init modNames
usefulFuncs = intercalate ", " (map (uncurry assign') internalImports)
getExports names stmts = "\n"++ intercalate ";\n" (op : map fnPair fns)
where exNames n = either derename id n `elem` names
exports | null names = concatMap get stmts
| otherwise = filter exNames (concatMap get stmts)
(fns,ops) = partitionEithers exports
opPair op = "'" ++ op ++ "' : $op['" ++ op ++ "']"
fnPair fn = let fn' = derename fn in "_." ++ fn' ++ " = " ++ fn
op = ("_.$op = "++) . jsObj $ map opPair ops
get' (FnDef x _ _) = Left x
get' (OpDef op _ _ _) = Right op
get s = case s of Definition d -> [ get' d ]
Datatype _ _ tcs -> map (Left . fst) tcs
ImportEvent _ _ x _ -> [ Left x ]
ExportEvent _ _ _ -> []
TypeAlias _ _ _ -> []
TypeAnnotation _ _ -> []
jsImport (modul, how) =
case how of
As name -> assign name ("Elm." ++ modul ++ parens "elm")
Hiding vs -> include ++ " var hiding=" ++ (jsObj $ map (++":1") vs) ++
"; for(var k in _){if(k in hiding)continue;" ++
"eval('var '+k+'=_[\"'+k+'\"]')}"
Importing vs -> include ++ named
where
imprt v = assign' v ("_." ++ v)
def (o:p) = imprt (if isOp o then "$op['" ++ o:p ++ "']" else deprime (o:p))
named = if null vs then "" else "\nvar " ++ intercalate ", " (map def vs) ++ ";"
where
include = "\nvar _ = Elm." ++ modul ++ parens "elm" ++ ";" ++ setup modul
setup moduleName = " var " ++ concatMap (++";") (defs ++ [assign' moduleName "_"])
where
defs = map (\n -> assign' n (n ++ "||{}")) (subnames moduleName)
subnames = map (intercalate ".") . tail . inits . init . split
split names = case go [] names of
(name, []) -> [name]
(name, ns) -> name : split ns
go name str = case str of
'.':rest -> (reverse name, rest)
c:rest -> go (c:name) rest
[] -> (reverse name, [])
stmtsToJS :: [Statement] -> String
stmtsToJS stmts = run $ do program <- mapM toJS (sortBy cmpStmt stmts)
return (concat program)
where
cmpStmt s1 s2 = compare (valueOf s1) (valueOf s2)
valueOf s = case s of
Datatype _ _ _ -> 1
ImportEvent _ _ _ _ -> 2
Definition (FnDef f [] _) ->
if derename f == "main" then 5 else 4
Definition _ -> 3
ExportEvent _ _ _ -> 6
TypeAlias _ _ _ -> 0
TypeAnnotation _ _ -> 0
class ToJS a where
toJS :: a -> GuidCounter String
instance ToJS Def where
toJS (FnDef x [] e) = assign x `liftM` toJS' e
toJS (FnDef f as e) = (assign f . wrapper . func) `liftM` toJS' e
where
func body = jsFunc (intercalate ", " as) (ret body)
wrapper e | length as == 1 = e
| otherwise = 'F' : show (length as) ++ parens e
toJS (OpDef op a1 a2 e) =
do body <- toJS' e
let func = "F2" ++ parens (jsFunc (a1 ++ ", " ++ a2) (ret body))
return (globalAssign ("$op['" ++ op ++ "']") func)
instance ToJS Statement where
toJS stmt =
case stmt of
Definition d -> toJS d
Datatype _ _ tcs -> concat `liftM` mapM (toJS . toDef) tcs
where toDef (name,args) =
let vars = map (('a':) . show) [1..length args] in
Definition . FnDef name vars . noContext $
Data (derename name) (map (noContext . Var) vars)
ImportEvent js base elm _ ->
do v <- toJS' base
return $ concat [ "\nvar " ++ elm ++ "=Elm.Signal(elm).constant(" ++ v ++ ");"
, "\ndocument.addEventListener('", js
, "_' + elm.id, function(e) { elm.notify(", elm
, ".id, e.value); });" ]
ExportEvent js elm _ ->
return $ concat [ "\nlift(function(v) { "
, "var e = document.createEvent('Event');"
, "e.initEvent('", js, "_' + elm.id, true, true);"
, "e.value = v;"
, "document.dispatchEvent(e); return v; })(", elm, ");" ]
TypeAnnotation _ _ -> return ""
TypeAlias n _ t -> return ""
toJS' :: CExpr -> GuidCounter String
toJS' (C txt span expr) =
case expr of
MultiIf ps -> multiIfToJS span ps
Case e cases -> caseToJS span e cases
_ -> toJS expr
remove x e = "_N.remove('" ++ x ++ "', " ++ e ++ ")"
addField x v e = "_N.insert('" ++ x ++ "', " ++ v ++ ", " ++ e ++ ")"
setField fs e = "_N.replace(" ++ jsList (map f fs) ++ ", " ++ e ++ ")"
where f (x,v) = "['" ++ x ++ "'," ++ v ++ "]"
access x e = e ++ "." ++ x
makeRecord kvs = record `liftM` collect kvs
where
combine r (k,v) = Map.insertWith (++) k v r
collect = liftM (foldl' combine Map.empty) . mapM prep
prep (k, as, e@(C t s _)) =
do v <- toJS' (foldr (\x e -> C t s $ Lambda x e) e as)
return (k,[v])
fields fs =
brackets ("\n "++intercalate ",\n " (map (\(k,v) -> k++":"++v) fs))
hidden = fields . map (second jsList) .
filter (not . null . snd) . Map.toList . Map.map tail
record kvs = fields . (("_", hidden kvs) :) . Map.toList . Map.map head $ kvs
instance ToJS Expr where
toJS expr =
case expr of
Var x -> return $ x
Chr c -> return $ quoted [c]
Str s -> return $ "_str" ++ parens (quoted s)
IntNum n -> return $ show n
FloatNum n -> return $ show n
Boolean b -> return $ if b then "true" else "false"
Range lo hi -> jsRange `liftM` toJS' lo `ap` toJS' hi
Access e x -> access x `liftM` toJS' e
Remove e x -> remove x `liftM` toJS' e
Insert e x v -> addField x `liftM` toJS' v `ap` toJS' e
Modify e fs -> do fs' <- (mapM (\(x,v) -> (,) x `liftM` toJS' v) fs)
setField fs' `liftM` toJS' e
Record fs -> makeRecord fs
Binop op e1 e2 -> binop op `liftM` toJS' e1 `ap` toJS' e2
If eb et ef ->
parens `liftM` (iff `liftM` toJS' eb `ap` toJS' et `ap` toJS' ef)
Lambda v e -> liftM (jsFunc v . ret) (toJS' e)
App e1 e2 -> jsApp e1 e2
Let defs e -> jsLet defs e
Data name es ->
do fs <- mapM toJS' es
return $ case name of
"Nil" -> jsNil
"Cons" -> jsCons (head fs) ((head . tail) fs)
_ -> jsObj $ ("ctor:" ++ show name) : fields
where fields = zipWith (\n e -> "_" ++ show n ++ ":" ++ e) [0..] fs
Markdown doc -> return $ "text('" ++ pad ++ md ++ pad ++ "')"
where pad = "<div style=\"height:0;width:0;\">&nbsp;</div>"
md = formatMarkdown $ Pan.writeHtmlString Pan.def doc
jsApp e1 e2 =
do f <- toJS' func
as <- mapM toJS' args
return $ case as of
[a] -> f ++ parens a
_ -> "A" ++ show (length as) ++ parens (intercalate ", " (f:as))
where
(func, args) = go [e2] e1
go args e =
case e of
(C _ _ (App e1 e2)) -> go (e2 : args) e1
_ -> (e, args)
formatMarkdown = concatMap f
where f '\'' = "\\'"
f '\n' = "\\n"
f '"' = "\""
f c = [c]
multiIfToJS span ps =
case last ps of
(C _ _ (Var "otherwise"), e) -> toJS' e >>= \b -> format b (init ps)
_ -> format ("_E.If" ++ parens (quoted (show span))) ps
where
format base ps =
foldr (\c e -> parens $ c ++ " : " ++ e) base `liftM` mapM f ps
f (b,e) = do b' <- toJS' b
e' <- toJS' e
return (b' ++ " ? " ++ e')
jsLet defs e' = do ds <- jsDefs defs
e <- toJS' e'
return $ jsFunc "" (concat ds ++ ret e) ++ "()"
where
jsDefs defs = mapM toJS (sortBy f defs)
f a b = compare (valueOf a) (valueOf b)
valueOf (FnDef _ args _) = min 1 (length args)
valueOf (OpDef _ _ _ _) = 1
caseToJS span e ps = do
match <- caseToMatch ps
e' <- toJS' e
let (match',stmt) = case (match,e) of
(Match name _ _, C _ _ (Var x)) -> (matchSubst [(name,x)] match, "")
(Match name _ _, _) -> (match, assign name e')
_ -> (match, "")
matches <- matchToJS span match'
return $ "function(){ " ++ stmt ++ matches ++ " }()"
matchToJS span match =
case match of
Match name clauses def ->
do cases <- concat `liftM` mapM (clauseToJS span name) clauses
finally <- matchToJS span def
return $ concat [ "\nswitch (", name, ".ctor) {",
indent cases, "\n}", finally ]
Fail -> return ("_E.Case" ++ parens (quoted (show span)))
Break -> return "break;"
Other e -> ret `liftM` toJS' e
Seq ms -> concat `liftM` mapM (matchToJS span) (dropEnd [] ms)
where
dropEnd acc [] = acc
dropEnd acc (m:ms) =
case m of
Other _ -> acc ++ [m]
_ -> dropEnd (acc ++ [m]) ms
clauseToJS span var (Clause name vars e) = do
let vars' = map (\n -> var ++ "._" ++ show n) [0..]
s <- matchToJS span $ matchSubst (zip vars vars') e
return $ concat [ "\ncase ", quoted name, ":", indent s ]
jsNil = "_L.Nil"
jsCons e1 e2 = "_L.Cons(" ++ e1 ++ "," ++ e2 ++ ")"
jsRange e1 e2 = "_L.range" ++ parens (e1 ++ "," ++ e2)
jsCompare e1 e2 op = parens ("_N.cmp(" ++ e1 ++ "," ++ e2 ++ ").ctor" ++ op)
binop (o:p) e1 e2
| isAlpha o || '_' == o = (o:p) ++ parens e1 ++ parens e2
| otherwise =
let ops = ["+","-","*","/","&&","||"] in
case o:p of
"::" -> jsCons e1 e2
"++" -> "_L.append" ++ parens (e1 ++ "," ++ e2)
"$" -> e1 ++ parens e2
"<|" -> e1 ++ parens e2
"|>" -> e2 ++ parens e1
"." -> jsFunc "x" . ret $ e1 ++ parens (e2 ++ parens "x")
"^" -> "Math.pow(" ++ e1 ++ "," ++ e2 ++ ")"
"==" -> "_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
"/=" -> "!_N.eq(" ++ e1 ++ "," ++ e2 ++ ")"
"<" -> jsCompare e1 e2 "==='LT'"
">" -> jsCompare e1 e2 "==='GT'"
"<=" -> jsCompare e1 e2 "!=='GT'"
">=" -> jsCompare e1 e2 "!=='LT'"
"<~" -> "A2(lift," ++ e1 ++ "," ++ e2 ++ ")"
"~" -> "A3(lift2,F2(function(f,x){return f(x)}),"++e1++","++e2++")"
_ | elem (o:p) ops -> parens (e1 ++ (o:p) ++ e2)
| otherwise -> concat [ "$op['", o:p, "']"
, parens e1, parens e2 ]

View file

@ -36,7 +36,6 @@ instance Extract Expr where
(_ , ss1 , ss2 ) -> ss1 ++ ss2
Lambda v e -> f e
App (C _ _ (App (C _ _ (Var "link")) src)) txt -> linkExtract src txt
App (C _ _ (App (C _ _ (Var "Graphics.link")) src)) txt -> linkExtract src txt
App (C _ _ (App (C _ _ (Var "Text.link")) src)) txt -> linkExtract src txt
App (C _ _ (Var "header")) e -> tag "h1" e
App (C _ _ (Var "bold")) e -> tag "b" e

View file

@ -0,0 +1,75 @@
{-# LANGUAGE OverloadedStrings #-}
module GenerateHtml (generateHtml,
modulesToHtml, linkedHtml,
JSStyle (..)
) where
import Data.List (intercalate)
import Text.Blaze (preEscapedToMarkup)
import Text.Blaze.Html (Html)
import qualified Text.Blaze.Html5 as H
import Text.Blaze.Html5 ((!))
import qualified Text.Blaze.Html5.Attributes as A
import Text.Jasmine (minify)
import qualified Data.ByteString.Lazy.Char8 as BS
import Ast
import Initialize (buildFromSource)
import CompileToJS
import ExtractNoscript
import Libraries as Libraries
data JSStyle = Minified | Readable
makeScript :: JSStyle -> Either String String -> H.Html
makeScript _ (Left s) =
H.script ! A.type_ "text/javascript" ! A.src (H.toValue s) $ ""
makeScript jsStyle (Right s) =
H.script ! A.type_ "text/javascript" $ preEscapedToMarkup content
where content = case jsStyle of
Minified -> BS.unpack . minify . BS.pack $ s
Readable -> s
-- |This function compiles Elm code into simple HTML.
--
-- Usage example:
--
-- > generateHtml "/elm-min.js" "Some title" [elmFile|elm-source/somePage.elm|]
generateHtml :: String -- ^ Location of elm-runtime.js as expected by the browser
-> String -- ^ The page title
-> String -- ^ The elm source code.
-> Html
generateHtml libLoc title source =
case buildFromSource True source of
Right modul -> modulesToHtml Readable title libLoc [] True [modul]
Left err -> createHtml Readable libLoc title (Right $ showErr err)
(H.noscript "") "Main"
modulesToHtml jsStyle title libLoc jss nscrpt modules =
createHtml jsStyle libLoc title' js noscript altTitle
where
js = Right $ jss ++ concatMap jsModule modules
noscript = if nscrpt then extractNoscript $ last modules else ""
title' = if null title then altTitle else title
altTitle = intercalate "." names
where Module names _ _ _ = last modules
linkedHtml rtLoc jsLoc modules =
createHtml Readable rtLoc title (Left jsLoc) (H.noscript "") title
where
title = (\(Module names _ _ _) -> intercalate "." names) (last modules)
createHtml jsStyle libLoc title js noscript moduleToLoad =
H.docTypeHtml $ do
H.head $ do
H.meta ! A.charset "UTF-8"
H.title . H.toHtml $ title
makeScript Readable (Left libLoc)
makeScript jsStyle js
H.body $ do
H.script ! A.type_ "text/javascript" $ preEscapedToMarkup ("Elm.fullscreen(Elm." ++ moduleToLoad ++ ")" :: String)
H.noscript $ preEscapedToMarkup noscript

97
compiler/Initialize.hs Normal file
View file

@ -0,0 +1,97 @@
module Initialize (build, buildFromSource) where
import Control.Applicative ((<$>))
import Control.Monad.Error
import Data.List (lookup,nub)
import qualified Data.Map as Map
import Ast
import Data.Either (lefts,rights)
import Data.List (intercalate,partition)
import Parse.Parser (parseProgram, preParse)
import Rename
import qualified Libraries as Libs
import Types.Types ((-:))
import Types.Hints (hints)
import Types.Unify
import Types.Alias (dealias, mistakes)
import Optimize
import CompileToJS (jsModule)
checkMistakes :: Module -> Either String Module
checkMistakes modul@(Module name ex im stmts) =
case mistakes stmts of
m:ms -> Left (unlines (m:ms))
[] -> return modul
checkTypes :: Module -> Either String Module
checkTypes modul =
do subs <- unify hints modul
subs `seq` return (optimize (renameModule modul))
check :: Module -> Either String Module
check = checkMistakes >=> checkTypes
buildFromSource :: Bool -> String -> Either String Module
buildFromSource withPrelude src = (check . add) =<< (parseProgram src)
where add = if withPrelude then Libs.addPrelude else id
build :: Bool -> FilePath -> IO (Either String [Module])
build withPrelude root = do
names <- getSortedModuleNames root
case names of
Left err -> return (Left err)
Right ns -> do srcs <- zipWithM buildFile' [1..] ns
return (sequence srcs)
where
buildFile' n name = putStrLn (msg n name) >> buildFile withPrelude name
msg n name = "["++show n++" of "++show (length ns)++"] Compiling "++name
buildFile :: Bool -> String -> IO (Either String Module)
buildFile withPrelude moduleName =
let filePath = toFilePath moduleName in
case isNative moduleName of
True -> return (Right $ Module [moduleName] [] [] [])
--return (Left "Can't do that yet")
--Right `liftM` readFile filePath
False -> do txt <- readFile filePath
return $ buildFromSource withPrelude txt
getSortedModuleNames :: FilePath -> IO (Either String [String])
getSortedModuleNames root = do
deps <- readDeps [] root
return (sortDeps =<< deps)
type Deps = (String, [String])
sortDeps :: [Deps] -> Either String [String]
sortDeps deps = go [] (nub deps)
where
msg = "A cyclical or missing module dependency or was detected in: "
go :: [String] -> [Deps] -> Either String [String]
go sorted [] = Right sorted
go sorted unsorted =
case partition (all (`elem` sorted) . snd) unsorted of
([],m:ms) -> Left (msg ++ intercalate ", " (map fst (m:ms)) ++ show sorted ++ show unsorted)
(srtd,unsrtd) -> go (sorted ++ map fst srtd) unsrtd
readDeps :: [FilePath] -> FilePath -> IO (Either String [Deps])
readDeps seen root = do
txt <- readFile root
case preParse txt of
Left err -> return (Left err)
Right (name,deps) -> do rest <- mapM (readDeps seen' . toFilePath) newDeps
return $ do rs <- sequence rest
return ((name, realDeps) : concat rs)
where realDeps = filter (`notElem` builtIns) deps
newDeps = filter (`notElem` seen) realDeps
seen' = root : seen ++ newDeps
builtIns = Map.keys Libs.libraries
isNative name = takeWhile (/='.') name == "Native"
toFilePath name = map swapDots name ++ ext
where swapDots '.' = '/'
swapDots c = c
ext = if isNative name then ".js" else ".elm"

58
compiler/Language/Elm.hs Normal file
View file

@ -0,0 +1,58 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{- | This module exports the functions necessary for compiling Elm code into the
respective HTML, JS and CSS code.
The type class @'ElmSource'@ requires an instance for all types that the Elm
compiler understands. The provided instances for String, Text and QuasiQuoted
Elm source code should be sufficient.
The documentation for the Elm language is available at
<http://elm-lang.org/Documentation.elm>, and many interactive examples are
available at <http://elm-lang.org/Examples.elm>
Example implementations using Yesod and Happstack are available
at <https://github.com/tazjin/Elm/tree/master/Examples>
-}
module Language.Elm (
compile, toHtml,
moduleName,
runtime, docs
) where
import qualified Ast as Ast
import qualified Libraries as Libraries
import Data.List (intercalate)
import Data.Version (showVersion)
import CompileToJS (showErr, jsModule)
import ExtractNoscript
import GenerateHtml
import Initialize
import Text.Blaze.Html (Html)
import Paths_Elm
-- |This function compiles Elm code to JavaScript.
compile :: String -> String
compile source = either showErr jsModule modul
where modul = buildFromSource True source
-- |This function extracts the module name of a given source program.
moduleName :: String -> String
moduleName source = either (const "Main") getName modul
where modul = buildFromSource False source
getName (Ast.Module names _ _ _) = intercalate "." names
-- |This function compiles Elm code into a full HTML page.
toHtml :: String -- ^ Location of elm-min.js as expected by the browser
-> String -- ^ The page title
-> String -- ^ The elm source code
-> Html
toHtml = generateHtml
-- |The absolute path to Elm's runtime system.
runtime :: IO FilePath
runtime = getDataFileName "elm-runtime.js"
-- |The absolute path to Elm's core library documentation.
docs :: IO FilePath
docs = getDataFileName "docs.json"

View file

@ -2,7 +2,7 @@
module Ast where
import Context
import Data.Char (isDigit)
import Data.Char (isDigit, isSymbol)
import Data.List (intercalate)
import Types.Types
import qualified Text.Pandoc as Pandoc
@ -12,8 +12,8 @@ data Module = Module [String] Exports Imports [Statement]
type Exports = [String]
type Imports = [(String, ImportMethod)]
data ImportMethod = As String | Hiding [String] | Importing [String]
deriving (Eq,Ord)
data ImportMethod = As String | Importing [String] | Hiding [String]
deriving (Eq, Ord, Show)
data Pattern = PData String [Pattern]
@ -73,24 +73,27 @@ plist = foldr pcons pnil
ptuple es = PData ("Tuple" ++ show (length es)) es
brkt s = "{ " ++ s ++ " }"
parensIf b s = if b then parens s else s
isOp c = isSymbol c || elem c "+-/*=.$<>:&|^?%#@~!"
instance Show Pattern where
show (PRecord fs) = brkt (intercalate ", " fs)
show (PVar x) = x
show PAnything = "_"
show (PData "Cons" [hd@(PData "Cons" _),tl]) =
parens (show hd) ++ " : " ++ show tl
where parens s = "(" ++ s ++ ")"
show (PData "Cons" [hd,tl]) = show hd ++ " : " ++ show tl
show (PData "Nil" []) = "[]"
show (PData name ps) =
show p =
case p of
PRecord fs -> brkt (intercalate ", " fs)
PVar x -> x
PAnything -> "_"
PData "Cons" [hd@(PData "Cons" _),tl] ->
parens (show hd) ++ " :: " ++ show tl
PData "Cons" [hd,tl] -> show hd ++ " : " ++ show tl
PData "Nil" [] -> "[]"
PData name ps ->
if take 5 name == "Tuple" && all isDigit (drop 5 name) then
parens . intercalate ", " $ map show ps
else (if null ps then id else parens) $ unwords (name : map show ps)
where parens s = "(" ++ s ++ ")"
else parensIf (not (null ps)) $ unwords (name : map show ps)
instance Show Expr where
show e =
let show' (C _ _ e) = parensIf (needsParens e) (show e) in
case e of
IntNum n -> show n
FloatNum n -> show n
@ -116,7 +119,7 @@ instance Show Expr where
where iff (b,e) = show b ++ " -> " ++ show e
sep = concatMap ("\n | " ++)
Let defs e -> "let { "++intercalate " ; " (map show defs)++" } in "++show e
Var x -> x
Var (c:cs) -> if isOp c then parens (c:cs) else c:cs
Case e pats -> "case "++ show e ++" of " ++ brkt (intercalate " ; " pats')
where pats' = map (\(p,e) -> show p ++ " -> " ++ show e) pats
Data name es
@ -138,13 +141,13 @@ getLambdas (C _ _ (Lambda x e)) = (x:xs,e')
where (xs,e') = getLambdas e
getLambdas e = ([],e)
show' (C _ _ e) = if needsParens e then "(" ++ show e ++ ")" else show e
needsParens (Binop _ _ _) = True
needsParens (Lambda _ _) = True
needsParens (App _ _) = True
needsParens (If _ _ _) = True
needsParens (Let _ _) = True
needsParens (Case _ _) = True
needsParens (Data name (x:xs)) = name /= "Cons"
needsParens _ = False
needsParens e =
case e of
Binop _ _ _ -> True
Lambda _ _ -> True
App _ _ -> True
If _ _ _ -> True
Let _ _ -> True
Case _ _ -> True
Data name (x:xs) -> name /= "Cons"
_ -> False

View file

@ -1,5 +1,5 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Guid (guid, run, runAt, GuidCounter) where
module Guid (guid, set, run, runAt, GuidCounter) where
import Control.Monad.State (evalState, State, get, put)
@ -13,5 +13,7 @@ guid = GC $ do n <- get
put (n + 1)
return n
set n = GC (put n)
run = runAt 0
runAt n x = evalState (runGC x) n

View file

@ -0,0 +1,59 @@
module Libraries (libraries, addPrelude) where
import Ast
import Control.Applicative ((<$>),(<*>))
import qualified Data.Map as Map
import Data.List (inits)
import Text.JSON
import LoadLibraries as Libs
addPrelude :: Module -> Module
addPrelude (Module name exs ims stmts) = Module name exs (customIms ++ ims) stmts
where customIms = concatMap addModule prelude
addModule (n, method) = case lookup n ims of
Nothing -> [(n, method)]
Just (As m) -> [(n, method)]
Just _ -> []
prelude = text : map (\n -> (n, Hiding [])) modules
where
text = ("Text", Hiding ["link", "color", "height"])
modules = [ "Prelude", "Signal", "List", "Maybe", "Time"
, "Graphics.Element", "Color", "Graphics.Collage" ]
libraries :: Map.Map String (Map.Map String String)
libraries =
case getLibs of
Error err -> error err
Ok libs -> Map.unionWith Map.union libs nilAndTuples
where nilAndTuples = Map.singleton "Prelude" (Map.fromList pairs)
pairs =
[ ("Cons", "a -> [a] -> [a]")
, ("Nil", "[a]")
] ++ map makeTuple (inits ['a'..'i'])
makeTuple cs =
let name = "Tuple" ++ show (length cs)
in (name, concatMap (\c -> c : " -> ") cs ++
name ++ concatMap (\c -> [' ',c]) cs)
getLibs :: Result (Map.Map String (Map.Map String String))
getLibs = do
obj <- decodeStrict Libs.docs :: Result (JSObject JSValue)
modules <- valFromObj "modules" obj :: Result [JSObject JSValue]
Map.fromList `fmap` mapM getValues modules
get :: String -> JSObject JSValue -> Result String
get = valFromObj
getValue :: JSObject JSValue -> Result (String,String)
getValue obj = (,) <$> get "name" obj <*> get "type" obj
getValues :: JSObject JSValue -> Result (String, Map.Map String String)
getValues obj = do
name <- get "name" obj
vs <- valFromObj "values" obj
vals <- mapM getValue vs
return (name, Map.fromList vals)

View file

@ -0,0 +1,34 @@
module LoadLibraries (docs) where
import Control.DeepSeq (force)
import qualified Control.Exception as E
import Paths_Elm
import System.Directory
import System.FilePath
import System.IO.Unsafe (unsafePerformIO)
-- See stackoverflow discussion for trade-off between using unsafeIO or TemplateHaskell:
-- http://stackoverflow.com/questions/12716215/load-pure-global-variable-from-file
-- Given the awkwardness of including a compile-time generated file
-- vs loading static data, then the unsafeIO seems better.
{-# NOINLINE docs #-}
docs :: String
docs = force $ unsafePerformIO (safeReadDocs =<< getDataFileName "docs.json")
safeReadDocs :: FilePath -> IO String
safeReadDocs name = E.catch (readDocs name) (emitError name)
readDocs :: FilePath -> IO String
readDocs name = do
exists <- doesFileExist name
if exists then readFile name
else ioError . userError $ "File Not Found"
emitError :: FilePath -> IOError -> IO String
emitError name err = do
putStrLn $ "Error! Types for standard library not loaded properly!\n File should be here:" ++ name ++ "\n The file is created and copied by command: cabal install"
putStrLn (show err)
return "{\"modules\":[]}"

View file

@ -28,6 +28,7 @@ table = [ (9, R, ".")
, (3, R, "&&")
, (2, R, "||")
, (0, R, "$")
, (0, R, "<|"), (0, L, "|>")
]
opLevel op = Map.findWithDefault 9 op dict

View file

@ -6,7 +6,7 @@ import Context
import Control.Applicative ((<$>),(<*>))
import Control.Monad
import Control.Monad.State
import Data.Char (isSymbol,isUpper)
import Data.Char (isUpper)
import Rename (deprime)
import Text.Parsec hiding (newline,spaces,State)
import Text.Parsec.Indent
@ -16,7 +16,7 @@ reserveds = [ "if", "then", "else"
, "let", "in"
, "data", "type"
, "module", "where"
, "import", "as", "hiding"
, "import", "as", "hiding", "open"
, "export", "foreign" ]
expecting = flip (<?>)
@ -58,8 +58,6 @@ reserved word =
anyOp :: IParser String
anyOp = betwixt '`' '`' var <|> symOp <?> "infix operator (e.g. +, *, ||)"
isOp c = isSymbol c || elem c "+-/*=.$<>:&|^?%#@~!"
symOp :: IParser String
symOp = do op <- many1 (satisfy isOp)
guard (op `notElem` [ "=", "..", "->", "--", "|", "\8594", ":" ])
@ -162,7 +160,7 @@ whitespace :: IParser ()
whitespace = optional forcedWS <?> ""
freshLine :: IParser [[String]]
freshLine = try (do { many1 newline; many space_nl }) <|> try (many1 space_nl) <?> ""
freshLine = try (many1 newline >> many space_nl) <|> try (many1 space_nl) <?> ""
where space_nl = try $ spaces >> many1 newline
newline :: IParser String

View file

@ -30,18 +30,17 @@ import' :: IParser (String, ImportMethod)
import' = do
reserved "import"
whitespace
open <- optionMaybe (reserved "open")
whitespace
name <- intercalate "." <$> dotSep1 capVar
method <- option (Hiding []) $ try (whitespace >>
(as' <|> hiding' <|> importing'))
return (name, method)
case open of
Just _ -> return (name, Hiding [])
Nothing -> let how = try (whitespace >> (as' <|> importing'))
in (,) name <$> option (Importing []) how
as' :: IParser ImportMethod
as' = reserved "as" >> whitespace >> As <$> capVar <?> "alias for module"
hiding' :: IParser ImportMethod
hiding' = reserved "hiding" >> whitespace >>
Hiding <$> varList <?> "listing of hidden values"
importing' :: IParser ImportMethod
importing' = Importing <$> varList <?> "listing of imported values (x,y,z)"

View file

@ -1,10 +1,10 @@
module Parse.Parser (parseProgram) where
module Parse.Parser (parseProgram, preParse) where
import Ast
import Control.Applicative ((<$>), (<*>))
import Control.Monad
import Data.Char (isSymbol, isDigit)
import Data.List (foldl')
import Data.List (foldl',intercalate)
import Text.Parsec hiding (newline,spaces)
import Parse.Library
@ -14,8 +14,8 @@ import Parse.Modules
import Parse.Foreign
statement = let defs = [ foreignDef, datatype, typeAlias, typeAnnotation ] in
(:[]) <$> choice defs <|> def <?> "datatype or variable definition"
statement = choice (typeAlias:defs) <|> def <?> "datatype or variable definition"
where defs = map ((:[]) <$>) [ foreignDef, datatype, typeAnnotation ]
freshDef = commitIf (freshLine >> (letter <|> char '_')) $ do
freshLine
@ -33,7 +33,20 @@ program = do
optional freshLine ; optional spaces ; eof
return $ Module names exports is statements
parseProgram source =
case iParse program "" source of
parseProgram = setupParser program
preParse :: String -> Either String (String, [String])
preParse = setupParser $ do
optional skip
(,) <$> option "Main" moduleName <*> option [] imprts
where
skip = try (manyTill anyChar (try (string "/**")))
imprts = fmap (map fst) imports `followedBy` freshLine
getName = intercalate "." . fst
moduleName = do optional freshLine
getName <$> moduleDef `followedBy` freshLine
setupParser p source =
case iParse p "" source of
Right result -> Right result
Left err -> Left $ "Parse error at " ++ show err

View file

@ -88,10 +88,11 @@ matchSingle :: Pattern -> CExpr -> Pattern -> GuidCounter [Def]
matchSingle pat exp@(C t s _) p =
let ctx = C t s in
case p of
PData _ ps -> do x <- guid
let v = '_' : show x
dss <- mapM (matchSingle p . ctx $ Var v) ps
return (FnDef v [] exp : concat dss)
PData _ ps -> do
x <- guid
let v = '_' : show x
dss <- mapM (matchSingle pat . ctx $ Var v) ps
return (FnDef v [] exp : concat dss)
PVar x ->
return [ FnDef x [] (ctx $ Case exp [(pat, ctx $ Var x)]) ]

View file

@ -9,8 +9,9 @@ import Data.List (lookup)
import Text.Parsec
import Text.Parsec.Indent
import Context
import Parse.Library
import Types.Types hiding (string,parens)
import Types.Types hiding (parens,string)
import Guid
data ParseType = VarPT String
@ -46,10 +47,7 @@ typeUnambiguous :: IParser ParseType
typeUnambiguous = typeList <|> typeTuple <|> typeRecord
typeSimple :: IParser ParseType
typeSimple = dealias <$> var
where dealias "String" = listPT (VarPT "Char")
dealias "Time" = VarPT "Float"
dealias v = VarPT v
typeSimple = VarPT <$> var
typeApp :: IParser ParseType
typeApp = do name <- capVar <?> "type constructor"
@ -70,8 +68,9 @@ typeConstructor :: IParser (String, [ParseType])
typeConstructor = (,) <$> (capVar <?> "another type constructor")
<*> spacePrefix (typeSimple <|> typeUnambiguous)
typeAlias :: IParser Statement
typeAlias :: IParser [Statement]
typeAlias = do
start <- getPosition
reserved "type" <?> "type alias (type Point = {x:Int, y:Int})"
forcedWS
alias <- capVar
@ -79,9 +78,21 @@ typeAlias = do
whitespace ; string "=" ; whitespace
let n = length args
tipe <- typeExpr
end <- getPosition
case toTypeWith alias (zip args [1..n]) tipe of
Right t -> return (TypeAlias alias [1..n] t)
Left msg -> fail msg
Right t -> return (TypeAlias alias [1..n] t : ctor)
where ctor = case tipe of
RecordPT _ kvs -> [toConstructor start end alias kvs]
_ -> []
toConstructor start end alias kvs =
Definition (FnDef alias args (ctxt (Record rec)))
where
ctxt = pos start end
args = map fst kvs
rec = map (\a -> (a, [], ctxt (Var a))) args
typeAnnotation :: IParser Statement
typeAnnotation = TypeAnnotation <$> try start <*> (toType <$> typeExpr)

View file

@ -0,0 +1,53 @@
module LetBoundVars (letBoundVars) where
import Context
import Ast
class LetBoundVars a where
letBoundVars :: a -> [String]
instance LetBoundVars a => LetBoundVars [a] where
letBoundVars = concatMap letBoundVars
instance LetBoundVars Statement where
letBoundVars stmt =
case stmt of
Definition d -> letBoundVars d
Datatype _ _ tcs -> []
ImportEvent _ e _ _ -> letBoundVars e
ExportEvent _ _ _ -> []
TypeAnnotation _ _ -> []
TypeAlias _ _ _ -> []
instance LetBoundVars Def where
letBoundVars (FnDef n _ e) = n : letBoundVars e
letBoundVars (OpDef _ _ _ e) = letBoundVars e
instance LetBoundVars e => LetBoundVars (Context e) where
letBoundVars (C _ _ e) = letBoundVars e
instance LetBoundVars Expr where
letBoundVars expr =
let f = letBoundVars in
case expr of
IntNum _ -> []
FloatNum _ -> []
Chr _ -> []
Str _ -> []
Boolean _ -> []
Range e1 e2 -> f e1 ++ f e2
Access e _ -> []
Remove e _ -> []
Insert e1 _ e2 -> f e1 ++ f e2
Modify e ps -> f e ++ concatMap (f . snd) ps
Record trps -> concatMap (\(_,_,e) -> f e) trps
Binop op e1 e2 -> f e1 ++ f e2
Lambda x e -> f e
App e1 e2 -> f e1 ++ f e2
If e1 e2 e3 -> concatMap f [e1,e2,e3]
MultiIf ps -> concatMap (\(b,e) -> f b ++ f e) ps
Let defs e -> concatMap letBoundVars defs ++ f e
Var x -> []
Data name es -> concatMap f es
Case e cases -> f e ++ concatMap (f . snd) cases
Markdown _ -> []

View file

@ -112,7 +112,7 @@ binop op ce1@(C t1 s1 e1) ce2@(C t2 s2 e2) =
("||", Boolean True, _) -> Boolean True
("||", Boolean False, _) -> e2
(":", _, _) -> let (C _ _ e) = cons ce1 ce2 in e
("::", _, _) -> let (C _ _ e) = cons ce1 ce2 in e
("++", Str s1, Str s2) -> Str $ s1 ++ s2
("++", Str s1, Binop "++" (C _ _ (Str s2)) ce) ->
@ -124,6 +124,8 @@ binop op ce1@(C t1 s1 e1) ce2@(C t2 s2 e2) =
("++", _, Data "Nil" []) -> e1
("++", Data "Cons" [h,t], _) -> Data "Cons" [h, noContext $ binop "++" t ce2]
("|>", _, _) -> App ce2 ce1
("<|", _, _) -> App ce1 ce2
("$", _, _) -> App ce1 ce2
(".", _, _) ->
Lambda "x" (noContext $

View file

@ -133,6 +133,8 @@ patternExtend pattern env =
first (PData name . reverse) `liftM` foldM f ([], env) ps
where f (rps,env') p = do (rp,env'') <- patternExtend p env'
return (rp:rps, env'')
PRecord fs ->
return (pattern, foldr (\f e n -> if n == f then f else env n) env fs)
patternRename :: (String -> String) -> (Pattern, CExpr) -> GuidCounter (Pattern, CExpr)
patternRename env (p,e) = do

View file

@ -1,5 +1,5 @@
module Types.Alias (dealias, mistakes) where
module Types.Alias (dealias, get, mistakes) where
import Ast
import Control.Arrow (second)
@ -10,37 +10,39 @@ import Types.Substitutions (subst)
import Types.Types
builtins :: [(String,([X],Type))]
builtins = [ ("String", ([], string)) ]
builtins =
let touch = ("t0", time) : map (flip (,) int) ["x","y","x0","y0","id"]
state = [("string", string), ("selectionStart", int), ("selectionEnd", int)]
line = [("color", tipe "Color"), ("width", float),
("cap", tipe "LineCap"), ("join", tipe "LineJoin"),
("miterLimit", float), ("dashing", listOf int),
("dashOffset", int)]
makeRecord fields =
RecordT (Map.fromList $ map (second (:[])) fields) EmptyRecord
in [ ("String", ([], listOf char)),
("Time", ([], float)),
("KeyCode", ([], int)),
("Touch", ([], makeRecord touch)),
("FieldState", ([], makeRecord state)),
("LineStyle", ([], makeRecord line))
]
getAliases :: [Statement] -> Map.Map String ([X],Type)
getAliases stmts = Map.fromList (builtins ++ concatMap getAlias stmts)
get :: [Statement] -> Map.Map String ([X],Type)
get stmts = Map.fromList (builtins ++ concatMap getAlias stmts)
where getAlias stmt = case stmt of
TypeAlias alias xs t -> [(alias, (xs,t))]
_ -> []
dealias :: [Statement] -> [Statement]
dealias stmts = map dealiasS stmts
where
dealiasT :: Type -> Type
dealiasT t =
case t of
ADT name ts -> case Map.lookup name (getAliases stmts) of
Just (xs,t) -> dealiasT (subst (zip xs ts) t)
Nothing -> ADT name (map dealiasT ts)
LambdaT t u -> LambdaT (dealiasT t) (dealiasT u)
RecordT r t -> RecordT (Map.map (map dealiasT) r) (dealiasT t)
_ -> t
dealiasS :: Statement -> Statement
dealiasS s =
case s of
Datatype n xs tcs -> Datatype n xs (map (second (map dealiasT)) tcs)
ExportEvent js elm tipe -> ExportEvent js elm (dealiasT tipe)
ImportEvent js e elm tipe -> ImportEvent js e elm (dealiasT tipe)
TypeAnnotation name tipe -> TypeAnnotation name (dealiasT tipe)
TypeAlias alias xs tipe -> TypeAlias alias xs (dealiasT tipe)
Definition _ -> s
dealias :: Map.Map String ([X],Type) -> Type -> Type
dealias aliases t =
let f = dealias aliases in
case t of
ADT name ts -> case Map.lookup name aliases of
Just (xs,t) -> f (subst (zip xs ts) t)
Nothing -> ADT name (map f ts)
LambdaT t u -> LambdaT (f t) (f u)
RecordT r t -> RecordT (Map.map (map f) r) (f t)
_ -> t
mistakes :: [Statement] -> [String]
mistakes stmts = badKinds stmts ++ dups stmts ++ badOrder stmts
@ -55,7 +57,7 @@ badKinds stmts = map msg (concatMap badS stmts)
badT t =
case t of
ADT name ts ->
case Map.lookup name (getAliases stmts) of
case Map.lookup name (get stmts) of
Just (xs,t) | length xs == length ts -> []
| otherwise -> [name]
Nothing -> concatMap badT ts

View file

@ -4,6 +4,7 @@ module Types.Constrain (constrain) where
import Control.Arrow (second)
import Control.Monad (liftM,mapM,zipWithM,foldM)
import Control.Monad.State (evalState)
import Data.Char (isDigit)
import Data.List (foldl',sort,group,isPrefixOf,intercalate,isSuffixOf)
import qualified Data.Map as Map
import qualified Data.Set as Set
@ -13,32 +14,33 @@ import Context
import Guid
import Types.Types
import Types.Substitutions
import qualified Types.Substitutions as Subs
beta = VarT `liftM` guid
unionA = Map.unionWith (++)
unionsA = Map.unionsWith (++)
getAliases imports hints = hints ++ concatMap aliasesFrom imports'
where imports' = map head . group $ sort imports
aliasesFrom (name,method) =
case method of
As alias -> concatMap (findAlias name alias) hints
Hiding [] -> concatMap (findAlias name "") hints
_ -> []
findAlias mName' mAlias (name,tipe) =
let mName = mName' ++ "." in
case mName `isPrefixOf` name of
True -> [ (mAlias ++ drop (length mName) name, tipe) ]
False -> []
getAliases imports hints = concatMap aliasesFrom imports
where aliasesFrom (name,method) =
let values = concatMap (getValue name) hints
in case method of
As alias -> map (\(n,t) -> (alias ++ "." ++ n, t)) values
Hiding vs -> filter (\(n,t) -> n `notElem` vs) values
Importing vs -> filter (\(n,t) -> n `elem` vs) values
getValue inModule (name,tipe) =
case inModule `isPrefixOf` name of
True -> [ (drop (length inModule + 1) name, tipe) ]
False -> []
findAmbiguous hints hints' assumptions continue =
let potentialDups = map head . filter (\g -> length g > 1) . group $ sort hints'
dups = filter (\k -> Map.member k assumptions) potentialDups
in case dups of
n:_ -> return . Left $ "Error: Ambiguous occurrence of '" ++ n ++ "' could refer to " ++
intercalate ", " (filter (isSuffixOf n) hints)
_ -> continue
findAmbiguous hints assumptions continue =
let potentialDups = map head . filter (\g -> length g > 1) . group . sort $
filter (elem '.') hints
dups = filter (\k -> Map.member k assumptions) potentialDups
in case dups of
n:_ -> return . Left $ "Error: Ambiguous occurrence of '" ++ n ++
"' could refer to " ++
intercalate ", " (filter (isSuffixOf n) hints)
_ -> continue
mergeSchemes :: [Map.Map String Scheme]
-> GuidCounter (TVarMap, ConstraintSet, Map.Map String Scheme)
@ -59,21 +61,20 @@ mergeSchemes schmss = do (ass,css,sss) <- unzip3 `liftM` mapM split kvs
constrain typeHints (Module _ _ imports stmts) = do
(ass,css,schemess) <- unzip3 `liftM` mapM stmtGen stmts
hints <- typeHints
aliasHints <- getAliases imports `liftM` typeHints
(as', cs', schemes) <- mergeSchemes schemess
let constraints = Set.unions (cs':css)
as = unionsA (as':ass)
extraImports = ("Time", Hiding ["read"]) : map (\n -> (n, Hiding []))
["List","Signal","Text","Graphics","Color"]
aliasHints = getAliases (imports ++ extraImports) hints
allHints = Map.union schemes (Map.fromList aliasHints)
insert as n = do v <- guid; return $ Map.insertWith' (\_ x -> x) n [v] as
assumptions <- foldM insert as (Map.keys schemes)
findAmbiguous (map fst hints) (map fst aliasHints) assumptions $ do
findAmbiguous (map fst aliasHints) assumptions $ do
let f k s vs = map (\v -> C (Just k) NoSpan $ v :<<: s) vs
cs = concat . Map.elems $ Map.intersectionWithKey f allHints assumptions
escapees = Map.keys $ Map.difference assumptions allHints
return . Right . (,) escapees $ Set.toList constraints ++ cs
return $ case escapees of
_ -> Right (Set.toList constraints ++ cs)
--_ -> Left ("Undefined variable(s): " ++ intercalate ", " escapees)
type TVarMap = Map.Map String [X]
type ConstraintSet = Set.Set (Context Constraint)
@ -232,13 +233,13 @@ caseGen tipe (p, ce@(C _ span e)) = do
return ( as', Set.union cs cs', t )
patternGen :: (Constraint -> Context Constraint)
-> Type
-> Type -- Type of e in `case e of ...`
-> TVarMap
-> Pattern
-> GuidCounter (TVarMap, ConstraintSet, Type)
patternGen ctxt tipe as pattern =
case pattern of
PAnything -> do b <- beta ; return (Map.empty, Set.empty, b)
PAnything -> do b <- beta ; return ( as, Set.empty, b )
PVar v -> do
b <- beta
let cs = map (ctxt . (b :=:) . VarT) (Map.findWithDefault [] v as)
@ -253,6 +254,15 @@ patternGen ctxt tipe as pattern =
return ( Map.insert name [constr] as'
, Set.insert (ctxt (VarT constr :=: t)) cs
, output )
PRecord fs ->
do pairs <- mapM (\f -> do b <- beta; return (f,b)) fs
b <- beta
let t = RecordT (Map.fromList $ map (second (:[])) pairs) b
mkCs (name,tipe) = map (ctxt . (tipe :=:) . VarT)
(Map.findWithDefault [] name as)
return ( foldr Map.delete as fs
, Set.fromList (ctxt (t :=: tipe) : concatMap mkCs pairs)
, t )
defScheme :: Def -> GuidCounter (Map.Map String [X], Scheme)
@ -273,7 +283,7 @@ defGenHelp name args e = do
let as' = Map.findWithDefault [v] arg as
return $ map (\y -> ctx arg NoSpan $ VarT x :=: VarT y) as'
cs' <- concat `liftM` mapM genCs argDict
scheme <- generalize (concat $ Map.elems as') $
scheme <- Subs.generalize (concat $ Map.elems as') $
Forall (map snd argDict) (cs' ++ Set.toList cs) tipe
return ( as', Set.empty, (name, scheme) )
@ -302,7 +312,7 @@ stmtGen stmt =
, Map.singleton elm (Forall [] [] tipe) )
TypeAnnotation name tipe ->
do schm <- generalize [] (Forall [] [] tipe)
do schm <- Subs.generalize [] =<< Subs.superize name tipe
return (Map.empty, Set.empty, Map.singleton name schm)
TypeAlias _ _ _ -> return (Map.empty, Set.empty, Map.empty)

31
compiler/Types/Hints.hs Normal file
View file

@ -0,0 +1,31 @@
module Types.Hints (hints) where
import Control.Arrow (first)
import Control.Monad (liftM)
import Data.Maybe (catMaybes)
import qualified Data.Map as Map
import Guid
import qualified Libraries as Libs
import Parse.Library (iParse)
import Parse.Types
import qualified Types.Substitutions as Subs
import Types.Types
hints :: GuidCounter [(String, Scheme)]
hints = liftM catMaybes (mapM toScheme values)
where
values :: [(String, String)]
values = addPrefixes (Map.toList (Map.map Map.toList Libs.libraries))
addPrefixes :: [(String,[(String, String)])] -> [(String, String)]
addPrefixes = concatMap (\(m,vs) -> map (first (\n -> m ++ "." ++ n)) vs)
toScheme :: (String, String) -> GuidCounter (Maybe (String, Scheme))
toScheme (name, 't':'y':'p':'e':' ':_) = return Nothing
toScheme (name, 'd':'a':'t':'a':' ':_) = return Nothing
toScheme (name, tipeString) =
let err = "compiler error parsing type of " ++ name ++ ":\n" ++ tipeString in
case iParse (fmap toType typeExpr) err tipeString of
Left err -> error (show err)
Right tipe -> do scheme <- Subs.generalize [] =<< Subs.superize name tipe
return (Just (name, scheme))

View file

@ -13,14 +13,17 @@ import Guid
import Types.Types
import Types.Constrain
import Types.Substitutions
import Types.Alias (dealias)
isSolved ss (C _ _ (t1 :=: t2)) = t1 == t2
isSolved ss (C _ _ (x :<<: _)) = isJust (lookup x ss)
isSolved ss c = False
crush :: Scheme -> GuidCounter (Either String Scheme)
crush (Forall xs cs t) =
do subs <- solver cs Map.empty
type Aliases = Map.Map String ([X],Type)
crush :: Aliases -> Scheme -> GuidCounter (Either String Scheme)
crush aliases (Forall xs cs t) =
do subs <- solver aliases Map.empty cs
return $ do ss' <- subs
let ss = Map.toList ss'
cs' = filter (not . isSolved ss) (subst ss cs)
@ -34,10 +37,11 @@ schemeSubHelp txt span x s t1 rltn t2 = do
| otherwise = do (st, cs) <- concretize s
return (subst [(x,st)] t, cs)
schemeSub x s c = do s' <- crush s
case s' of
Right s'' -> Right `liftM` schemeSub' x s'' c
Left err -> return $ Left err
schemeSub aliases x s c =
do s' <- crush aliases s
case s' of
Right s'' -> Right `liftM` schemeSub' x s'' c
Left err -> return $ Left err
schemeSub' x s c@(C txt span constraint) =
case constraint of
@ -77,29 +81,32 @@ recordConstraints eq fs t fs' t' =
let tipe = RecordT (Map.singleton k bs) (VarT x)
return (cs ++ [eq t tipe])
solver :: [Context Constraint]
solver :: Aliases
-> Map.Map X Type
-> [Context Constraint]
-> GuidCounter (Either String (Map.Map X Type))
solver [] subs = return $ Right subs
solver (C txt span c : cs) subs =
solver _ subs [] = return $ Right subs
solver aliases subs (C txt span c : cs) =
let ctx = C txt span
eq t1 t2 = ctx (t1 :=: t2)
solv = solver aliases subs
uniError' = uniError (\t1 t2 -> solv (eq t1 t2 : cs)) aliases txt span
in case c of
-- Destruct Type-constructors
t1@(ADT n1 ts1) :=: t2@(ADT n2 ts2) ->
if n1 /= n2 then uniError txt span t1 t2 else
solver (zipWith eq ts1 ts2 ++ cs) subs
if n1 == n2 then solv (zipWith eq ts1 ts2 ++ cs)
else uniError' t1 t2
LambdaT t1 t2 :=: LambdaT t1' t2' ->
solver ([ eq t1 t1', eq t2 t2' ] ++ cs) subs
solv ([ eq t1 t1', eq t2 t2' ] ++ cs)
RecordT fs t :=: RecordT fs' t' ->
do cs' <- recordConstraints eq fs t fs' t'
solver (cs' ++ cs) subs
solv (cs' ++ cs)
-- Type-equality
VarT x :=: VarT y
| x == y -> solver cs subs
| x == y -> solv cs
| otherwise ->
case (Map.lookup x subs, Map.lookup y subs) of
(Just (Super xts), Just (Super yts)) ->
@ -108,68 +115,69 @@ solver (C txt span c : cs) subs =
in case Set.toList ts of
[] -> unionError txt span xts yts
[t] -> let cs1 = subst [(x,t),(y,t)] cs in
cs1 `seq` solver cs1 (setXY t subs)
_ -> solver cs $ setXY (Super ts) subs
cs1 `seq` solver aliases (setXY t subs) cs1
_ -> solver aliases (setXY (Super ts) subs) cs
(Just (Super xts), _) ->
let cs2 = subst [(y,VarT x)] cs in
solver cs2 $ Map.insert y (VarT x) subs
solver aliases (Map.insert y (VarT x) subs) cs2
(_, _) ->
let cs3 = subst [(x,VarT y)] cs in
solver cs3 $ Map.insert x (VarT y) subs
solver aliases (Map.insert x (VarT y) subs) cs3
VarT x :=: t -> do
if x `occurs` t then occursError txt span (VarT x) t else
(case Map.lookup x subs of
Nothing -> let cs4 = subst [(x,t)] cs in
solver cs4 . Map.map (subst [(x,t)]) $
Map.insert x t subs
Nothing ->
let cs4 = subst [(x,t)] cs
subs' = Map.map (subst [(x,t)]) $ Map.insert x t subs
in solver aliases subs' cs4
Just (Super ts) ->
let ts' = Set.intersection ts (Set.singleton t) in
case Set.toList ts' of
[] -> solver (ctx (t :<: Super ts) : cs) subs
[] -> solv (ctx (t :<: Super ts) : cs)
[t'] -> let cs5 = subst [(x,t)] cs in
solver cs5 $ Map.insert x t' subs
_ -> solver cs $ Map.insert x (Super ts') subs
Just t' -> solver (ctx (t' :=: t) : cs) subs
solver aliases (Map.insert x t' subs) cs5
_ -> solver aliases (Map.insert x (Super ts') subs) cs
Just t' -> solv (ctx (t' :=: t) : cs)
)
t :=: VarT x -> solver ((ctx (VarT x :=: t)) : cs) subs
t :=: VarT x -> solv ((ctx (VarT x :=: t)) : cs)
t1 :=: t2 | t1 == t2 -> solver cs subs
| otherwise -> uniError txt span t1 t2
t1 :=: t2 | t1 == t2 -> solv cs
| otherwise -> uniError' t1 t2
-- subtypes
VarT x :<: Super ts ->
case Map.lookup x subs of
Nothing -> solver cs $ Map.insert x (Super ts) subs
Nothing -> solver aliases (Map.insert x (Super ts) subs) cs
Just (Super ts') ->
case Set.toList $ Set.intersection ts ts' of
[] -> unionError txt span ts ts'
[t] -> solver (subst [(x,t)] cs) $ Map.insert x t subs
ts'' -> solver cs $
Map.insert x (Super $ Set.fromList ts'') subs
[t] -> solver aliases (Map.insert x t subs) (subst [(x,t)] cs)
ts'' -> solver aliases subs' cs
where subs' = Map.insert x (Super $ Set.fromList ts'') subs
ADT "List" [t] :<: Super ts
| any f (Set.toList ts) -> solver cs subs
| any f (Set.toList ts) -> solv cs
| otherwise -> subtypeError txt span (ADT "List" [t]) (Super ts)
where f (ADT "List" [VarT _]) = True
f (ADT "List" [t']) = t == t'
f (ADT "List" [t']) = dealias aliases t == t'
f _ = False
t :<: Super ts
| Set.member t ts -> solver cs subs
| otherwise -> subtypeError txt span t (Super ts)
| Set.member t ts -> solv cs
| Set.member (dealias aliases t) ts -> solv cs
| otherwise -> subtypeError txt span t (Super ts)
x :<<: s
| any (occurs x) cs ->
do css <- mapM (schemeSub x s) cs
do css <- mapM (schemeSub aliases x s) cs
case lefts css of
err : _ -> return $ Left err
[] -> solver (concat (rights css)) subs
[] -> solv (concat (rights css))
| otherwise ->
do (t,cs7) <- concretize s
let cs'' = (cs ++ ctx (VarT x :=: t) : cs7)
solver cs'' subs
solv (cs ++ ctx (VarT x :=: t) : cs7)
showMsg msg = case msg of
Just str -> "\nIn context: " ++ str
@ -181,10 +189,14 @@ occursError msg span t1 t2 =
, "Occurs check: cannot construct the infinite type:\n"
, show t1, " = ", show t2, showMsg msg ]
uniError msg span t1 t2 =
return . Left $ concat
[ "Type error (" ++ show span ++ "):\n"
, show t1, " is not equal to ", show t2, showMsg msg ]
uniError solveWith aliases msg span t1 t2 =
let t1' = dealias aliases t1
t2' = dealias aliases t2
in if t1 /= t1' || t2 /= t2'
then solveWith t1' t2'
else return . Left $ concat
[ "Type error (" ++ show span ++ "):\n"
, show t1, " is not equal to ", show t2, showMsg msg ]
unionError msg span ts ts' =
return . Left $ concat

View file

@ -1,16 +1,17 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Types.Substitutions (subst,
occurs,
freeVars,
concretize,
rescheme,
generalize) where
generalize,
superize) where
import Ast
import Context
import Control.DeepSeq (NFData (..), deepseq)
import Control.Monad (liftM)
import Data.List (foldl')
import Control.Monad (liftM, liftM2)
import Control.Monad.State (runState, State, get, put)
import Data.List (nub)
import qualified Data.Set as Set
import qualified Data.Map as Map
import Guid
@ -51,13 +52,14 @@ class FreeVars a where
freeVars :: a -> [X]
instance FreeVars Type where
freeVars (VarT v) = [v]
freeVars (LambdaT t1 t2) = freeVars t1 ++ freeVars t2
freeVars (ADT _ ts) = concatMap freeVars ts
freeVars (RecordT fs t) =
freeVars (concat $ Map.elems fs) ++ freeVars t
freeVars EmptyRecord = []
freeVars (Super _ ) = []
freeVars t =
case t of
VarT v -> [v]
LambdaT t1 t2 -> freeVars t1 ++ freeVars t2
ADT _ ts -> concatMap freeVars ts
RecordT fs t -> freeVars (concat $ Map.elems fs) ++ freeVars t
EmptyRecord -> []
Super _ -> []
instance FreeVars Constraint where
freeVars (t1 :=: t2) = freeVars t1 ++ freeVars t2
@ -88,3 +90,47 @@ generalize :: [X] -> Scheme -> GuidCounter Scheme
generalize exceptions (Forall xs cs t) = rescheme (Forall (xs ++ frees) cs t)
where allFrees = Set.fromList $ freeVars t ++ concatMap freeVars cs
frees = Set.toList $ Set.difference allFrees (Set.fromList exceptions)
newtype Superize a = S { runSuper :: State ([X], [X], [X]) a }
deriving (Monad)
superize :: String -> Type -> GuidCounter Scheme
superize name tipe =
do constraints <- liftM concat $
sequence [ mapM (<: nmbr) (nub ns)
, mapM (<: apnd) (nub as)
, mapM (<: comp) (nub cs) ]
return (Forall (concat [ns,as,cs]) constraints tipe')
where
(tipe', (ns,as,cs)) = runState (runSuper (go tipe)) ([],[],[])
t <: super = do x <- guid
return $ C (Just name) NoSpan (VarT t :<: super x)
nmbr t = number
apnd t = appendable t
comp t = comparable t
go :: Type -> Superize Type
go t =
case t of
EmptyRecord -> return t
Super _ -> return t
VarT _ -> return t
LambdaT t1 t2 -> liftM2 LambdaT (go t1) (go t2)
ADT "Number" [VarT t] -> addNumber t
ADT "Appendable" [VarT t] -> addAppendable t
ADT "Comparable" [VarT t] -> addComparable t
ADT name ts -> liftM (ADT name) (mapM go ts)
RecordT fs t -> liftM2 RecordT fs' (go t)
where pairs = Map.toList fs
fs' = do ps <- mapM (\(f,t) -> liftM ((,) f) (mapM go t)) pairs
return (Map.fromList ps)
add :: (X -> ([X],[X],[X]) -> ([X],[X],[X])) -> X -> Superize Type
add f v = S $ do (ns, as, cs) <- get
put $ f v (ns, as, cs)
return (VarT v)
addNumber = add (\n (ns,as,cs) -> (n:ns,as,cs))
addAppendable = add (\a (ns,as,cs) -> (ns,a:as,cs))
addComparable = add (\c (ns,as,cs) -> (ns,as,c:cs))

94
compiler/Types/Types.hs Normal file
View file

@ -0,0 +1,94 @@
module Types.Types where
import Context
import Data.Char (isDigit)
import Data.List (intercalate,isPrefixOf)
import qualified Data.Set as Set
import qualified Data.Map as Map
type X = Int
data Type = LambdaT Type Type
| VarT X
| ADT String [Type]
| EmptyRecord
| RecordT (Map.Map String [Type]) Type
| Super (Set.Set Type)
deriving (Eq, Ord)
data Scheme = Forall [X] [Context Constraint] Type deriving (Eq, Ord, Show)
data Constraint = Type :=: Type
| Type :<: Type
| X :<<: Scheme
deriving (Eq, Ord, Show)
recordT :: [(String,Type)] -> Map.Map String [Type]
recordT fields =
foldl (\r (x,t) -> Map.insertWith (++) x [t] r) Map.empty fields
recordOf :: [(String,Type)] -> Type
recordOf fields = RecordT (recordT fields) EmptyRecord
tipe t = ADT t []
int = tipe "Int"
float = tipe "Float"
time = tipe "Time"
date = tipe "Date"
char = tipe "Char"
bool = tipe "Bool"
text = tipe "Text"
order = tipe "Order"
string = tipe "String"
number = Super $ Set.fromList [ int, float, time ]
appendable t = Super $ Set.fromList [ string, text, listOf (VarT t) ]
comparable t = Super $ Set.fromList [ int, float, char, string, time, date ]
element = tipe "Element"
listOf t = ADT "List" [t]
signalOf t = ADT "Signal" [t]
tupleOf ts = ADT ("Tuple" ++ show (length ts)) ts
maybeOf t = ADT "Maybe" [t]
eitherOf a b = ADT "Either" [a,b]
pairOf t = tupleOf [t,t]
point = pairOf int
infixr ==>
t1 ==> t2 = LambdaT t1 t2
infix 8 -:
name -: tipe = (,) name $ Forall [] [] tipe
parens = ("("++) . (++")")
instance Show Type where
show t =
let addParens (c:cs) =
if notElem ' ' cs || c == '(' then c:cs else parens (c:cs)
in case t of
LambdaT t1@(LambdaT _ _) t2 -> parens (show t1) ++ " -> " ++ show t2
LambdaT t1 t2 -> show t1 ++ " -> " ++ show t2
VarT x -> 't' : show x
ADT "List" [ADT "Char" []] -> "String"
ADT "List" [tipe] -> "[" ++ show tipe ++ "]"
ADT name cs ->
if isTupleString name
then parens . intercalate "," $ map show cs
else name ++ concatMap ((' ':) . addParens . show) cs
Super ts -> "{" ++ (intercalate "," . map show $ Set.toList ts) ++ "}"
EmptyRecord -> "{}"
RecordT fs t ->
start ++ intercalate ", " (concatMap fields $ Map.toList fs) ++ " }"
where field n s = n ++ " : " ++ show s
fields (n,ss) = map (field n) ss
start = case t of
EmptyRecord -> "{ "
_ -> "{ " ++ show t ++ " | "
isTupleString str = "Tuple" `isPrefixOf` str && all isDigit (drop 5 str)

16
compiler/Types/Unify.hs Normal file
View file

@ -0,0 +1,16 @@
module Types.Unify (unify) where
import Control.Monad (liftM)
import qualified Data.Map as Map
import Ast
import Guid
import Types.Constrain
import Types.Solver
import Types.Alias as Alias
unify hints modul@(Module _ _ _ stmts) = run $ do
constraints <- constrain hints modul
either (return . Left) (solver (Alias.get stmts) Map.empty) constraints

View file

@ -1,221 +0,0 @@
(function() {
try{
var $op={};
for(this['i'] in Elm){eval('var '+this['i']+'=Elm[this.i];');}
if (Elm.Automaton) throw new Error("Module name collision, 'Automaton' is already defined.");
Elm.Automaton=function(){
try{
if (!(Elm.Prelude instanceof Object)) throw 'module not found';
} catch(e) {
throw ("Module 'Prelude' is missing. Compile with --make flag or load missing module in a separate JavaScript file.");
}
var hiddenVars={};
for (this['i'] in Elm.Prelude) {
if (hiddenVars[this['i']]) continue;
eval('var ' + this['i'] + ' = Elm.Prelude[this.i];');}
function Automaton_0(a1){
return ["Automaton",a1];}
var Listen_8=["Listen"];
var Ignore_9=["Ignore"];
function DragFrom_10(a1){
return ["DragFrom",a1];}
$op['>>>'] = function(a1_24){
return function(a2_25){
return function(){
var Automaton$m1_26=a1_24;
var m1_27=function(){
switch(Automaton$m1_26[0]){
case "Automaton":
return Automaton$m1_26[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var Automaton$m2_28=a2_25;
var m2_29=function(){
switch(Automaton$m2_28[0]){
case "Automaton":
return Automaton$m2_28[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
return Automaton_0(function(a_32){
return function(){
var Tuple2$bm1__33=m1_27(a_32);
var b_34=function(){
switch(Tuple2$bm1__33[0]){
case "Tuple2":
return Tuple2$bm1__33[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var m1__35=function(){
switch(Tuple2$bm1__33[0]){
case "Tuple2":
return Tuple2$bm1__33[2];
}
throw new Error("Non-exhaustive pattern match in case");}();
return function(){
var Tuple2$cm2__40=m2_29(b_34);
var c_41=function(){
switch(Tuple2$cm2__40[0]){
case "Tuple2":
return Tuple2$cm2__40[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var m2__42=function(){
switch(Tuple2$cm2__40[0]){
case "Tuple2":
return Tuple2$cm2__40[2];
}
throw new Error("Non-exhaustive pattern match in case");}();
return ["Tuple2",c_41,$op['>>>'](m1__35)(m2__42)];}();}();});}();};};
$op['<<<'] = function(a2_47){
return function(a1_48){
return $op['>>>'](a1_48)(a2_47);};};
$op['^>>'] = function(f_49){
return function(a_50){
return $op['>>>'](pure_4(f_49))(a_50);};};
$op['>>^'] = function(a_51){
return function(f_52){
return $op['>>>'](a_51)(pure_4(f_52));};};
$op['^<<'] = function(f_53){
return function(a_54){
return $op['>>>'](a_54)(pure_4(f_53));};};
$op['<<^'] = function(a_55){
return function(f_56){
return $op['>>>'](pure_4(f_56))(a_55);};};
var count_7=init_5(0)(function(__84){
return function(c_85){
return (1+c_85);};});
function run_1(Automaton$m0_14){
return function(input_15){
return function(){
switch(Automaton$m0_14[0]){
case "Automaton":
return lift(fst)(foldp$(function(a_17){
return function(Tuple2$bAutomaton$m_18){
return function(){
switch(Tuple2$bAutomaton$m_18[0]){
case "Tuple2":
switch(Tuple2$bAutomaton$m_18[2][0]){
case "Automaton":
return Tuple2$bAutomaton$m_18[2][1](a_17);
}break;
}
throw new Error("Non-exhaustive pattern match in case");}();};})(Automaton$m0_14[1])(input_15));
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function step_2(Automaton$m_21){
return function(a_22){
return function(){
switch(Automaton$m_21[0]){
case "Automaton":
return Automaton$m_21[1](a_22);
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function combine_3(autos_57){
return Automaton_0(function(a_58){
return function(){
var Tuple2$bsautos__59=unzip(map(function(Automaton$m_62){
return function(){
switch(Automaton$m_62[0]){
case "Automaton":
return Automaton$m_62[1](a_58);
}
throw new Error("Non-exhaustive pattern match in case");}();})(autos_57));
var bs_60=function(){
switch(Tuple2$bsautos__59[0]){
case "Tuple2":
return Tuple2$bsautos__59[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var autos__61=function(){
switch(Tuple2$bsautos__59[0]){
case "Tuple2":
return Tuple2$bsautos__59[2];
}
throw new Error("Non-exhaustive pattern match in case");}();
return ["Tuple2",bs_60,combine_3(autos__61)];}();});}
function pure_4(f_68){
return Automaton_0(function(x_69){
return ["Tuple2",f_68(x_69),pure_4(f_68)];});}
function init_5(s_70){
return function(step_71){
return Automaton_0(function(a_72){
return function(){
var s__73=step_71(a_72)(s_70);
return ["Tuple2",s__73,init_5(s__73)(step_71)];}();});};}
function init__6(s_74){
return function(step_75){
return Automaton_0(function(a_76){
return function(){
var Tuple2$bs__77=step_75(a_76)(s_74);
var b_78=function(){
switch(Tuple2$bs__77[0]){
case "Tuple2":
return Tuple2$bs__77[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var s__79=function(){
switch(Tuple2$bs__77[0]){
case "Tuple2":
return Tuple2$bs__77[2];
}
throw new Error("Non-exhaustive pattern match in case");}();
return ["Tuple2",b_78,init__6(s__79)(step_75)];}();});};}
function vecSub_11(Tuple2$x1y1_86){
return function(Tuple2$x2y2_87){
return function(){
switch(Tuple2$x1y1_86[0]){
case "Tuple2":
return function(){
switch(Tuple2$x2y2_87[0]){
case "Tuple2":
return ["Tuple2",(Tuple2$x1y1_86[1]-Tuple2$x2y2_87[1]),(Tuple2$x1y1_86[2]-Tuple2$x2y2_87[2])];
}
throw new Error("Non-exhaustive pattern match in case");}();
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function stepDrag_12(Tuple2$presspos_92){
return function(Tuple2$dsform_93){
return function(){
switch(Tuple2$presspos_92[0]){
case "Tuple2":
return function(){
switch(Tuple2$dsform_93[0]){
case "Tuple2":
return function(){
function wrap_98(ds__99){
return ["Tuple2",Tuple2$dsform_93[2],["Tuple2",ds__99,Tuple2$dsform_93[2]]];}
return function(){
switch(Tuple2$dsform_93[1][0]){
case "DragFrom":
return (Tuple2$presspos_92[1]?["Tuple2",uncurry(move)(vecSub_11(Tuple2$presspos_92[2])(Tuple2$dsform_93[1][1]))(Tuple2$dsform_93[2]),["Tuple2",DragFrom_10(Tuple2$dsform_93[1][1]),Tuple2$dsform_93[2]]]:function(){
var form__101=uncurry(move)(vecSub_11(Tuple2$presspos_92[2])(Tuple2$dsform_93[1][1]))(Tuple2$dsform_93[2]);
return ["Tuple2",form__101,["Tuple2",Listen_8,form__101]];}());
case "Ignore":
return wrap_98((Tuple2$presspos_92[1]?Ignore_9:Listen_8));
case "Listen":
return wrap_98((not(Tuple2$presspos_92[1])?Listen_8:(isWithin(Tuple2$presspos_92[2])(Tuple2$dsform_93[2])?DragFrom_10(Tuple2$presspos_92[2]):Ignore_9)));
}
throw new Error("Non-exhaustive pattern match in case");}();}();
}
throw new Error("Non-exhaustive pattern match in case");}();
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function draggable_13(form_102){
return init__6(["Tuple2",Listen_8,form_102])(stepDrag_12);}
return {$op : {'>>>' : $op['>>>'], '<<<' : $op['<<<'], '^>>' : $op['^>>'], '>>^' : $op['>>^'], '^<<' : $op['^<<'], '<<^' : $op['<<^']},
run:run_1,
step:step_2,
combine:combine_3,
pure:pure_4,
init:init_5,
init$:init__6,
count:count_7,
draggable:draggable_13};}();
Elm.main=function(){
return Elm.Automaton.main;};
} catch (e) {
Elm.main=function() {
var msg = ('<br/><h2>Your browser may not be supported. Are you using a modern browser?</h2>' + '<br/><span style="color:grey">Runtime Error in Automaton module:<br/>' + e + '</span>');
document.body.innerHTML = Elm.Text.monospace(msg);throw e;};}}());

View file

@ -1,64 +0,0 @@
/*! Char !*/
/*[Classification]*/
/** isUpper : Char -> Bool
Selects upper case letters.
**/
/** isLower : Char -> Bool
Selects lower case letters.
**/
/** isDigit : Char -> Bool
Selects ASCII digits (0..9).
**/
/** isOctDigit : Char -> Bool
Selects ASCII octal digits (0..7).
**/
/** isHexDigit : Char -> Bool
Selects ASCII hexadecimal digits (0..9a..fA..F).
**/
/*[Conversion]*/
/** toUpper : Char -> Char
Convert to upper case.
**/
/** toLower : Char -> Char
Convert to lower case.
**/
/** toLocaleUpper : Char -> Char
Convert to upper case, according to any locale-specific case mappings.
**/
/** toLocaleLower : Char -> Char
Convert to lower case, according to any locale-specific case mappings.
**/
/** toCode : Char -> Int
Convert to unicode.
**/
/** fromCode : Int -> Char
Convert from unicode.
**/
Elm.Char = function() {
function isBetween(lo,hi) { return function(chr) {
var c = chr.charCodeAt(0);
return lo <= c && c <= hi;
};
}
var isDigit = isBetween('0'.charCodeAt(0),'9'.charCodeAt(0));
var chk1 = isBetween('a'.charCodeAt(0),'f'.charCodeAt(0));
var chk2 = isBetween('A'.charCodeAt(0),'F'.charCodeAt(0));
return {fromCode : function(c) { return String.fromCharCode(c); },
toCode : function(c) { return c.charCodeAt(0); },
toUpper : function(c) { return c.toUpperCase(); },
toLower : function(c) { return c.toLowerCase(); },
toLocaleUpper : function(c) { return c.toLocaleUpperCase(); },
toLocaleLower : function(c) { return c.toLocaleLowerCase(); },
isLower : isBetween('a'.charCodeAt(0),'z'.charCodeAt(0)),
isUpper : isBetween('A'.charCodeAt(0),'Z'.charCodeAt(0)),
isDigit : isDigit,
isOctDigit : isBetween('0'.charCodeAt(0),'7'.charCodeAt(0)),
isHexDigit : function(c) { return isDigit(c) || chk1(c) || chk2(c); }
};
}();

View file

@ -1,32 +0,0 @@
Elm.Date = function() {
function dateNow() { return new window.Date; }
function readDate(str) {
var d = new window.Date(Elm.JavaScript.castStringToJSString(str));
if (isNaN(d.getTime())) return ["Nothing"];
return ["Just",d];
}
var dayTable = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var monthTable = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
return {
read : readDate,
year : function(d) { return d.getFullYear(); },
month : function(d) { return [monthTable[d.getMonth()]]; },
day : function(d) { return d.getDate(); },
hour : function(d) { return d.getHours(); },
minute : function(d) { return d.getMinutes(); },
second : function(d) { return d.getSeconds(); },
dayOfWeek : function(d) { return [dayTable[d.getDay()]]; },
toTime : function(d) { return d.getTime(); },
Mon : ["Mon"], Tue : ["Tue"], Wed : ["Wed"],
Thu : ["Thu"], Fri : ["Fri"], Sat : ["Sat"], Sun : ["Sun"],
Jan : ["Jan"], Feb : ["Feb"], Mar : ["Mar"], Apr : ["Apr"],
May : ["May"], Jun : ["Jun"], Jul : ["Jul"], Aug : ["Aug"],
Sep : ["Sep"], Oct : ["Oct"], Nov : ["Nov"], Dec : ["Dec"]
};
}();

View file

@ -1,485 +0,0 @@
Elm.Dict=function(){
var compare = Elm.Prelude.compare;
var uncurry = Elm.Prelude.uncurry;
var Nothing = Elm.Prelude.Nothing;
var Just = Elm.Prelude.Just;
var not = Elm.Prelude.not;
var eq = Elm.Prelude.eq;
var isJust=Elm.Maybe.isJust;
var Red_0=['Red'];
var Black_1=['Black'];
function RBNode_2(a1){
return function(a2){
return function(a3){
return function(a4){
return function(a5){
return ['RBNode',a1,a2,a3,a4,a5];};};};};}
var RBEmpty_3=['RBEmpty'];
function min_6(t_42){
return function(){
switch(t_42[0]){
case 'RBEmpty':
throw '(min RBEmpty) is not defined';
case 'RBNode':
switch(t_42[4][0]){
case 'RBEmpty':
return ['Tuple2',t_42[2],t_42[3]];
}
return min_6(t_42[4]);
}
throw new Error("Non-exhaustive pattern match in case");}();}
function lookup_7(k_46){
return function(t_47){
return function(){
switch(t_47[0]){
case 'RBEmpty':
return Nothing;
case 'RBNode':
return function(){
var case12=compare(k_46)(t_47[2]);
switch(case12[0]){
case 'EQ':
return Just(t_47[3]);
case 'GT':
return lookup_7(k_46)(t_47[5]);
case 'LT':
return lookup_7(k_46)(t_47[4]);
}
throw new Error("Non-exhaustive pattern match in case");}();
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function findWithDefault_8(base_52){
return function(k_53){
return function(t_54){
return function(){
switch(t_54[0]){
case 'RBEmpty':
return base_52;
case 'RBNode':
return function(){
var case19=compare(k_53)(t_54[2]);
switch(case19[0]){
case 'EQ':
return t_54[3];
case 'GT':
return findWithDefault_8(base_52)(k_53)(t_54[5]);
case 'LT':
return findWithDefault_8(base_52)(k_53)(t_54[4]);
}
throw new Error("Non-exhaustive pattern match in case");}();
}
throw new Error("Non-exhaustive pattern match in case");}();};};}
function member_9(k_59){
return function(t_60){
return isJust(lookup_7(k_59)(t_60));};}
function rotateLeft_10(t_61){
return function(){
switch(t_61[0]){
case 'RBNode':
switch(t_61[5][0]){
case 'RBNode':
return RBNode_2(t_61[1])(t_61[5][2])(t_61[5][3])(RBNode_2(Red_0)(t_61[2])(t_61[3])(t_61[4])(t_61[5][4]))(t_61[5][5]);
}break;
}
throw 'rotateLeft of a node without enough children';}();}
function rotateRight_11(t_71){
return function(){
switch(t_71[0]){
case 'RBNode':
switch(t_71[4][0]){
case 'RBNode':
return RBNode_2(t_71[1])(t_71[4][2])(t_71[4][3])(t_71[4][4])(RBNode_2(Red_0)(t_71[2])(t_71[3])(t_71[4][5])(t_71[5]));
}break;
}
throw 'rotateRight of a node without enough children';}();}
function rotateLeftIfNeeded_12(t_81){
return function(){
switch(t_81[0]){
case 'RBNode':
switch(t_81[5][0]){
case 'RBNode':
switch(t_81[5][1][0]){
case 'Red':
return rotateLeft_10(t_81);
}break;
}break;
}
return t_81;}();}
function rotateRightIfNeeded_13(t_82){
return function(){
switch(t_82[0]){
case 'RBNode':
switch(t_82[4][0]){
case 'RBNode':
switch(t_82[4][1][0]){
case 'Red':
switch(t_82[4][4][0]){
case 'RBNode':
switch(t_82[4][4][1][0]){
case 'Red':
return rotateRight_11(t_82);
}break;
}break;
}break;
}break;
}
return t_82;}();}
function otherColor_14(c_83){
return function(){
switch(c_83[0]){
case 'Black':
return Red_0;
case 'Red':
return Black_1;
}
throw new Error("Non-exhaustive pattern match in case");}();}
function color_flip_15(t_84){
return function(){
switch(t_84[0]){
case 'RBNode':
switch(t_84[4][0]){
case 'RBNode':
switch(t_84[5][0]){
case 'RBNode':
return RBNode_2(otherColor_14(t_84[1]))(t_84[2])(t_84[3])(RBNode_2(otherColor_14(t_84[4][1]))(t_84[4][2])(t_84[4][3])(t_84[4][4])(t_84[4][5]))(RBNode_2(otherColor_14(t_84[5][1]))(t_84[5][2])(t_84[5][3])(t_84[5][4])(t_84[5][5]));
}break;
}break;
}
throw 'color_flip called on a RBEmpty or RBNode with a RBEmpty child';}();}
function color_flipIfNeeded_16(t_98){
return function(){
switch(t_98[0]){
case 'RBNode':
switch(t_98[4][0]){
case 'RBNode':
switch(t_98[4][1][0]){
case 'Red':
switch(t_98[5][0]){
case 'RBNode':
switch(t_98[5][1][0]){
case 'Red':
return color_flip_15(t_98);
}break;
}break;
}break;
}break;
}
return t_98;}();}
function fixUp_17(t_99){
return color_flipIfNeeded_16(rotateRightIfNeeded_13(rotateLeftIfNeeded_12(t_99)));}
function ensureBlackRoot_18(t_100){
return function(){
switch(t_100[0]){
case 'RBNode':
switch(t_100[1][0]){
case 'Red':
return RBNode_2(Black_1)(t_100[2])(t_100[3])(t_100[4])(t_100[5]);
}break;
}
return t_100;}();}
function insert_19(k_105){
return function(v_106){
return function(t_107){
return function(){
function ins_108(t_109){
return function(){
switch(t_109[0]){
case 'RBEmpty':
return RBNode_2(Red_0)(k_105)(v_106)(RBEmpty_3)(RBEmpty_3);
case 'RBNode':
return function(){
var h_115=function(){
var case114=compare(k_105)(t_109[2]);
switch(case114[0]){
case 'EQ':
return RBNode_2(t_109[1])(t_109[2])(v_106)(t_109[4])(t_109[5]);
case 'GT':
return RBNode_2(t_109[1])(t_109[2])(t_109[3])(t_109[4])(ins_108(t_109[5]));
case 'LT':
return RBNode_2(t_109[1])(t_109[2])(t_109[3])(ins_108(t_109[4]))(t_109[5]);
}
throw new Error("Non-exhaustive pattern match in case");}();
return fixUp_17(h_115);}();
}
throw new Error("Non-exhaustive pattern match in case");}();}
return ensureBlackRoot_18(ins_108(t_107));}();};};}
function singleton_20(k_116){
return function(v_117){
return insert_19(k_116)(v_117)(RBEmpty_3);};}
function isRed_21(t_118){
return function(){
switch(t_118[0]){
case 'RBNode':
switch(t_118[1][0]){
case 'Red':
return true;
}break;
}
return false;}();}
function isRedLeft_22(t_119){
return function(){
switch(t_119[0]){
case 'RBNode':
switch(t_119[4][0]){
case 'RBNode':
switch(t_119[4][1][0]){
case 'Red':
return true;
}break;
}break;
}
return false;}();}
function isRedLeftLeft_23(t_120){
return function(){
switch(t_120[0]){
case 'RBNode':
switch(t_120[4][0]){
case 'RBNode':
switch(t_120[4][4][0]){
case 'RBNode':
switch(t_120[4][4][1][0]){
case 'Red':
return true;
}break;
}break;
}break;
}
return false;}();}
function isRedRight_24(t_121){
return function(){
switch(t_121[0]){
case 'RBNode':
switch(t_121[5][0]){
case 'RBNode':
switch(t_121[5][1][0]){
case 'Red':
return true;
}break;
}break;
}
return false;}();}
function isRedRightLeft_25(t_122){
return function(){
switch(t_122[0]){
case 'RBNode':
switch(t_122[5][0]){
case 'RBNode':
switch(t_122[5][4][0]){
case 'RBNode':
switch(t_122[5][4][1][0]){
case 'Red':
return true;
}break;
}break;
}break;
}
return false;}();}
function moveRedLeft_26(t_123){
return function(){
var t__124=color_flip_15(t_123);
return function(){
switch(t__124[0]){
case 'RBNode':
return function(){
switch(t__124[5][0]){
case 'RBNode':
switch(t__124[5][4][0]){
case 'RBNode':
switch(t__124[5][4][1][0]){
case 'Red':
return color_flip_15(rotateLeft_10(RBNode_2(t__124[1])(t__124[2])(t__124[3])(t__124[4])(rotateRight_11(t__124[5]))));
}break;
}break;
}
return t__124;}();
}
return t__124;}();}();}
function moveRedRight_27(t_130){
return function(){
var t__131=color_flip_15(t_130);
return (isRedLeftLeft_23(t__131)?color_flip_15(rotateRight_11(t__131)):t__131);}();}
function moveRedLeftIfNeeded_28(t_132){
return ((not(isRedLeft_22(t_132))&&not(isRedLeftLeft_23(t_132)))?moveRedLeft_26(t_132):t_132);}
function moveRedRightIfNeeded_29(t_133){
return ((not(isRedRight_24(t_133))&&not(isRedRightLeft_25(t_133)))?moveRedRight_27(t_133):t_133);}
function deleteMin_30(t_134){
return function(){
function del_135(t_136){
return function(){
switch(t_136[0]){
case 'RBNode':
switch(t_136[4][0]){
case 'RBEmpty':
return RBEmpty_3;
}break;
}
return function(){
var case198=moveRedLeftIfNeeded_28(t_136);
switch(case198[0]){
case 'RBEmpty':
return RBEmpty_3;
case 'RBNode':
return fixUp_17(RBNode_2(case198[1])(case198[2])(case198[3])(del_135(case198[4]))(case198[5]));
}
throw new Error("Non-exhaustive pattern match in case");}();}();}
return ensureBlackRoot_18(del_135(t_134));}();}
function remove_31(k_142){
return function(t_143){
return function(){
function eq_and_noRightNode_144(t_150){
return function(){
switch(t_150[0]){
case 'RBNode':
switch(t_150[5][0]){
case 'RBEmpty':
return eq(k_142,t_150[2]);
}break;
}
return false;}();}
function eq_145(t_152){
return function(){
switch(t_152[0]){
case 'RBNode':
return eq(k_142,t_152[2]);
}
return false;}();}
function delLT_146(t_154){
return function(){
var case216=moveRedLeftIfNeeded_28(t_154);
switch(case216[0]){
case 'RBEmpty':
throw 'delLT on RBEmpty';
case 'RBNode':
return fixUp_17(RBNode_2(case216[1])(case216[2])(case216[3])(del_149(case216[4]))(case216[5]));
}
throw new Error("Non-exhaustive pattern match in case");}();}
function delEQ_147(t_160){
return function(){
switch(t_160[0]){
case 'RBEmpty':
throw 'delEQ called on a RBEmpty';
case 'RBNode':
return function(){
var Tuple2$k_v__164=min_6(t_160[5]);
var k__165=function(){
switch(Tuple2$k_v__164[0]){
case 'Tuple2':
return Tuple2$k_v__164[1];
}
throw new Error("Non-exhaustive pattern match in case");}();
var v__166=function(){
switch(Tuple2$k_v__164[0]){
case 'Tuple2':
return Tuple2$k_v__164[2];
}
throw new Error("Non-exhaustive pattern match in case");}();
return fixUp_17(RBNode_2(t_160[1])(k__165)(v__166)(t_160[4])(deleteMin_30(t_160[5])));}();
}
throw new Error("Non-exhaustive pattern match in case");}();}
function delGT_148(t_171){
return function(){
switch(t_171[0]){
case 'RBEmpty':
throw 'delGT called on a RBEmpty';
case 'RBNode':
return fixUp_17(RBNode_2(t_171[1])(t_171[2])(t_171[3])(t_171[4])(del_149(t_171[5])));
}
throw new Error("Non-exhaustive pattern match in case");}();}
function del_149(t_177){
return function(){
switch(t_177[0]){
case 'RBEmpty':
return RBEmpty_3;
case 'RBNode':
return ((compare(k_142)(t_177[2])[0] === 'LT')?delLT_146(t_177):function(){
var u_179=(isRedLeft_22(t_177)?rotateRight_11(t_177):t_177);
return (eq_and_noRightNode_144(u_179)?u_179[4]:function(){
var t__180=moveRedRightIfNeeded_29(t_177);
return (eq_145(t__180)?delEQ_147(t__180):delGT_148(t__180));}());}());
}
throw new Error("Non-exhaustive pattern match in case");}();}
return (member_9(k_142)(t_143)?ensureBlackRoot_18(del_149(t_143)):t_143);}();};}
function map_32(f_181){
return function(t_182){
return function(){
switch(t_182[0]){
case 'RBEmpty':
return RBEmpty_3;
case 'RBNode':
return RBNode_2(t_182[1])(t_182[2])(f_181(t_182[3]))(map_32(f_181)(t_182[4]))(map_32(f_181)(t_182[5]));
}
throw new Error("Non-exhaustive pattern match in case");}();};}
function foldl_33(f_188){
return function(acc_189){
return function(t_190){
return function(){
switch(t_190[0]){
case 'RBEmpty':
return acc_189;
case 'RBNode':
return foldl_33(f_188)(f_188(t_190[2])(t_190[3])(foldl_33(f_188)(acc_189)(t_190[4])))(t_190[5]);
}
throw new Error("Non-exhaustive pattern match in case");}();};};}
function foldr_34(f_195){
return function(acc_196){
return function(t_197){
return function(){
switch(t_197[0]){
case 'RBEmpty':
return acc_196;
case 'RBNode':
return foldr_34(f_195)(f_195(t_197[2])(t_197[3])(foldr_34(f_195)(acc_196)(t_197[5])))(t_197[4]);
}
throw new Error("Non-exhaustive pattern match in case");}();};};}
function union_35(t1_202){
return function(t2_203){
return foldl_33(insert_19)(t2_203)(t1_202);};}
function intersect_36(t1_204){
return function(t2_205){
return foldl_33(function(k_206){
return function(v_207){
return function(t_208){
return (member_9(k_206)(t2_205)?insert_19(k_206)(v_207)(t_208):t_208);};};})(empty_4)(t1_204);};}
function diff_37(t1_209){
return function(t2_210){
return foldl_33(function(k_211){
return function(v_212){
return function(t_213){
return remove_31(k_211)(t_213);};};})(t1_209)(t2_210);};}
function keys_38(t_214){
return foldr_34(function(k_215){
return function(v_216){
return function(acc_217){
return ['Cons',k_215,acc_217];};};})(['Nil'])(t_214);}
function values_39(t_218){
return foldr_34(function(k_219){
return function(v_220){
return function(acc_221){
return ['Cons',v_220,acc_221];};};})(['Nil'])(t_218);}
function toList_40(t_222){
return foldr_34(function(k_223){
return function(v_224){
return function(acc_225){
return ['Cons',['Tuple2',k_223,v_224],acc_225];};};})(['Nil'])(t_222);}
function fromList_41(assocs_226){
return Elm.List.foldl(uncurry(insert_19))(empty_4)(assocs_226);}
var empty_4=RBEmpty_3;
return {$op : {},
empty:empty_4,
lookup:lookup_7,
findWithDefault:findWithDefault_8,
member:member_9,
insert:insert_19,
singleton:singleton_20,
remove:remove_31,
map:map_32,
foldl:foldl_33,
foldr:foldr_34,
union:union_35,
intersect:intersect_36,
diff:diff_37,
keys:keys_38,
values:values_39,
toList:toList_40,
fromList:fromList_41};}();

View file

@ -1,75 +0,0 @@
/*! Either
!*/
Elm.Either = function() {
/*[Definition]*/
/** data Either a b = Left a | Right b
Represents any data that can take two different types.
This can also be used for error handling (`Either String a`) where
error messages are stored on the left, and the correct values
("right" values) are stored on the right.
**/
function Left(a1) { return ['Left',a1]; }
function Right(a1){ return ['Right',a1]; }
/*[Basics]*/
/** either : (a -> c) -> (b -> c) -> Either a b -> c
Apply the first function to a `Left` and the second function to a `Right`.
This allows the extraction of a value from an `Either`.
**/
function either(f){ return function(g){ return function(e){
switch(e[0]){
case 'Left': return f(e[1]);
case 'Right': return g(e[1]);
}
};};}
/** isLeft : Either a b -> Bool
True if the value is a `Left`.
**/
function isLeft(e) { return e[0] == 'Left'; }
/** isRight : Either a b -> Bool
True if the value is a `Right`.
**/
function isRight(e) { return e[0] == 'Right'; }
/*[With Lists]*/
function get(es) { return Elm.List.map(function(x){return x[1];})(es); }
/** lefts : [Either a b] -> [a]
Keep only the values held in `Left` values.
**/
function lefts(es) { return get(Elm.List.filter(isLeft)(es)); }
/** rights : [Either a b] -> [a]
Keep only the values held in `Right` values.
**/
function rights(es) { return get(Elm.List.filter(isRight)(es)); }
/** partition : [Either a b] -> ([a],[b])
Split into two lists, lefts on the left and rights on the right.
So we have the equivalence:
partition es == (lefts es, rights es)
**/
function partition(es) {
var lrs = Elm.List.partition(isLeft)(es);
lrs[1] = get(lrs[1]);
lrs[2] = get(lrs[2]);
return lrs;
}
return {Left:Left,
Right:Right,
either:either,
isLeft:isLeft,
isRight:isRight,
lefts:lefts,
rights:rights,
partition:partition};
}();

View file

@ -1,317 +0,0 @@
var Collage = function() {
var JS = Elm.JavaScript;
function tracePoints(ctx,points) {
var i = points.length - 1;
if (i <= 0) return;
ctx.moveTo(points[i][1], points[i][2]);
while (i--) { ctx.lineTo(points[i][1], points[i][2]); }
}
function solid(ctx,color,points) {
tracePoints(ctx,points);
ctx.strokeStyle = Elm.Color.extract(color);
ctx.stroke();
};
function filled(ctx,color,points) {
tracePoints(ctx,points);
ctx.fillStyle = Elm.Color.extract(color);
ctx.fill();
}
function textured(redo,ctx,src,points) {
var img = new Image();
img.src = JS.castStringToJSString(src);
img.onload = redo;
tracePoints(ctx,points);
ctx.fillStyle = ctx.createPattern(img,'repeat');
ctx.fill();
}
function customLine(pattern,ctx,color,points) {
if (pattern.length === 0) { pattern = [8,4]; }
customLineHelp(ctx, pattern, points);
ctx.strokeStyle = Elm.Color.extract(color);
ctx.stroke();
};
var customLineHelp = function(ctx, pattern, points) {
var i = points.length - 1;
if (i <= 0) return;
var x0 = points[i][1], y0 = points[i][2];
var x1=0, y1=0, dx=0, dy=0, remaining=0, nx=0, ny=0;
var pindex = 0, plen = pattern.length;
var draw = true, segmentLength = pattern[0];
ctx.moveTo(x0,y0);
while (i--) {
x1 = points[i][1]; y1 = points[i][2];
dx = x1 - x0; dy = y1 - y0;
remaining = Math.sqrt(dx * dx + dy * dy);
while (segmentLength <= remaining) {
x0 += dx * segmentLength / remaining;
y0 += dy * segmentLength / remaining;
ctx[draw ? 'lineTo' : 'moveTo'](x0, y0);
// update starting position
dx = x1 - x0; dy = y1 - y0;
remaining = Math.sqrt(dx * dx + dy * dy);
// update pattern
draw = !draw;
pindex = (pindex + 1) % plen;
segmentLength = pattern[pindex];
}
if (remaining > 0) {
ctx[draw ? 'lineTo' : 'moveTo'](x1, y1);
segmentLength -= remaining;
}
x0 = x1; y0 = y1;
}
};
function drawLine(ctx,form) {
var points = form[3][1];
switch(form[1][0]) {
case "Solid" : return solid(ctx,form[2],points);
case "Dotted": return customLine([3,3],ctx,form[2],points);
case "Dashed": return customLine([8,4],ctx,form[2],points);
case "Custom": return customLine(form[1][1],ctx,form[2],points);
}
};
function drawShape(redo,ctx,shapeStyle,color,points) {
switch(shapeStyle[0]) {
case "Filled": return filled(ctx,color,points);
case "Outlined": return solid(ctx,color,points);
case "Textured": return textured(redo,ctx,shapeStyle[1],points);
case "CustomOutline":
return customLine(shapeStyle[1],ctx,color,points);
}
};
function drawImage(redo,ctx,w,h,src) {
var img = new Image();
img.onload = redo;
img.src = JS.castStringToJSString(src);
ctx.drawImage(img,-w/2,-h/2,w,h);
}
function renderForm(redo,ctx,theta,scale,x,y,form) {
ctx.save();
if (x !== 0 || y !== 0) ctx.translate(x,y);
if (theta !== ~~theta) ctx.rotate(2*Math.PI*theta);
if (scale !== 1) ctx.scale(scale,scale);
ctx.beginPath();
switch(form[0]) {
case "FLine": drawLine(ctx,form); break;
case "FShape": drawShape(redo,ctx,form[1],form[2],form[3][1]); break;
case "FImage": drawImage(redo,ctx,form[1],form[2],form[3]); break;
}
ctx.restore();
};
function renderForms(redo,ctx,w,h,forms) {
ctx.clearRect(0,0,w,h);
for (var i = forms.length; i--; ) {
var f = forms[i];
renderForm(redo,ctx,f[1],f[2],f[3][1],f[3][2],f[4]);
}
}
function collageForms(w,h,forms) {
var canvas = Render.newElement('canvas');
w = ~~w;
h = ~~h;
canvas.style.width = w + 'px';
canvas.style.height = h + 'px';
canvas.style.display = "block";
canvas.width = w;
canvas.height = h;
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
function redo() { renderForms(this,ctx,w,h,forms); }
renderForms(redo,ctx,w,h,forms);
return canvas;
}
canvas.innerHTML = "Your browser does not support the canvas element.";
return canvas;
};
function applyTransforms(theta,scale,x,y,w,h,e) {
var t = "translate(" + (x - w / 2) + "px,"+ (y - h / 2) + "px)";
var r = theta === (~~theta) ? "" : "rotate(" + theta*360 + "deg)";
var s = scale === 1 ? "" : "scale(" + scale + "," + scale + ")";
var transforms = t + " " + s + " " + r;
e.style.transform = transforms;
e.style.msTransform = transforms;
e.style.MozTransform = transforms;
e.style.webkitTransform = transforms;
e.style.OTransform = transforms;
}
function collageElement(w,h,theta,scale,x,y,elem) {
var e = Render.render(elem);
applyTransforms(theta,scale,x,y,elem[3],elem[4],e);
var div = Render.newElement('div');
Render.addTo(div,e);
div.style.width = (~~w) + "px";
div.style.height = (~~h) + "px";
div.style.overflow = "hidden";
return div;
}
function collage(w,h,formss) {
if (formss.length === 0) { return collageForms(w,h,[]); }
var elems = new Array(formss.length);
for (var i = formss.length; i--; ) {
var f = formss[i];
if (typeof f[0] === "string") {
elems[i] = collageElement(w,h,f[1],f[2],f[3][1],f[3][2],f[4][1]);
} else {
elems[i] = collageForms(w,h,f);
}
}
if (formss.length === 1) { return elems[0]; }
return Render.flowWith(Render.goIn,function(x){return x},elems);
}
function updateFormSet(node,currSet,nextSet) {
if (Value.eq(nextSet,currSet)) return;
var w = node.style.width.slice(0,-2) - 0;
var h = node.style.height.slice(0,-2) - 0;
if (typeof nextSet[0] === "object") {
if (typeof currSet[0] === "object") {
if (node.getContext) {
var ctx = node.getContext('2d');
function redo() { renderForms(this,ctx,w,h,nextSet); }
return renderForms(redo,ctx,w,h,nextSet);
}
}
var newNode = collageForms(w,h,nextSet);
newNode.style.position = 'absolute';
return node.parentNode.replaceChild(newNode,node);
}
node.style.width = (~~w) + "px";
node.style.height = (~~h) + "px";
var f = nextSet;
var next = nextSet[4][1];
Render.update(node.firstChild, currSet[4][1], next);
applyTransforms(f[1],f[2],f[3][1],f[3][2],next[3],next[4],node.firstChild);
}
// assumes that the form sets are the same length.
function updateCollage(node,currs,nexts) {
if (nexts.length === 1) {
return updateFormSet(node,currs[0],nexts[0]);
}
var kids = node.childNodes;
var len = kids.length;
for (var i = len; i--; ) {
updateFormSet(kids[len-i-1], currs[i], nexts[i]);
}
}
function style(clr,n,list) {
return ["Tuple2",
'<span style="font-size:100%;color:' + clr + ';">' + n + '</span>',
list];
}
function insideForm(point) { return function(form) {
if (!inBoundsOf(point[1],point[2],form)) return false;
var hw, hh;
switch (form[4][0]) {
case "FShape": return insideShape(point,form[1],form[2],form[3],form[4][3][1]);
case "FLine": return false;
case "FImage":
hw = form[4][1] / 2;
hh = form[4][2] / 2;
break;
case "FElement":
hw = form[4][1][3] / 2;
hh = form[4][1][4] / 2;
break;
}
return insideShape(point,form[1],form[2],form[3],
[ [null, hw, hh],
[null,-hw, hh],
[null,-hw,-hh],
[null, hw,-hh],
[null, hw, hh] ]);
};
}
function inBoundsOf(px,py,form) {
if (form.length < 6) {
var fx = form[3][1], fy = form[3][2];
var radiusSquared = 0;
var scale = form[2];
switch (form[4][0]) {
case "FShape":
var points = form[4][3][1];
for (var i = points.length; --i; ) {
var p = points[i];
radiusSquared = Math.max(radiusSquared, p[1]*p[1] + p[2]*p[2]);
}
radiusSquared *= scale * scale;
break;
case "FLine":
break;
case "FImage":
var x = scale * form[4][1] / 2;
var y = scale * form[4][2] / 2;
radiusSquared = x*x + y*y;
break;
case "FElement":
var x = scale * form[4][1][3] / 2;
var y = scale * form[4][1][4] / 2;
radiusSquared = x*x + y*y;
break;
}
form.push(function(px,py) {
var dx = px - fx;
var dy = py - fy;
return dx*dx + dy*dy < radiusSquared + 1;
});
}
return form[5](px,py);
}
function insideShape(point,theta,scale,pos,points) {
var counter = 0;
var list = ["Nil"];
var p1,p2;
var x = (point[1] - pos[1]) / scale;
var y = (point[2] - pos[2]) / scale;
if (theta !== 0) {
var t = -2 * Math.PI * theta;
var nx = x * Math.cos(t) - y * Math.sin(t);
y = x * Math.sin(t) + y * Math.cos(t);
x = nx;
}
if (points.length === 0) { return false; }
p1 = points[0];
for (var i = points.length - 1; i--; ) {
p2 = points[i];
var p1x = p1[1], p1y = p1[2], p2x = p2[1], p2y = p2[2];
if (p1y < p2y) {var ymin=p1y, ymax=p2y;} else {var ymin=p2y, ymax=p1y;}
if (p1x < p2x) {var xmin=p1x, xmax=p2x;} else {var xmin=p2x, xmax=p1x;}
if (ymin < y && y <= ymax && x <= xmax) {
if (x <= xmin || x <= ((y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x)) {
++counter;
}
}
p1 = p2;
}
return (counter % 2) === 1;
}
return {collage:collage, updateCollage:updateCollage, insideForm:insideForm};
}();

View file

@ -1,86 +0,0 @@
Elm.Color = function() {
function Color_0(a1) {
return function(a2) {
return function(a3) {
return function(a4) {
return["Color", a1, a2, a3, a4]
}
}
}
}
var rgba_1 = Color_0;
var red_3 = ["Color",255,0,0,1];
var green_4 = ["Color",0,255,0,1];
var blue_5 = ["Color",0,0,255,1];
var yellow_6 = ["Color",255,255,0,1];
var cyan_7 = ["Color",0,255,255,1];
var magenta_8 = ["Color",255,0,255,1];
var black_9 = ["Color",0,0,0,1];
var white_10 = ["Color",255,255,255,1];
var gray_11 = ["Color",128,128,128,1];
var grey_12 = ["Color",128,128,128,1];
function rgb_2(r_13) {
return function(g_14) {
return function(b_15) {
return ["Color",r_13,g_14,b_15,1]
}
}
}
function extract(c) {
if (c[4] === 1) { return 'rgb(' + c[1] + ',' + c[2] + ',' + c[3] + ')'; }
return 'rgba(' + c[1] + ',' + c[2] + ',' + c[3] + ',' + c[4] + ')';
}
function complement(rgb) {
var hsv = toHSV(rgb);
hsv.hue = (hsv.hue + 180) % 360;
return toRGB(hsv);
}
function hsva(h) { return function(s) { return function(v) { return function(a) {
var clr = toRGB({hue:h, saturation:s, value:v});
clr[4] = a;
return clr;
}; }; };
}
function hsv(h) { return function(s) { return function(v) {
return toRGB({hue:h, saturation:s, value:v}); }; }; }
function toHSV(rgb) {
var hsv = {};
var r = rgb[1] / 255.0, g = rgb[2] / 255.0, b = rgb[3] / 255.0;
var M = Math.max(r,g,b);
var m = Math.min(r,g,b);
var c = M - m;
var h = 0;
if (c === 0) { h = 0; }
else if (M === r) { h = ((g - b) / c) % 6; }
else if (M === g) { h = ((b - r) / c) + 2; }
else if (M === b) { h = ((r - g) / c) + 4; }
h *= 60;
return { value : M, hue : h, saturation : (M === 0 ? 0 : c / M) };
}
function between(lo,hi,x) { return lo <= x && x < hi; }
function norm(n) { return Math.round(n*255); }
function toRGB(hsv) {
var c = hsv.value * hsv.saturation;
var hue = hsv.hue / 60;
var x = c * (1 - Math.abs((hue % 2) - 1));
var r = 0, g = 0, b = 0;
if (between(0,1,hue)) { r = c; g = x; b = 0; }
else if (between(1,2,hue)) { r = x; g = c; b = 0; }
else if (between(2,3,hue)) { r = 0; g = c; b = x; }
else if (between(3,4,hue)) { r = 0; g = x; b = c; }
else if (between(4,5,hue)) { r = x; g = 0; b = c; }
else if (between(5,6,hue)) { r = c; g = 0; b = x; }
var m = hsv.value - c;
return ["Color", norm(r+m), norm(g+m), norm(b+m), 1 ];
}
return{rgba:rgba_1, rgb:rgb_2, hsva:hsva, hsv:hsv, red:red_3, green:green_4, blue:blue_5, yellow:yellow_6, cyan:cyan_7, magenta:magenta_8, black:black_9, white:white_10, gray:gray_11, grey:grey_12,complement:complement,extract:extract}
}();

View file

@ -1,517 +0,0 @@
Elm.Graphics = function() {
for (this['i'] in Elm.List) {
eval('var ' + this['i'] + ' = Elm.List[this.i];');
}
var JS = Elm.JavaScript;
var DLeft_0 = ["DLeft"];
var DRight_1 = ["DRight"];
var DUp_2 = ["DUp"];
var DDown_3 = ["DDown"];
var DIn_4 = ["DIn"];
var DOut_5 = ["DOut"];
function Absolute_12(a1) {
return["Absolute", a1]
}
function Relative_13(a1) {
return["Relative", a1]
}
var Near_14 = ["Near"];
var Mid_15 = ["Mid"];
var Far_16 = ["Far"];
function Position_17(a1) {
return function(a2) {
return["Position", a1, a2]
}
}
function PositionTL_18(a1) {
return function(a2) {
return["PositionTL", a1, a2]
}
}
function PositionTR_19(a1) {
return function(a2) {
return["PositionTR", a1, a2]
}
}
function PositionBL_20(a1) {
return function(a2) {
return["PositionBL", a1, a2]
}
}
function PositionBR_21(a1) {
return function(a2) {
return["PositionBR", a1, a2]
}
}
function Element_37(id,e,w,h,o,c,l) {
return["Element", id, e, w, h, o, c, l ]
}
function EText_39(a1) {
return function(a2) {
return["EText", a1, a2]
}
}
function EImage_40(a1) {
return["EImage", JS.castStringToJSString(a1)]
}
function EVideo_41(a1) {
return["EVideo", JS.castStringToJSString(a1)]
}
function EFittedImage_42(a1) {
return["EFittedImage", JS.castStringToJSString(a1)]
}
function EFlow_43(a1) {
return function(a2) {
return["EFlow", a1, JS.castListToJSArray(a2)]
}
}
function ECollage_44(a1) {
return function(a2) {
return function(a3) {
return["ECollage", a1, a2, Value.groupForms(a3)]
}
}
}
var EEmpty_45 = ["EEmpty"];
function EContainer_46(a1) {
return function(a2) {
return["EContainer", a1, a2]
}
}
var Solid_68 = ["Solid"];
var Dotted_69 = ["Dotted"];
var Dashed_70 = ["Dashed"];
function Custom_71(a1) {
return["Custom", JS.castListToJSArray(a1)]
}
var Filled_72 = ["Filled"];
var Outlined_73 = ["Outlined"];
function CustomOutline_74(a1) {
return["CustomOutline", JS.castListToJSArray(a1)]
}
function Line_75(a1) {
return["Line", JS.castListToJSArray(a1)]
}
function Shape_78(a1) {
return function(a2) {
var points = JS.castListToJSArray(a1);
if (points.length > 0) { points.push(points[0]); }
return["Shape", points, a2];
}
}
function Form_84(a1) {
return function(a2) {
return function(a3) {
return function(a4) {
return["Form", a1, a2, a3, a4]
}
}
}
}
function FLine_85(a1) {
return function(a2) {
return function(a3) {
return["FLine", a1, a2, a3]
}
}
}
function FShape_86(a1) {
return function(a2) {
return function(a3) {
return["FShape", a1, a2, a3]
}
}
}
function FImage_87(a1) {
return function(a2) {
return function(a3) {
return["FImage", a1, a2, JS.castStringToJSString(a3)]
}
}
}
function FElement_88(a1) {
return["FElement", a1]
}
var left_6 = DLeft_0;
var right_7 = DRight_1;
var down_8 = DDown_3;
var up_9 = DUp_2;
var inward_10 = DIn_4;
var outward_11 = DOut_5;
var topLeft_22 = Position_17(Near_14)(Near_14);
var topRight_23 = Position_17(Far_16)(Near_14);
var bottomLeft_24 = Position_17(Near_14)(Far_16);
var bottomRight_25 = Position_17(Far_16)(Far_16);
var midLeft_26 = Position_17(Near_14)(Mid_15);
var midRight_27 = Position_17(Far_16)(Mid_15);
var midTop_28 = Position_17(Mid_15)(Near_14);
var midBottom_29 = Position_17(Mid_15)(Far_16);
var middle_30 = Position_17(Mid_15)(Mid_15);
function middleAt(a1) {
return function(a2) {
return["PositionAt", a1, a2]
}
}
var topLeftAt_31 = PositionTL_18;
var topRightAt_32 = PositionTR_19;
var bottomLeftAt_33 = PositionBL_20;
var bottomRightAt_34 = PositionBR_21;
var absolute_35 = Absolute_12;
var relative_36 = Relative_13;
function newElement_38(e,w,h,o,c,l) { return Element_37(Guid.guid(),e,w,h,o,c,l); }
function basicNewElement(e,w,h) { return Element_37(Guid.guid(),e,w,h,1,["Nothing"],["Nothing"]); }
var line_76 = Line_75;
var polygon_79 = Shape_78;
function sprite_96(src) {
return function(w) {
return function(h) {
return function(pos) {
return Form_84(0)(1)(pos)(FImage_87(w)(h)(src))
}
}
}
}
function toForm_97(pos) {
return function(e) {
return Form_84(0)(1)(pos)(FElement_88(e))
}
}
function width_47(w__101) {
return function(e) {
var be = e[2];
switch(be[0]) {
case "EImage":
case "EVideo":
return newElement_38(e[2],w__101,e[4] * w__101 / e[3], e[5], e[6], e[7]);
case "EText":
var p = Value.getTextSize(w__101,e[4],be[2]);
return newElement_38(e[2], w__101, p[1], e[5], e[6], e[7]);
}
return newElement_38(e[2], w__101, e[4], e[5], e[6], e[7]);
}
}
function height_48(h__108) {
return function(e) {
var be = e[2];
switch(be[0]) {
case "EImage":
case "EVideo":
return newElement_38(e[2], e[3] * h__108 / e[4], h__108, e[5], e[6], e[7]);
}
return newElement_38(e[2], e[3], h__108, e[5], e[6], e[7]);
}
}
function size_49(w) {
return function(h) {
return function(e) {
return newElement_38(e[2], w, h, e[5], e[6], e[7]);
}
}
}
function opacity_50(o) {
return function(e) {
return newElement_38(e[2], e[3], e[4], o, e[6], e[7]);
}
}
function color_51(c) {
return function(e) {
return newElement_38(e[2], e[3], e[4], e[5], ["Just",c], e[7]);
}
}
function link(lnk) {
return function(e) {
return newElement_38(e[2], e[3], e[4], e[5], e[6], ["Just", JS.castStringToJSString(lnk)]);
}
}
function widthOf_52(e) { return ~~e[3]; }
function heightOf_53(e) { return ~~e[4]; }
function sizeOf_54(e) { return["Tuple2", ~~e[3], ~~e[4]] }
function text_56(txt) {
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("left")(txt), p[0], p[1])
}
function plainText(str) {
var txt = Value.toText(str);
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("left")(txt),p[0],p[1])
}
function asText(v) {
var txt = Elm.Text.monospace(Value.toText(Value.show(v)));
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("left")(txt),p[0],p[1])
}
function centeredText(txt) {
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("center")(txt),p[0],p[1])
}
function justifiedText(txt) {
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("justify")(txt),p[0],p[1])
}
function rightedText(txt) {
var p = Value.getTextSize(0,0,txt);
return basicNewElement(EText_39("right")(txt),p[0],p[1])
}
function image_57(w) {
return function(h) {
return function(src) {
return basicNewElement(EImage_40(src),w,h)
}
}
}
function images(srcs) {
var pics = Elm.Signal.constant(spacer_66(0)(0));
var update = Elm.Signal.lift(function(src) {
src = JS.castStringToJSString(src);
var img = new Image();
img.onload = function() {
Dispatcher.notify(pics.id,
image_57(this.width)(this.height)(src));
};
img.src = src;
})(srcs);
function f(x) { return function(y) { return x; } }
var combine = Elm.Signal.lift2(f)(pics)(update);
return combine;
}
function video_58(w) {
return function(h) {
return function(src) {
return basicNewElement(EVideo_41(src),w,h)
}
}
}
function fittedImage_59(w_147) {
return function(h_148) {
return function(s_149) {
return basicNewElement(EFittedImage_42(s_149),w_147,h_148)
}
}
}
function flow_60(dir_150) {
return function(es_151) {
return function() {
var w_152 = function() {
var ws_154 = map(widthOf_52)(es_151);
return function(case1) {
var case0 = case1;
switch(case0[0]) {
case "DLeft":
return sum(ws_154);
case "DRight":
return sum(ws_154)
}
return maximum(ws_154)
}(dir_150)
}();
var h_153 = function() {
var hs_155 = map(heightOf_53)(es_151);
return function(case3) {
var case2 = case3;
switch(case2[0]) {
case "DDown":
return sum(hs_155);
case "DUp":
return sum(hs_155)
}
return maximum(hs_155)
}(dir_150)
}();
return basicNewElement(EFlow_43(dir_150)(es_151), w_152, h_153)
}()
}
}
function above_61(e1_156) {
return function(e2_157) {
return basicNewElement(EFlow_43(DDown_3)(["Cons", e1_156, ["Cons", e2_157, ["Nil"]]]), Math.max(widthOf_52(e1_156),widthOf_52(e2_157)), heightOf_53(e1_156) + heightOf_53(e2_157));
}
}
function below_62(e1_158) {
return function(e2_159) {
return basicNewElement(EFlow_43(DDown_3)(["Cons", e2_159, ["Cons", e1_158, ["Nil"]]]), Math.max(widthOf_52(e1_158),widthOf_52(e2_159)), heightOf_53(e1_158) + heightOf_53(e2_159));
}
}
function beside_63(e1_160) {
return function(e2_161) {
return basicNewElement(EFlow_43(DRight_1)(["Cons", e1_160, ["Cons", e2_161, ["Nil"]]]), widthOf_52(e1_160) + widthOf_52(e2_161), Math.max(heightOf_53(e1_160),heightOf_53(e2_161)));
}
}
function layers_64(es_162) {
return basicNewElement(EFlow_43(DOut_5)(es_162), maximum(map(widthOf_52)(es_162)), maximum(map(heightOf_53)(es_162)))
}
function collage_65(w_163) {
return function(h_164) {
return function(forms_165) {
return basicNewElement(ECollage_44(w_163)(h_164)(forms_165),w_163,h_164)
}
}
}
function spacer_66(w_166) {
return function(h_167) {
return basicNewElement(EEmpty_45,w_166,h_167)
}
}
function container_67(w_169) {
return function(h_170) {
return function(pos_168) {
return function(e_171) {
return basicNewElement(EContainer_46(pos_168)(e_171),w_169,h_170)
}
}
}
}
function segment_77(p1_172) {
return function(p2_173) {
return Line_75(["Cons", p1_172, ["Cons", p2_173, ["Nil"]]])
}
}
function rect_80(w_174) {
return function(h_175) {
return function(pos_176) {
return Shape_78(["Cons", ["Tuple2", 0 - w_174 / 2, 0 - h_175 / 2],
["Cons", ["Tuple2", 0 - w_174 / 2, h_175 / 2],
["Cons", ["Tuple2", w_174 / 2, h_175 / 2],
["Cons", ["Tuple2", w_174 / 2, 0 - h_175 / 2], ["Nil"]]]]])(pos_176)
}
}
}
function oval_81(w_177) {
return function(h_178) {
return function(pos_179) {
return function() {
var n_180 = 50;
return function() {
function f_181(i_182) {
return["Tuple2", w_177 / 2 * Math.cos(2 * (Math.PI / n_180) * i_182), h_178 / 2 * Math.sin(2 * (Math.PI / n_180) * i_182)];
}
return Shape_78(map(f_181)(function() {
var lo = 0;
var hi = n_180 - 1;
var lst = ["Nil"];
if(lo <= hi) {
do {
lst = ["Cons", hi, lst]
}while(hi-- > lo)
}
return lst
}()))(pos_179)
}()
}()
}
}
}
function circle_82(r_183) {
return oval_81(2 * r_183)(2 * r_183)
}
function ngon_83(n_184) {
return function(r_185) {
return function(pos_186) {
return function() {
var m_187 = n_184;
return function() {
function f_188(i_189) {
return["Tuple2", r_185 * Math.cos(2 * (Math.PI / m_187) * i_189), r_185 * Math.sin(2 * (Math.PI / m_187) * i_189)];
}
return Shape_78(map(f_188)(function() {
var lo = 0;
var hi = n_184 - 1;
var lst = ["Nil"];
if(lo <= hi) {
do {
lst = ["Cons", hi, lst]
}while(hi-- > lo)
}
return lst
}()))(pos_186)
}()
}()
}
}
}
function solid_89(clr_190) {
return function(ln_191) {
return Form_84(0)(1)(["Tuple2", 0, 0])(FLine_85(Solid_68)(clr_190)(ln_191))
}
}
function dotted_90(clr_192) {
return function(ln_193) {
return Form_84(0)(1)(["Tuple2", 0, 0])(FLine_85(Dotted_69)(clr_192)(ln_193))
}
}
function dashed_91(clr_194) {
return function(ln_195) {
return Form_84(0)(1)(["Tuple2", 0, 0])(FLine_85(Dashed_70)(clr_194)(ln_195))
}
}
function customLine_92(pattern_196) {
return function(clr_197) {
return function(ln_198) {
return Form_84(0)(1)(["Tuple2", 0, 0])(FLine_85(Custom_71(pattern_196))(clr_197)(ln_198))
}
}
}
function filled_93(clr) {
return function(shape) {
return Form_84(0)(1)(shape[2])(FShape_86(Filled_72)(clr)(shape));
}
}
function outlined_94(clr) {
return function(shape) {
return Form_84(0)(1)(shape[2])(FShape_86(Outlined_73)(clr)(shape));
}
}
function customOutline_95(pattern) {
return function(clr) {
return function(shape) {
return Form_84(0)(1)(shape[2])(FShape_86(CustomOutline_74(pattern))(clr)(shape));
}
}
}
function textured(src) {
return function(shape) {
return Form_84(0)(1)(shape[2])(FShape_86(["Textured",src])(null)(shape));
}
}
function rotate_98(t_212) {
return function(Form$thetascaleposform_213) {
return function(case5) {
var case0 = case5;
switch(case0[0]) {
case "Form":
var case1 = case0[1], case2 = case0[2], case3 = case0[3], case4 = case0[4];
return Form_84(t_212 + case1)(case2)(case3)(case4)
}
throw new Error("Non-exhaustive pattern match in case");
}(Form$thetascaleposform_213)
}
}
function scale_99(s) {
return function(form) {
return Form_84(form[1])(s * form[2])(form[3])(form[4])
}
}
function move_100(x_224) {
return function(y_225) {
return function(Form$thetascaleTuple2$pxpyform_226) {
return function(case7) {
var case0 = case7;
switch(case0[0]) {
case "Form":
var case1 = case0[1], case2 = case0[2], case3 = case0[3], case4 = case0[4];
switch(case3[0]) {
case "Tuple2":
var case5 = case3[1], case6 = case3[2];
return Form_84(case1)(case2)(["Tuple2", x_224 + case5, y_225 + case6])(case4)
}
break
}
throw new Error("Non-exhaustive pattern match in case");
}(Form$thetascaleTuple2$pxpyform_226)
}
}
}
return{left:left_6, right:right_7, down:down_8, up:up_9, inward:inward_10, outward:outward_11, topLeft:topLeft_22, topRight:topRight_23, bottomLeft:bottomLeft_24, bottomRight:bottomRight_25, midLeft:midLeft_26, midRight:midRight_27, midTop:midTop_28, midBottom:midBottom_29, middle:middle_30, middleAt:middleAt, topLeftAt:topLeftAt_31, topRightAt:topRightAt_32, bottomLeftAt:bottomLeftAt_33, bottomRightAt:bottomRightAt_34, absolute:absolute_35, relative:relative_36, width:width_47, height:height_48, size:size_49, opacity:opacity_50,
color:color_51, link:link, widthOf:widthOf_52, heightOf:heightOf_53, sizeOf:sizeOf_54, text:text_56, asText:asText, plainText:plainText, centeredText:centeredText, justifiedText:justifiedText, rightedText:rightedText, image:image_57, images:images, video:video_58, fittedImage:fittedImage_59, flow:flow_60, above:above_61, below:below_62, beside:beside_63, layers:layers_64, collage:collage_65, spacer:spacer_66, container:container_67, line:line_76, segment:segment_77, polygon:polygon_79, rect:rect_80, oval:oval_81, circle:circle_82, ngon:ngon_83, solid:solid_89, dotted:dotted_90, dashed:dashed_91, customLine:customLine_92, filled:filled_93,
outlined:outlined_94, customOutline:customOutline_95, textured:textured, sprite:sprite_96, toForm:toForm_97, rotate:rotate_98, scale:scale_99, move:move_100,
isWithin: Collage.insideForm}
}();

View file

@ -1,277 +0,0 @@
var Render = function(){
function newElement(elementType) {
var e = document.createElement(elementType);
e.style.padding = "0";
e.style.margin = "0";
return e;
};
function addTo(container, elem) {
container.appendChild(elem);
};
function makeText(pos,txt) {
var e = newElement('div');
e.innerHTML = txt;
e.style.textAlign = pos;
return e;
};
function image(src) {
var img = newElement('img');
img.src = src;
img.name = src;
img.style.display = "block";
return img;
}
function fittedImage(w,h,src) {
var e = newElement('div');
e.style.width = w + 'px';
e.style.height = h + 'px';
e.style.position = "relative";
e.style.overflow = "hidden";
var img = newElement('img');
img.onload = function() {
img.style.position = 'absolute';
img.style.margin = 'auto';
var sw = w, sh = h;
if (w / h > this.width / this.height) {
sh = Math.round(this.height * w / this.width);
} else {
sw = Math.round(this.width * h / this.height);
}
img.style.width = sw + 'px';
img.style.height = sh + 'px';
img.style.left = ((w - sw) / 2) + 'px';
img.style.top = ((h - sh) / 2) + 'px';
};
img.src = src;
img.name = src;
addTo(e,img);
return e;
};
var video = function(src) {
var e = newElement('video');
e.controls = "controls";
var source = newElement('source');
source.src = src;
var segs = src.split('.');
source.type = "video/" + segs[segs.length-1];
addTo(e, source);
e.style.display = "block";
return e;
};
function divify(e) {
var div = newElement('div');
addTo(div, e);
return div;
};
function goDown(e) {
return e //.tagName === "DIV" ? e : divify(e);
};
function goRight(e) {
e.style.styleFloat = "left";
e.style.cssFloat = "left";
return e;
};
function goIn(e) {
e.style.position = 'absolute';
return e;
};
function flowWith(f, prep, elist) {
var container = newElement('div');
for (var i = elist.length; i--; ) {
addTo(container, f(prep(elist[i])));
}
return container;
};
function flow(dir,elist) {
switch(dir) {
case "DDown": elist = elist.slice(0).reverse();
case "DUp": return flowWith(goDown,render,elist);
case "DRight": elist = elist.slice(0).reverse();
case "DLeft": return flowWith(goRight,render,elist);
case "DOut": elist = elist.slice(0).reverse();
case "DIn": return flowWith(goIn,render,elist);
};
};
function toPos(pos) {
switch(pos[0]) {
case "Absolute": return pos[1] + "px";
case "Relative": return (pos[1] * 100) + "%";
}
}
function setPos(pos,e) {
e.style.position = 'absolute';
e.style.margin = 'auto';
switch(pos[0]) {
case "Position":
if (pos[1][0] !== "Far") e.style.left = 0;
if (pos[1][0] !== "Near") e.style.right = 0;
if (pos[2][0] !== "Far") e.style.top = 0;
if (pos[2][0] !== "Near") e.style.bottom = 0;
break;
case "PositionAt":
e.style.top = toPos(pos[2]);
e.style.left = toPos(pos[1]);
var shift = "translate(" + ~~(-e.style.width.slice(0,-2) / 2) + "px," + ~~(-e.style.height.slice(0,-2) / 2) + "px)";
e.style.transform = shift;
e.style.msTransform = shift;
e.style.MozTransform = shift;
e.style.webkitTransform = shift;
e.style.OTransform = shift;
break;
default:
var p = pos[0].slice(-2);
e.style[p[0] === "T" ? 'top' : 'bottom'] = toPos(pos[2]);
e.style[p[1] === "L" ? 'left' : 'right'] = toPos(pos[1]);
}
}
function container(pos,elem) {
var e = render(elem);
setPos(pos,e);
var div = newElement('div');
div.style.position = "relative";
div.style.overflow = "hidden";
addTo(div,e);
return div;
};
function render(elem) {
var e = {};
switch(elem[2][0]) {
case "EText": e = makeText(elem[2][1],elem[2][2]); break;
case "EImage": e = image(elem[2][1]); break;
case "EVideo": e = video(elem[2][1]); break;
case "EFittedImage": e = fittedImage(elem[3],elem[4],elem[2][1]); break;
case "EFlow": e = flow(elem[2][1][0],elem[2][2]); break;
case "ECollage": e = Collage.collage(elem[2][1],elem[2][2],elem[2][3]); break;
case "EEmpty": e = newElement('div'); break;
case "EContainer": e = container(elem[2][1],elem[2][2]); break;
case "EHtml":
e = elem[2][1];
if (e.type !== 'button') {
var p = Value.getExcess(e);
elem[3] -= p[0];
elem[4] -= p[1];
}
break;
case "EExternalHtml":
e = newElement('div');
addTo(e, elem[2][1]);
break;
}
e.id = elem[1];
e.style.width = (~~elem[3]) + 'px';
e.style.height = (~~elem[4]) + 'px';
if (elem[5] !== 1) { e.style.opacity = elem[5]; }
if (elem[6][0] === "Just") {
e.style.backgroundColor = Elm.Color.extract(elem[6][1]);
}
if (elem[7][0] === "Just") {
var a = newElement('a');
a.href = elem[7][1];
addTo(a,e);
return a;
}
return e;
};
function update(node,curr,next) {
if (node.tagName === 'A') { node = node.firstChild; }
if (curr[1] === next[1]) return;
if (curr[2][0] !== next[2][0]) {
return node.parentNode.replaceChild(render(next),node);
}
var nextE = next[2], currE = curr[2];
switch(nextE[0]) {
case "EText":
if (nextE[1] !== currE[1]) node.style.textAlign = nextE[1];
if (nextE[2] !== currE[2]) node.innerHTML = nextE[2];
break;
case "EImage":
if (nextE[1] !== currE[1]) node.src = nextE[1];
break;
case "EVideo":
case "EFittedImage":
if (!Value.eq(nextE,currE) || next[3]!==curr[3] || next[4]!==curr[4]) {
return node.parentNode.replaceChild(render(next),node);
}
break;
case "ECollage":
if (nextE[1] !== currE[1] || nextE[2] !== currE[2] || nextE[3].length !== currE[3].length) {
return node.parentNode.replaceChild(render(next),node);
}
Collage.updateCollage(node,currE[3],nextE[3]);
break;
case "EFlow":
if (nextE[1] !== currE[1]) {
return node.parentNode.replaceChild(render(next),node);
}
var nexts = nextE[2];
var kids = node.childNodes;
if (nexts.length !== kids.length) {
return node.parentNode.replaceChild(render(next),node);
}
var currs = currE[2];
var goDir = function(x) { return x; };
switch(nextE[1][0]) {
case "DDown": case "DUp": goDir = goDown; break;
case "DRight": case "DLeft": goDir = goRight; break;
case "DOut": case "DIn": goDir = goIn; break;
}
for (var i = kids.length; i-- ;) {
update(kids[i],currs[i],nexts[i]);
goDir(kids[i]);
}
break;
case "EContainer":
update(node.childNodes[0],currE[2],nextE[2]);
setPos(nextE[1],node.childNodes[0]);
break;
case "EEmpty":
break;
case "EHtml":
if (next[1] !== curr[1]) {
var e = render(next);
node.parentNode.replaceChild(e,node);
node = e;
}
if (e.type !== 'button') {
var p = Value.getExcess(node);
next[3] -= p[0];
next[4] -= p[1];
}
break;
case "EExternalHtml":
if (next[1] !== curr[1])
node.parentNode.replaceChild(render(next),node);
break;
}
if (next[3] !== curr[3]) node.style.width = (~~next[3]) + 'px';
if (next[4] !== curr[4]) node.style.height = (~~next[4]) + 'px';
if (next[5] !== curr[5]) node.style.opacity = next[5];
if (next[6].length === 2) {
var clr = Elm.Color.extract(next[6][1]);
if (clr !== node.style.backgroundColor) node.style.backgroundColor = clr;
}
if (next[7].length === 2) {
if (curr[7].length === 1 || next[7][1] !== curr[7][1]) node.parentNode.href = next[7][1];
}
next[1] = curr[1];
}
return {render:render,update:update,addTo:addTo,newElement:newElement,flowWith:flowWith,goIn:goIn};
}();

View file

@ -1,8 +0,0 @@
Elm = {};
var Guid = function() {
var counter = 0;
var guid = function() { counter += 1; return counter; };
return {guid : guid};
}();

View file

@ -1,482 +0,0 @@
Elm.List = function() {
var throwError = function(f) {
throw new Error("Function '" + f + "' expecting a list!");
}
function length(xs) {
var out = 0;
while (xs[0] === "Cons") {
out += 1;
xs = xs[2];
}
return out;
};
var reverse = foldl(function(x_72) {
return function(y_73) {
return["Cons", x_72, y_73]
}
})(["Nil"]);
var concat = foldr(function(x_74) {
return function(y_75) {
return Value.append(x_74, y_75)
}
})(["Nil"]);
var and = foldl(function(x_77) {
return function(y_78) {
return x_77 && y_78
}
})(true);
var or = foldl(function(x_79) {
return function(y_80) {
return x_79 || y_80
}
})(false);
var sum = foldl(function(x_89) {
return function(y_90) {
return x_89 + y_90
}
})(0);
var product = foldl(function(x_91) {
return function(y_92) {
return x_91 * y_92
}
})(1);
var maximum = foldl1(function(x) { return function(y) { return Math.max(x,y) } });
var minimum = foldl1(function(x) { return function(y) { return Math.min(x,y) } });
function head(v) {
if (v[0] !== "Cons") {
throw new Error("Error: 'head' only accepts lists of length greater than one.");
}
return v[1];
}
function tail(v) {
if (v[0] !== "Cons") {
throw new Error("Error: 'tail' only accepts lists of length greater than one.");
}
return v[2];
}
function last(v) {
if (v[0] !== "Cons") {
throw new Error("Error: 'last' only accepts lists of length greater than one.");
}
var out = v[1];
while (v[0] === "Cons") {
out = v[1];
v = v[2];
}
return out;
}
function map(f) {
return function(xs) {
if (xs[0] === "Nil") { return xs; }
if (xs[0] !== "Cons") { throwError('map'); }
var root = ["Cons", f(xs[1]), ["Nil"]];
var curr = root;
xs = xs[2];
while (xs[0]==="Cons") {
curr[2] = ["Cons", f(xs[1]), ["Nil"]];
xs = xs[2];
curr = curr[2];
}
return root;
}
}
function foldl(f) {
return function(b) {
return function(xs) {
var acc = b;
if (xs[0] === "Nil") { return acc; }
if (xs[0] !== "Cons") { throwError('foldl'); }
while (xs[0] === "Cons") {
acc = f(xs[1])(acc);
xs = xs[2];
}
return acc;
}
}
}
function foldr(f) {
return function(b) {
return function(xs) {
var acc = b;
if (xs[0] === "Nil") { return acc; }
if (xs[0] !== "Cons") { throwError('foldr'); }
var arr = [];
while (xs[0] === "Cons") {
arr.push(xs[1]);
xs = xs[2];
}
for (var i = arr.length; i--; ) {
acc = f(arr[i])(acc);
}
return acc;
}
}
}
function foldl1(f_49) {
return function(_temp_50) {
return function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var x_51 = v[1];
var xs_52 = v[2];
return foldl(f_49)(x_51)(xs_52)
}
}(_temp_50)
}
}
function foldr1(f) {
return function(xs) {
if (xs[0] === "Nil") { throw new Error("'foldr1' requires an non-empty list.") }
if (xs[0] !== "Cons") { throwError('foldr1'); }
var arr = [];
while (xs[0] === "Cons") {
arr.push(xs[1]);
xs = xs[2];
}
var acc = arr.pop();
for (var i = arr.length; i--; ) {
acc = f(arr[i])(acc);
}
return acc;
}
}
function scanl(f) {
return function(b) {
return function(xs) {
if (xs[0] === "Nil") { return ["Cons",b,["Nil"]]; }
if (xs[0] !== "Cons") { throwError('scanl'); }
var arr = [b];
while (xs[0] === "Cons") {
b = f(xs[1])(b);
arr.push(b);
xs = xs[2];
}
var out = ["Nil"];
for (var i = arr.length; i--; ) {
out = ["Cons", arr[i], out];
}
return out;
}
}
}
function scanl1(f) {
return function(xs) {
if (xs[0] !== "Cons") {
throw new Error("Error: 'scanl1' requires a list of at least length 1.");
}
return scanl(f)(xs[1])(xs[2]);
}
}
function filter(pred) {
return function(xs) {
if (xs[0] === "Nil") { return xs; }
if (xs[0] !== "Cons") { throwError('filter'); }
var arr = [];
while (xs[0] === "Cons") {
if (pred(xs[1])) { arr.push(xs[1]); }
xs = xs[2];
}
var out = ["Nil"];
for (var i = arr.length; i--; ) {
out = ["Cons", arr[i], out];
}
return out;
}
}
function concatMap(f_76) {
return function(x) {
return concat(map(f_76)(x))
}
}
function all(pred) {
return foldl(function(x) { return function(acc) {
return acc && pred(x);
};})(true);
}
function any(pred) {
return foldl(function(x) { return function(acc) {
return acc || pred(x);
};})(false);
}
function partition(pred_93) {
return function(lst_94) {
return function() {
var v = lst_94;
var c = [function(v) {
if("Nil" !== v[0]) {
return undefined
}else {
return["Tuple2", ["Nil"], ["Nil"]]
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var x_95 = v[1];
var xs_96 = v[2];
return function(v) {
if("Tuple2" !== v[0]) {
return undefined
}else {
var as_97 = v[1];
var bs_98 = v[2];
return pred_93(x_95) ? ["Tuple2", ["Cons", x_95, as_97], bs_98] : ["Tuple2", as_97, ["Cons", x_95, bs_98]]
}
}(partition(pred_93)(xs_96))
}
}];
for(var i = c.length;i--;) {
var r = c[i](v);
if(r !== undefined) {
return r
}
}
}()
}
}
function zipWith(f) {
return function(listA) {
return function(listB) {
if (listA[0] === "Nil" || listB[0] === "Nil") { return ["Nil"]; }
if (listA[0] !== "Cons" || listB[0] !== "Cons") { throwError('zipWith'); }
var arr = [];
while (listA[0] === "Cons" && listB[0] === "Cons") {
arr.push(f(listA[1])(listB[1]));
listA = listA[2];
listB = listB[2];
}
var out = ["Nil"];
for (var i = arr.length; i--; ) {
out = ["Cons", arr[i], out];
}
return out;
}
}
}
function zip(listA) {
return function(listB) {
if (listA[0] === "Nil" || listB[0] === "Nil") { return ["Nil"]; }
if (listA[0] !== "Cons" || listB[0] !== "Cons") { throwError('zip'); }
var arr = [];
while (listA[0] === "Cons" && listB[0] === "Cons") {
arr.push(["Tuple2", listA[1], listB[1]]);
listA = listA[2];
listB = listB[2];
}
var out = ["Nil"];
for (var i = arr.length; i--; ) {
out = ["Cons", arr[i], out];
}
return out;
}
}
function unzip(pairs_112) {
return function() {
var v = pairs_112;
var c = [function(v) {
if("Nil" !== v[0]) {
return undefined
}else {
return["Tuple2", ["Nil"], ["Nil"]]
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var p_113 = v[1];
var ps_114 = v[2];
return function(v) {
if("Tuple2" !== v[0]) {
return undefined
}else {
if("Tuple2" !== v[1][0]) {
return undefined
}else {
var x_115 = v[1][1];
var y_116 = v[1][2];
if("Tuple2" !== v[2][0]) {
return undefined
}else {
var xs_117 = v[2][1];
var ys_118 = v[2][2];
return["Tuple2", ["Cons", x_115, xs_117], ["Cons", y_116, ys_118]]
}
}
}
}(["Tuple2", p_113, unzip(ps_114)])
}
}];
for(var i = c.length;i--;) {
var r = c[i](v);
if(r !== undefined) {
return r
}
}
}()
}
function intersperse(sep_119) {
return function(xs_120) {
return function() {
var v = xs_120;
var c = [function(v) {
if("Nil" !== v[0]) {
return undefined
}else {
return["Nil"]
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var a_124 = v[1];
if("Nil" !== v[2][0]) {
return undefined
}else {
return["Cons", a_124, ["Nil"]]
}
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var a_121 = v[1];
if("Cons" !== v[2][0]) {
return undefined
}else {
var b_122 = v[2][1];
var cs_123 = v[2][2];
return["Cons", a_121, ["Cons", sep_119, intersperse(sep_119)(["Cons", b_122, cs_123])]]
}
}
}];
for(var i = c.length;i--;) {
var r = c[i](v);
if(r !== undefined) {
return r
}
}
}()
}
}
function intercalate(sep_125) {
return function(xs_126) {
return function() {
var v = xs_126;
var c = [function(v) {
if("Nil" !== v[0]) {
return undefined
}else {
return["Nil"]
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var a_130 = v[1];
if("Nil" !== v[2][0]) {
return undefined
}else {
return a_130
}
}
}, function(v) {
if("Cons" !== v[0]) {
return undefined
}else {
var a_127 = v[1];
if("Cons" !== v[2][0]) {
return undefined
}else {
var b_128 = v[2][1];
var cs_129 = v[2][2];
return Value.append(a_127, Value.append(sep_125, intercalate(sep_125)(["Cons", b_128, cs_129])))
}
}
}];
for(var i = c.length;i--;) {
var r = c[i](v);
if(r !== undefined) {
return r
}
}
}()
}
}
function sort(xs) {
if (xs[0] === "Nil") { return xs; }
if (xs[0] !== "Cons") { throwError('sort'); }
var arr = [];
while (xs[0] === "Cons") {
arr.push(xs[1]);
xs = xs[2];
}
arr.sort(function(a,b) { return a - b});
var out = ["Nil"];
for (var i = arr.length; i--; ) {
out = [ "Cons", arr[i], out ];
}
return out;
}
function take(n) { return function(xs) {
if (n <= 0) { return ["Nil"]; }
if (xs[0] === "Nil") { return xs; }
if (xs[0] !== "Cons") { throwError('take'); }
var out = [ "Cons", xs[1], ["Nil"] ];
var temp = out;
xs = xs[2];
--n;
while (xs[0] === "Cons" && n > 0) {
temp[2] = [ "Cons", xs[1], ["Nil"] ];
temp = temp[2];
xs = xs[2];
--n;
}
return out;
};
}
function drop(n) { return function(xs) {
if (xs[0] === "Nil") { return xs; }
if (xs[0] !== "Cons") { throwError('drop'); }
while (xs[0] === "Cons" && n > 0) {
xs = xs[2];
--n;
}
return xs;
};
}
return {head:head,
tail:tail,
last:last,
map:map,
foldl:foldl,
foldr:foldr,
foldl1:foldl1,
foldr1:foldr1,
scanl:scanl,
scanl1:scanl1,
filter:filter,
length:length,
reverse:reverse,
concat:concat,
concatMap:concatMap,
and:and,
or:or,
all:all,
any:any,
sum:sum,
product:product,
maximum:maximum,
minimum:minimum,
partition:partition,
zipWith:zipWith,
zip:zip,
unzip:unzip,
intersperse:intersperse,
intercalate:intercalate,
sort:sort,
take:take,
drop:drop};
}();

View file

@ -1,62 +0,0 @@
/*! Maybe
!*/
/*[Definition]*/
/** data Maybe a = Just a | Nothing
The Maybe datatype. Useful when a computation may or may not
result in a value (e.g. logarithm is defined only for positive numbers).
**/
/*[Basic Utilities]*/
/** maybe : b -> (a -> b) -> Maybe a -> b
Apply a function to the contents of a `Maybe`.
Return default when given `Nothing`.
**/
/** isJust : Maybe a -> Bool
Check if constructed with `Just`.
**/
/** isNothing : Maybe a -> Bool
Check if constructed with `Nothing`.
**/
/*[Maybe with Lists]*/
/** cons : Maybe a -> [a] -> [a]
If `Just`, adds the value to the front of the list.
If `Nothing`, list is unchanged.
**/
/** justs : [Maybe a] -> [a]
Filters out Nothings and extracts the remaining values.
**/
Elm.Maybe = function() {
function consMaybe(x) { return function(xs) {
if (x[0] === "Just") return ["Cons", x[1], xs];
return xs;
};
}
function mapCons(f) { return function(y) { return function(xs) {
var x = f(y);
if (x[0] === "Just") return ["Cons", x[1], xs];
return xs;
};
};
}
function maybe(b) { return function(f) { return function(m) {
if (m[0] === "Just") return f(m[1]);
return b;
};
};
}
return {Just : function(x) { return ["Just",x]; },
Nothing : ["Nothing"],
justs : Elm.List.foldr(consMaybe)(["Nil"]),
isJust : function(m) { return m[0] === "Just"; },
isNothing : function(m) { return m[0] === "Nothing"; },
cons : consMaybe,
maybe : maybe
};
}();

View file

@ -1,243 +0,0 @@
function elmRecordCopy(r) {
var o = {};
for (var i in r) { o[i] = r[i]; }
return o;
}
function elmRecordRemove(x,r) {
var o = elmRecordCopy(r);
if (x in o._) {
o[x] = o._[x][0];
o._[x] = o._[x].slice(1);
if (o._[x].length === 0) { delete o._[x]; }
} else {
delete o[x];
}
return o;
}
function elmRecordReplace(kvs,r) {
var o = elmRecordCopy(r);
for (var i = kvs.length; i--; ) {
kvsi = kvs[i];
o[kvsi[0]] = kvsi[1];
}
return o;
}
function elmRecordInsert(x,v,r) {
var o = elmRecordCopy(r);
if (x in o) o._[x] = [o[x]].concat(x in o._ ? o._[x].slice(0) : []);
o[x] = v;
return o;
}
Value.addListener(document, 'elm_log', function(e) { console.log(e.value); });
Value.addListener(document, 'elm_title', function(e) {document.title = e.value;});
Value.addListener(document, 'elm_redirect', function(e) {
if (e.value.length > 0) { window.location = e.value; }
});
Value.addListener(document, 'elm_viewport', function(e) {
var node = document.getElementById('elm_viewport');
if (!node) {
node = document.createElement('meta');
node.id = 'elm_viewport';
node.name = 'viewport';
document.head.appendChild(node);
}
node.content = e.value;
Dispatcher.notify(Elm.Window.dimensions.id,
Value.Tuple(window.innerWidth, window.innerHeight));
});
Elm.Prelude = function() {
var mod = function(x) { return function(y) {
var r = x % y;
var m = x==0 ? 0 : (y>0 ? (x>=0 ? r : r+y) : -mod(-x)(-y));
return m == y ? 0 : m;
}; };
var min = function(x) { return function(y) { return Math.min(x,y); }; };
var max = function(x) { return function(y) { return Math.max(x,y); }; };
var flip=function(f){return function(x){return function(y){return f(y)(x);};};};
var clamp = function(lo) { return function(hi) {
return function(x) { return Math.min(hi, Math.max(lo, x)); };
};
};
var curry = function(f) { return function(x) { return function(y) {
return f(["Tuple2",x,y]); }; };
};
var uncurry = function(f) { return function(p) {
if (p[0] !== "Tuple2") {
throw new Error("Function was uncurry'd but was not given a pair.");
}
return f(p[1])(p[2]); };
};
var logBase=function(b){return function(x){return Math.log(x)/Math.log(b);};};
function readInt(str) {
var s = Elm.JavaScript.castStringToJSString(str);
var len = s.length;
if (len === 0) { return ["Nothing"]; }
var start = 0;
if (s[0] == '-') {
if (len === 1) { return ["Nothing"]; }
start = 1;
}
for (var i = start; i < len; ++i) {
if (!Elm.Char.isDigit(s[i])) { return ["Nothing"]; }
}
return ["Just", parseInt(s)];
}
function readFloat(str) {
var s = Elm.JavaScript.castStringToJSString(str);
var len = s.length;
if (len === 0) { return ["Nothing"]; }
var start = 0;
if (s[0] == '-') {
if (len === 1) { return ["Nothing"]; }
start = 1;
}
var dotCount = 0;
for (var i = start; i < len; ++i) {
if (Elm.Char.isDigit(s[i])) { continue; }
if (s[i] === '.') {
dotCount += 1;
if (dotCount <= 1) { continue; }
}
return ["Nothing"];
}
return ["Just", parseFloat(s)];
}
function compare(x) { return function (y) {
if (x instanceof Array && y instanceof Array) {
var len = x.length;
if (len == y.length) {
for (var i = 1; i < len; ++i) {
var cmp = compare(x[i])(y[i]);
if (cmp[0] === 'EQ') continue;
return cmp;
}
return ['EQ'];
}
return [ y.length == 1 ? 'GT' : 'LT' ];
}
return [ x === y ? 'EQ' : (x < y ? 'LT' : 'GT') ];
};
}
return {eq : Value.eq,
id : function(x) { return x; },
not : function(b) { return !b; },
xor : function(x) { return function(y) { return x != y; }; },
fst : function(p) { return p[1]; },
snd : function(p) { return p[2]; },
rem : function(x) { return function(y) { return x % y; }; },
div : function(x) { return function(y) { return ~~(x / y); }; },
otherwise : true,
compare : compare,
toFloat : function(x) { return x; },
round : function(n) { return Math.round(n); },
floor : function(n) { return Math.floor(n); },
ceiling : function(n) { return Math.ceil(n); },
truncate : function(n) { return ~~n; },
readInt : readInt,
readFloat : readFloat,
sqrt : Math.sqrt,
abs : Math.abs,
pi : Math.PI,
e : Math.E,
sin : Math.sin,
cos : Math.cos,
tan : Math.tan,
asin : Math.asin,
acos : Math.acos,
atan : Math.atan,
atan2 : function(y) { return function(x) { return Math.atan2(y,x); }; },
mod : mod,
min : min,
max : max,
flip : flip,
clamp : clamp,
curry : curry,
uncurry : uncurry,
logBase : logBase,
Just : Elm.Maybe.Just,
Nothing : Elm.Maybe.Nothing,
maybe : Elm.Maybe.maybe,
map : Elm.List.map,
zip : Elm.List.zip,
zipWith : Elm.List.zipWith,
filter : Elm.List.filter,
head : Elm.List.head,
tail : Elm.List.tail,
last : Elm.List.last,
length : Elm.List.length,
reverse : Elm.List.reverse,
foldr : Elm.List.foldr,
foldr1 : Elm.List.foldr1,
foldl : Elm.List.foldl,
foldl1 : Elm.List.foldl1,
and : Elm.List.and,
or : Elm.List.or,
all : Elm.List.all,
any : Elm.List.any,
sum : Elm.List.sum,
product : Elm.List.product,
concat : Elm.List.concat,
concatMap : Elm.List.concatMap,
maximum : Elm.List.maximum,
minimum : Elm.List.minimum,
scanl : Elm.List.scanl,
scanl1 : Elm.List.scanl1,
take : Elm.List.take,
drop : Elm.List.drop,
zip : Elm.List.zip,
unzip : Elm.List.unzip,
lift : Elm.Signal.lift,
lift2 : Elm.Signal.lift2,
lift3 : Elm.Signal.lift3,
lift4 : Elm.Signal.lift4,
lift5 : Elm.Signal.lift5,
lift6 : Elm.Signal.lift6,
lift7 : Elm.Signal.lift7,
lift8 : Elm.Signal.lift8,
foldp : Elm.Signal.foldp,
foldp1 : Elm.Signal.foldp1,
foldp$ : Elm.Signal.foldp$,
constant : Elm.Signal.constant,
merge : Elm.Signal.merge,
merges : Elm.Signal.merges,
mergeEither : Elm.Signal.mergeEither,
count : Elm.Signal.count,
countIf : Elm.Signal.countIf,
average : Elm.Signal.average,
keepIf : Elm.Signal.keepIf,
dropIf : Elm.Signal.dropIf,
keepWhen : Elm.Signal.keepWhen,
dropWhen : Elm.Signal.dropWhen,
dropRepeats : Elm.Signal.dropRepeats,
sampleOn : Elm.Signal.sampleOn,
timestamp : Elm.Signal.timestamp,
timeOf : Elm.Signal.timeOf
};
}();
(function() {
var include = function(library) {
for (var i in library) {
Elm.Prelude[i] = library[i];
}
};
include (Elm.Color);
include (Elm.Text);
include (Elm.Graphics);
include (Elm.Time);
show = Value.show;
}());

View file

@ -1,31 +0,0 @@
Elm.Set=function(){
var empty_0=Elm.Dict.empty;
var remove_3=Elm.Dict.remove;
var member_4=Elm.Dict.member;
var union_5=Elm.Dict.union;
var intersect_6=Elm.Dict.intersect;
var diff_7=Elm.Dict.diff;
var toList_8=Elm.Dict.keys;
var fromList_9=Elm.List.foldl(function(k_15){
return function(t_16){
return Elm.Dict.insert(k_15)(["Tuple0"])(t_16);};})(empty_0);
function singleton_1(k_13){
return Elm.Dict.singleton(k_13)(["Tuple0"]);};
function insert_2(k_14){
return Elm.Dict.insert(k_14)(["Tuple0"]);};
function foldl_10(f_17){
return Elm.Dict.foldl(function(k_18){
return function(v_19){
return function(b_20){
return f_17(k_18)(b_20);};};});};
function foldr_11(f_21){
return Elm.Dict.foldr(function(k_22){
return function(v_23){
return function(b_24){
return f_21(k_22)(b_24);};};});};
function map_12(f_25){
return function(t_26){
return function(x){
return fromList_9(Elm.List.map(f_25)(x));}(toList_8(t_26));};};
return {empty:empty_0,singleton:singleton_1,insert:insert_2,remove:remove_3,member:member_4,union:union_5,intersect:intersect_6,diff:diff_7,toList:toList_8,fromList:fromList_9,foldl:foldl_10,foldr:foldr_11,map:map_12};}();

View file

@ -1,111 +0,0 @@
/*! HTTP
A library for asynchronous HTTP requests (AJAX). See the
[WebSocket](http://elm-lang.org/docs/WebSocket.elm) library if
you have very strict latency requirements.
!*/
Elm.HTTP = function() {
var JS = Elm.JavaScript;
var toElmString = Elm.JavaScript.castJSStringToString;
/*[Creating Requests]*/
/** get : String -> Request String
Create a GET request to the given url.
**/
function get(url) { return request("GET")(url)(null)(["Nil"]); }
/** post : String -> String -> Request String
Create a POST request to the given url, carrying the given data.
**/
function post(url) { return function(data) {
return request("POST")(url)(data)(["Nil"]); }; }
/** request : String -> String -> String -> [(String,String)] -> Request String
Create a customized request. Arguments are request type (get, post, put,
delete, etc.), target url, data, and a list of additional headers.
**/
function request(verb) { return function(url) { return function(data) {
return function(headers) {
return {0 : "Request",
length : 1,
verb : JS.castStringToJSString(verb),
url : JS.castStringToJSString(url),
data : data === null ? null : JS.castStringToJSString(data),
headers : headers }; }; }; };
}
function registerReq(queue,responses) { return function(req) {
if (req.url !== "") { sendReq(queue,responses,req); }
};
}
function updateQueue(queue,responses) {
if (queue.length > 0) {
Dispatcher.notify(responses.id, queue[0].value);
if (queue[0].value[0] !== "Waiting") {
queue.shift();
setTimeout(function() { updateQueue(queue,responses); }, 0);
}
}
}
function sendReq(queue,responses,req) {
var response = { value: ["Waiting"] };
queue.push(response);
var request = null;
if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
request.onreadystatechange = function(e) {
if (request.readyState === 4) {
response.value = (request.status === 200
? ["Success", toElmString(request.responseText)]
: ["Failure", request.status,
toElmString(request.statusText)]);
setTimeout(function() { updateQueue(queue,responses); }, 0);
}
};
request.open(req.verb, req.url, true);
Elm.List.map(function(pair) {
request.setRequestHeader(
JS.castStringToJSString(pair[1]),
JS.castStringToJSString(pair[2]));
})(req.headers);
request.send(req.data);
return null;
}
/*[Responses]*/
/** data Response a = Waiting | Success a | Failure Int String
The datatype for responses. Success contains only the returned message.
Failures contain both an error code and an error message.
**/
/*[Sending Requests]*/
/** send : Signal (Request a) -> Signal (Response String)
Performs an HTTP request with the given requests. Produces a signal
that carries the responses.
**/
function send(requests) {
var responses = Elm.Signal.constant(["Waiting"]);
var sender = Elm.Signal.lift(registerReq([],responses))(requests);
function f(x) { return function(y) { return x; } }
return Elm.Signal.lift2(f)(responses)(sender);
}
/** sendGet : Signal String -> Signal (Response String)
Performs an HTTP GET request with the given urls. Produces a signal
that carries the responses.
**/
return {get : get,
post : post,
request : request,
send : send,
sendGet : function(urls){return send(Elm.Signal.lift(get)(urls));}
};
}();

View file

@ -1,84 +0,0 @@
Elm.Input = function() {
var JS = Elm.JavaScript;
var toElmString = Elm.JavaScript.castJSStringToString;
var newTextInput = function(elem, ghostText) {
elem.placeholder = JS.castStringToJSString(ghostText);
var str = Elm.Signal.constant(["Nil"]);
Value.addListener(elem, 'keyup', function(e) {
Dispatcher.notify(str.id, toElmString(elem.value));
elem.focus();
});
elem.style.padding = "1px";
return Value.Tuple(Value.wrap(elem), str);
};
var newElement = function(name) {
var e = document.createElement(name);
e.style.padding = "0";
e.style.margin = "0";
return e;
};
var textArea = function(cols) { return function(rows) {
var textarea = newElement('textarea');
textarea.rows = rows;
textarea.cols = cols;
return newTextInput(textarea, "");
};
};
var textField = function(ghostText) {
var field = newElement('input');
field.type = 'text';
return newTextInput(field, ghostText);
};
var password = function(ghostText) {
var field = newElement('input');
field.type = 'password';
return newTextInput(field, ghostText);
};
var checkbox = function(checked) {
var box = newElement('input');
box.type = 'checkbox';
box.checked = checked;
var status = Elm.Signal.constant(checked);
Value.addListener(box, 'change', function(e) {
Dispatcher.notify(status.id, box.checked);
});
return Value.Tuple(Value.wrap(box), status);
};
var dropDown = function(options) {
var slct = newElement('select');
var opts = [];
while (options[0] === "Cons") {
var opt = newElement('option');
var str = Value.toText(options[1][1]);
opt.value = str;
opt.innerHTML = str;
slct.appendChild(opt);
opts.push(options[1][2]);
options = options[2];
}
var status = Elm.Signal.constant(opts[0]);
Value.addListener(slct, 'change', function(e) {
Dispatcher.notify(status.id, opts[slct.selectedIndex]);
});
return Value.Tuple(Value.wrap(slct), status);
};
var stringDropDown = function(opts) {
return dropDown(Elm.List.map (function(x) {return Value.Tuple(x,x);}) (opts));
};
var button = function(name) {
var b = newElement('button');
b.type = "button";
b.innerText = JS.castStringToJSString(name);
var press = Elm.Signal.constant(false);
Value.addListener(b, 'click', function(e) {
Dispatcher.notify(press.id, true);
Dispatcher.notify(press.id, false);
});
return Value.Tuple(Value.wrap(b),press);
};
return {textArea:textArea, textField:textField,
password:password, checkbox:checkbox,
dropDown:dropDown, stringDropDown:stringDropDown,
button:button};
}();

View file

@ -1,123 +0,0 @@
Elm.Keyboard = { Raw : function() {
var keysDown = Elm.Signal.constant(["Nil"]);
var charPressed = Elm.Signal.constant(["Nothing"]);
function remove(x,xs) {
if (xs[0] === "Nil") return xs;
if (xs[1] === x) return xs[2];
return ["Cons", xs[1], remove(x,xs[2])];
}
function has(x,xs) {
while (xs[0] !== "Nil") {
if (xs[1] === x) return true;
xs = xs[2];
}
return false;
}
Value.addListener(document, 'keydown', function(e) {
if (has(e.keyCode, keysDown.value)) return;
var hasListener = Dispatcher.notify(keysDown.id, ["Cons", e.keyCode, keysDown.value]);
if (!hasListener)
this.removeEventListener('keydown',arguments.callee,false);
});
Value.addListener(document, 'keyup', function(e) {
var codes = remove(e.keyCode, keysDown.value);
var hasListener = Dispatcher.notify(keysDown.id, codes);
if (!hasListener)
this.removeEventListener('keyup',arguments.callee,false);
});
Value.addListener(window, 'blur', function(e) {
var hasListener = Dispatcher.notify(keysDown.id, ["Nil"]);
if (!hasListener)
this.removeEventListener('blur',arguments.callee,false);
});
Value.addListener(document, 'keypress', function(e) {
var hasListener = Dispatcher.notify(charPressed.id, ["Just",e.charCode || e.keyCode]);
Dispatcher.notify(charPressed.id, ["Nothing"]);
if (!hasListener)
this.removeEventListener('keypress',arguments.callee,false);
});
return {keysDown:keysDown,
charPressed:charPressed};
}()
};
/*! Keyboard
These are nicely curated inputs from the keyboard. See the
[Keyboard.Raw library](/docs/Signal/KeyboardRaw.elm) for a
lower-level interface that will let you define more complicated behavior.
!*/
(function() {
function keySignal(f) {
var signal = Elm.Signal.lift(f)(Elm.Keyboard.Raw.keysDown);
Elm.Keyboard.Raw.keysDown.defaultNumberOfKids += 1;
signal.defaultNumberOfKids = 0;
return signal;
}
function dir(left,right,up,down) {
function f(ks) {
var x = 0, y = 0;
while (ks[0] == "Cons") {
switch (ks[1]) {
case left : --x; break;
case right: ++x; break;
case up : ++y; break;
case down : --y; break;
}
ks = ks[2];
}
return { _:[true], x:[x], y:[y] };
}
return keySignal(f);
}
function is(key) {
function f(ks) {
while (ks[0] == "Cons") {
if (key == ks[1]) return true;
ks = ks[2];
}
return false;
}
return keySignal(f);
}
/*[Directions]*/
/** arrows : Signal { x:Int, y:Int }
A signal of records indicating which arrow keys are pressed.
`{ x = 0, y = 0 }` when pressing no arrows.
`{ x =-1, y = 0 }` when pressing the left arrow.
`{ x = 1, y = 1 }` when pressing the up and right arrows.
`{ x = 0, y =-1 }` when pressing the down, left, and right arrows.
**/
Elm.Keyboard.arrows = dir(37,39,38,40);
/** wasd : Signal { x:Int, y:Int }
Just like the arrows signal, but this uses keys w, a, s, and d,
which are common controls for many computer games.
**/
Elm.Keyboard.wasd = dir(65,68,87,83);
/*[Modifiers]*/
/** shift : Signal Bool
Whether the shift key is pressed.
**/
Elm.Keyboard.shift = is(16);
/** ctrl : Signal Bool
Whether the control key is pressed.
**/
Elm.Keyboard.ctrl = is(17);
/** space : Signal Bool
Whether the space key is pressed.
**/
Elm.Keyboard.space = is(32);
}());

View file

@ -1,106 +0,0 @@
/*! Mouse
!*/
Elm.Mouse = function() {
/*[Position]*/
/** position : Signal (Int,Int)
The current mouse position.
**/
var position = Elm.Signal.constant(Value.Tuple(0,0));
position.defaultNumberOfKids = 2;
/** x : Signal Int
The current x-coordinate of the mouse.
**/
var x = Elm.Signal.lift(function(p){return p[1];})(position);
x.defaultNumberOfKids = 0;
/** y : Signal Int
The current y-coordinate of the mouse.
**/
var y = Elm.Signal.lift(function(p){return p[2];})(position);
y.defaultNumberOfKids = 0;
/*[Button Status]*/
/** isDown : Signal Bool
The current state of the left mouse-button.
True when the button is down, and false otherwise.
**/
var isDown = Elm.Signal.constant(false);
/** isClicked : Signal Bool
True immediately after the left mouse-button has been clicked,
and false otherwise.
**/
var isClicked = Elm.Signal.constant(false);
/** clicks : Signal ()
Always equal to unit. Event triggers on every mouse click.
**/
var clicks = Elm.Signal.constant(Value.Tuple());
function getXY(e) {
var posx = 0;
var posy = 0;
if (!e) e = window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
} else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
return Value.Tuple(posx, posy);
}
Value.addListener(document, 'click', function(e) {
var hasListener1 = Dispatcher.notify(isClicked.id, true);
var hasListener2 = Dispatcher.notify(clicks.id, Value.Tuple());
Dispatcher.notify(isClicked.id, false);
if (!hasListener1 && !hasListener2)
this.removeEventListener('click',arguments.callee,false);
});
Value.addListener(document, 'mousedown', function(e) {
var hasListener = Dispatcher.notify(isDown.id, true);
if (!hasListener)
this.removeEventListener('mousedown',arguments.callee,false);
});
Value.addListener(document, 'mouseup', function(e) {
var hasListener = Dispatcher.notify(isDown.id, false);
if (!hasListener)
this.removeEventListener('mouseup',arguments.callee,false);
});
Value.addListener(document, 'mousemove', function(e) {
var hasListener = Dispatcher.notify(position.id, getXY(e));
if (!hasListener)
this.removeEventListener('mousemove',arguments.callee,false);
});
/** isClickedOn : Element -> (Element, Signal Bool)
Determine whether an element has been clicked. The resulting pair
is a signal of booleans that is true when its paired element has
been clicked. The signal is True immediately after the left
mouse-button has been clicked, and false otherwise.
**/
var clickedOn = function(elem) {
var node = Render.render(elem);
var click = Elm.Signal.constant(false);
Value.addListener(node, 'click', function(e) {
Dispatcher.notify(click.id, true);
Dispatcher.notify(click.id, false);
});
return Value.Tuple(Value.wrap(node), click);
};
return {position: position,
x:x,
y:y,
isClicked: isClicked,
isDown: isDown,
clicks: clicks,
isClickedOn: clickedOn
};
}();

View file

@ -1,29 +0,0 @@
/*! Random
!*/
Elm.Random = function() {
/*[In a Range]*/
/** inRange : Int -> Int -> Signal Int
Given a range from low to high, this produces a random number
between 'low' and 'high' inclusive. The value in the signal does
not change after the page has loaded.
**/
var inRange = function(min) { return function(max) {
return Elm.Signal.constant(Math.floor(Math.random() * (max-min+1)) + min);
};
};
/** randomize : Int -> Int -> Signal a -> Signal Int
Given a range from low to high and a signal of values, this produces
a new signal that changes whenever the input signal changes. The new
values are random number between 'low' and 'high' inclusive.
**/
var randomize = function(min) { return function(max) { return function(signal) {
function f(x) { return Math.floor(Math.random() * (max-min+1)) + min; }
return Elm.Signal.lift(f)(signal);
};
};
};
return { inRange:inRange, randomize:randomize };
}();

View file

@ -1,112 +0,0 @@
/*! Time
Library for working with time. Type `Time` represents some number of
milliseconds.
!*/
Elm.Time = function() {
/*[Times]*/
/** hour, minute, second, ms : Time
Units of time, making it easier to specify things like a
half-second `(second / 2)`.
**/
function timeNow() { return (new window.Date).getTime(); }
/*[Tickers]*/
/** fps : Number -> Signal Time
Takes desired number of frames per second (fps). The resulting signal
gives a sequence of time deltas as quickly as possible until it reaches
the desired FPS. A time delta is the time between the last frame and the
current frame.
**/
/** fpsWhen : Number -> Signal Bool -> Signal Time
Same as the fps function, but you can turn it on and off. Allows you
to do brief animations based on user input without major ineffeciencies.
The first time delta after a pause is always zero, no matter how long
the pause was. This way summing the deltas will actually give the amount
of time that the output signal has been running.
**/
function fpsWhen(desiredFPS) { return function (isOn) {
var msPerFrame = 1000 / desiredFPS;
var prev = timeNow(), curr = prev, diff = 0, wasOn = true;
var ticker = Elm.Signal.constant(diff);
function tick(zero) { return function() {
curr = timeNow();
diff = zero ? 0 : curr - prev;
prev = curr;
Dispatcher.notify(ticker.id, diff);
};
}
var timeoutID = 0;
function f(isOn) { return function(t) {
if (isOn) {
timeoutID = setTimeout(tick(!wasOn && isOn), msPerFrame);
} else if (wasOn) {
clearTimeout(timeoutID);
}
wasOn = isOn;
return t;
};
}
return Elm.Signal.lift2(f)(isOn)(ticker);
};
}
/** every : Time -> Signal Time
Takes a time interval t. The resulting signal is the current time,
updated every t.
**/
function everyWhen(isOn) { return function(t) {
var clock = Elm.Signal.constant(timeNow());
function tellTime() { Dispatcher.notify(clock.id, timeNow()); }
setInterval(tellTime, t);
return clock;
};
}
function since(t) { return function(s) {
function cmp(a) { return function(b) { return !Value.eq(a,b); }; }
var dcount = Elm.Signal.count(Elm.Signal.delay(t)(s));
return Elm.Signal.lift2(cmp)(Elm.Signal.count(s))(dcount);
};
}
function after(t) {
t *= 1000;
var thread = Elm.Signal.constant(false);
setTimeout(function() { Dispatcher.notify(thread.id, true); }, t);
return thread;
}
function before(t) {
t *= 1000;
var thread = Elm.Signal.constant(true);
setTimeout(function() { Dispatcher.notify(thread.id, false); }, t);
return thread;
}
function read(s) {
var t = window.Date.parse(s);
return isNaN(t) ? ["Nothing"] : ["Just",t];
}
return {fpsWhen : fpsWhen,
fps : function(t) { return fpsWhen(t)(Elm.Signal.constant(true)); },
every : everyWhen(Elm.Signal.constant(true)),
delay : Elm.Signal.delay,
since : since,
after : after,
before : before,
hour : 3600000,
minute : 60000,
second : 1000,
ms : 1,
inHours : function(t) { return t / 3600000; },
inMinutes : function(t) { return t / 60000; },
inSeconds : function(t) { return t / 1000; },
inMss : function(t) { return t; },
toDate : function(t) { return new window.Date(t); },
read : read
};
}();

View file

@ -1,109 +0,0 @@
/*! Touch
This is an early version of the touch library. It will likely grow to
include gestures that would be useful for both games and web-pages.
!*/
Elm.Touch = function() {
function Dict() {
this.keys = [];
this.values = [];
this.insert = function(key,value) {
this.keys.push(key);
this.values.push(value);
};
this.lookup = function(key) {
var i = this.keys.indexOf(key)
return i >= 0 ? this.values[i] : {x:0,y:0,t:0};
};
this.remove = function(key) {
var i = this.keys.indexOf(key);
if (i < 0) return;
var t = this.values[i];
this.keys.splice(i,1);
this.values.splice(i,1);
return t;
};
}
var root = Elm.Signal.constant([]),
tapTime = 500,
hasTap = false,
tap = {_:[true],x:[0],y:[0]},
dict = new Dict();
function touch(t) {
var r = dict.lookup(t.identifier);
return {_ : [true], id: [t.identifier],
x: [t.pageX], y: [t.pageY],
x0: [r.x], y0: [r.y],
t0: [r.t] };
}
function start(e) {
dict.insert(e.identifier,{x:e.pageX,y:e.pageY,t:Date.now()});
}
function end(e) {
var t = dict.remove(e.identifier);
if (Date.now() - t.t < tapTime) {
hasTap = true;
tap = {_:[true], x:[t.x], y:[t.y]};
}
}
function listen(name, f) {
function update(e) {
for (var i = e.changedTouches.length; i--; ) { f(e.changedTouches[i]); }
var ts = new Array(e.touches.length);
for (var i = e.touches.length; i--; ) { ts[i] = touch(e.touches[i]); }
var hasListener = Dispatcher.notify(root.id, ts);
if (!hasListener) return document.removeEventListener(name, update);
e.preventDefault();
}
Value.addListener(document, name, update);
}
listen("touchstart", start);
listen("touchmove", function(_){});
listen("touchend", end);
listen("touchcancel", end);
listen("touchleave", end);
function dependency(f) {
var sig = Elm.Signal.lift(f)(root);
root.defaultNumberOfKids += 1;
sig.defaultNumberOfKids = 0;
return sig;
}
/*[Touches]*/
/** touches : Signal [{ x:Int, y:Int, id:Int, x0:Int, y0:Int, t0:Time }]
A list of touches. Each ongoing touch is represented by a set of
coordinates and an identifier id that allows you to distinguish
between different touches. Each touch also contains the coordinates and
time of the initial contact (x0, y0, and t0) which helps compute more
complicated gestures.
**/
var touches = dependency(function(ts) {
return Elm.JavaScript.castJSArrayToList(ts);
});
/*[Gestures]*/
/** taps : Signal { x:Int, y:Int }
The last position that was tapped. Default value is `{x=0,y=0}`.
Updates whenever the user taps the screen.
**/
var taps = function() {
var sig = dependency(function(_) { return tap; });
sig.defaultNumberOfKids = 1;
function pred(_) { var b = hasTap; hasTap = false; return b; }
var sig2 = Elm.Signal.keepIf(pred)({_:[true],x:[0],y:[0]})(sig);
sig2.defaultNumberOfKids = 0;
return sig2;
}();
return { touches: touches, taps: taps };
}();

View file

@ -1,41 +0,0 @@
/*! WebSocket
A library for low latency HTTP communication. See the HTTP library for standard
requests like GET, POST, etc.
!*/
Elm.WebSocket = function() {
var JS = Elm.JavaScript;
/** open : String -> Signal String -> Signal String
Create a web-socket. The first argument is the URL of the desired
web-socket server. The input signal holds the outgoing messages,
and the resulting signal contains the incoming ones.
**/
function open(url) { return function(outgoing) {
var incoming = Elm.Signal.constant(["Nil"]);
var ws = new window.WebSocket(JS.castStringToJSString(url));
var pending = [];
var ready = false;
ws.onopen = function(e) {
var len = pending.length;
for (var i = 0; i < len; ++i) { ws.send(pending[i]); }
ready = true;
};
ws.onmessage = function(event) {
Dispatcher.notify(incoming.id, JS.castJSStringToString(event.data));
};
function send(msg) {
var s = JS.castStringToJSString(msg);
ready ? ws.send(s) : pending.push(s);
}
function take1(x) { return function(y) { return x; } }
return Elm.Signal.lift2(take1)(incoming)(Elm.Signal.lift(send)(outgoing));
};
}
return {open:open};
}();

View file

@ -1,35 +0,0 @@
/*! Window !*/
Elm.Window = function() {
/*[Dimensions]*/
/** dimensions : Signal (Int,Int)
The current dimensions of the window (i.e. the area viewable to the
user, not including scroll bars).
**/
var dimensions = Elm.Signal.constant(Value.Tuple(window.innerWidth,
window.innerHeight));
dimensions.defaultNumberOfKids = 2;
/** width : Signal Int
The current width of the window.
**/
var width = Elm.Signal.lift(function(p){return p[1];})(dimensions);
width.defaultNumberOfKids = 0;
/** height : Signal Int
The current height of the window.
**/
var height = Elm.Signal.lift(function(p){return p[2];})(dimensions);
height.defaultNumberOfKids = 0;
Value.addListener(window, 'resize', function(e) {
var w = document.getElementById('widthChecker').offsetWidth;
var hasListener = Dispatcher.notify(dimensions.id,
Value.Tuple(w, window.innerHeight));
if (!hasListener)
this.removeEventListener('resize',arguments.callee,false);
});
return {dimensions:dimensions,width:width,height:height};
}();

Some files were not shown because too many files have changed in this diff Show more