{-# LANGUAGE OverloadedStrings, TypeFamilies, DataKinds, ScopedTypeVariables #-} module Main where import Prelude hiding (mod, div) import Data.Monoid import Data.Boolean import Data.Boolean.Numbers import Data.Default import System.FilePath ( () ) import Language.Sunroof import Language.Sunroof.Types import Language.Sunroof.JS.Canvas import Language.Sunroof.JS.Browser hiding ( eval ) import Language.Sunroof.JS.JQuery import Language.Sunroof.JS.Date import Paths_sunroof_examples main :: IO () main = do -- Copy the index HTML and jquery.js to the current directory. dataDir <- getDataDir readFile (dataDir "js/jquery.js") >>= writeFile "jquery.js" readFile (dataDir "examples/clock/index.html") >>= writeFile "clock.html" -- Compile the JavaScript and also write it to the current directory. sunroofCompileJSA def "main" clockJS >>= writeFile "main.js" default(JSNumber, JSString, String) -- type instance BooleanOf () = JSBool clockJS :: JS A (JSFunction () ()) clockJS = function $ \() -> do -- Renders a single line (with number) of the clock face. renderClockFaceLine <- function $ \(c :: JSCanvas, u :: JSNumber, n :: JSNumber) -> do c # save -- Draw one of the indicator lines c # beginPath c # moveTo (0, -u * 1.0) ifB (n `mod` 5 ==* 0) (c # lineTo (0, -u * 0.8)) -- Minute line (c # lineTo (0, -u * 0.9)) -- Hour line ifB (n `mod` 15 ==* 0) (c # lineWidth := 8) -- Quarter line (c # lineWidth := 3) -- Non-Quarter line c # stroke c # closePath -- Draw of the hour numbers ifB (n `mod` 5 ==* 0) (do c # translate (-u * 0.75, 0) c # rotate (-2 * pi / 4) c # fillText (cast $ n `div` 5) (0, 0) ) (return ()) c # restore -- Renders a single clock pointer. renderClockPointer <- function $ \(c :: JSCanvas, u :: JSNumber, angle :: JSNumber, width :: JSNumber, len :: JSNumber) -> do c # save c # lineCap := "round" c # rotate angle c # lineWidth := width c # beginPath c # moveTo (0, u * 0.1) c # lineTo (0, -u * len) c # stroke c # closePath c # restore -- Renders the clocks pointers for hours, minutes and seconds. renderClockPointers <- function $ \(c :: JSCanvas, u :: JSNumber) -> do (h, m, s) <- currentTime c # save c # lineCap := "round" -- Hour pointer renderClockPointer $$ (c, u, (2 * pi / 12) * ((h `mod` 12) + (m `mod` 60) / 60), 15, 0.4) -- Minute pointer renderClockPointer $$ ( c, u, (2 * pi / 60) * ((m `mod` 60) + (s `mod` 60) / 60), 10, 0.7) -- Second pointer c # strokeStyle := "red" renderClockPointer $$ ( c, u, (2 * pi / 60) * (s `mod` 60), 4, 0.9) -- Restore everything c # restore -- Renders the complete face of the clock, without pointers. renderClockFace <- function $ \(c :: JSCanvas, u :: JSNumber) -> do c # save c # rotate (2 * pi / 4) -- 0 degrees is at the top -- Draw all hour lines. lines <- array [1..60::Int] lines # forEach $ \n -> do c # save c # rotate ((2 * pi / 60) * n) renderClockFaceLine $$ (c, u, n) c # restore c # restore -- Undo all the rotation. -- Renders the complete clock. renderClock <- continuation $ \() -> do u <- clockUnit (w,h) <- canvasSize c <- context -- Basic setup c # save c # fillStyle := "black" c # strokeStyle := "black" c # lineCap := "round" c # textAlign := "center" c # font := ((cast $ u * 0.1) <> "px serif") c # textBaseline := "top" c # clearRect (0,0) (w,h) c # translate (w / 2, h / 2) -- Draw all hour lines. renderClockFace $$ (c, u) -- Draw the clock pointers renderClockPointers $$ (c, u) c # restore return () window # setInterval (goto renderClock) 1000 -- and draw one now, rather than wait till later goto renderClock () return () canvas :: JS t JSObject canvas = document # getElementById "canvas" context :: JS t JSCanvas context = canvas >>= getContext "2d" clockUnit :: JS t JSNumber clockUnit = do (w, h) <- canvasSize return $ (maxB w h) / 2 canvasSize :: JS t (JSNumber, JSNumber) canvasSize = do c <- jQuery "#canvas" w <- c # invoke "innerWidth" () h <- c # invoke "innerHeight" () return (w, h) currentTime :: JS t (JSNumber, JSNumber, JSNumber) currentTime = do date <- newDate () h <- date # getHours m <- date # getMinutes s <- date # getSeconds return (h, m, s)