This page has a regular input element and suggestion.
12 | Our "custom browser" knows how to augment this with the
13 | code to validate the prime as it is entered
14 |
15 | enter a prime
16 |
17 |
18 |
--------------------------------------------------------------------------------
/weblog/sync/sync.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import GHCJS.Types
6 | import GHCJS.Foreign
7 |
8 | import Data.Text (Text)
9 | import Control.Applicative
10 | import Text.Read (readMaybe)
11 |
12 | main = do
13 | o <- newObj
14 | setProp ("input"::Text) ("123"::JSString) o
15 | factorial o
16 | r <- getProp ("output"::Text) o
17 | putStrLn (fromJSString r)
18 |
19 | factorial :: JSRef () -> IO ()
20 | factorial ref = do
21 | x <- fromJSString <$> getProp ("input"::Text) ref
22 | let r = case readMaybe x of
23 | Just n | n < 0 || n > 5000 -> "invalid input"
24 | Just n -> toJSString . show . fact $ n
25 | Nothing -> "parse error"
26 | setProp ("output"::Text) r ref
27 | where
28 | fact :: Integer -> Integer
29 | fact n = product [1..n]
30 |
--------------------------------------------------------------------------------
/try-purescript/codemirror/theme/elegant.css:
--------------------------------------------------------------------------------
1 | .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
2 | .cm-s-elegant span.cm-comment {color: #262; font-style: italic; line-height: 1em;}
3 | .cm-s-elegant span.cm-meta {color: #555; font-style: italic; line-height: 1em;}
4 | .cm-s-elegant span.cm-variable {color: black;}
5 | .cm-s-elegant span.cm-variable-2 {color: #b11;}
6 | .cm-s-elegant span.cm-qualifier {color: #555;}
7 | .cm-s-elegant span.cm-keyword {color: #730;}
8 | .cm-s-elegant span.cm-builtin {color: #30a;}
9 | .cm-s-elegant span.cm-link {color: #762;}
10 | .cm-s-elegant span.cm-error {background-color: #fdd;}
11 |
12 | .cm-s-elegant .CodeMirror-activeline-background {background: #e8f2ff !important;}
13 | .cm-s-elegant .CodeMirror-matchingbracket {outline:1px solid grey; color:black !important;}
14 |
--------------------------------------------------------------------------------
/multiple-pages/multiple-pages.cabal:
--------------------------------------------------------------------------------
1 | name: multiple-pages
2 | version: 0.0.1
3 | cabal-version: >=1.2
4 | build-type: Simple
5 | license: BSD3
6 | license-file: ""
7 | synopsis: Dynamic web site build on multiple HTML pages
8 | description: Shows how to add GHCJS support to static HTML files or server generated HTML files.
9 | .
10 | All the files run Main.main, but it simply detects the page and loads and runs the pages "main" function.
11 | data-files: *.html
12 | data-dir: data
13 |
14 | executable multiple-pages
15 | build-depends: text >=0.11.2.3 && <0.12, hamlet >=1.1.0.2 && <1.2,
16 | blaze-html >=0.5.0.0 && <0.7, webkit >=0.12.5 && <0.13,
17 | mtl >=2.1.1 && <2.2, ghcjs-dom >=0.0.1 && <0.1, base -any
18 | main-is: Main.hs
19 | buildable: True
20 | hs-source-dirs: src
21 |
22 |
--------------------------------------------------------------------------------
/ghcjs-hello/ghcjs-hello.cabal:
--------------------------------------------------------------------------------
1 | name: ghcjs-hello
2 | version: 0.0.1
3 | cabal-version: >=1.8
4 | build-type: Simple
5 | license: BSD3
6 |
7 | flag jmacro
8 | Description: Include some JMacro support
9 | Default: False
10 |
11 | executable ghcjs-hello
12 | build-depends: deepseq >=1.3.0.2 && <1.4, lens -any,
13 | containers -any, random -any,
14 | template-haskell -any, base -any, blaze-html -any, filepath -any,
15 | hamlet -any, text -any, blaze-markup -any, shakespeare -any,
16 | ghcjs-dom >=0.1.1.0 && <0.2, mtl -any, sodium -any, webkit-sodium -any,
17 | jsaddle >=0.2.0.0 && <0.3
18 |
19 | if flag(jmacro)
20 | build-depends: jmacro >=0.6.3 && <0.8
21 |
22 | main-is: Main.hs
23 | buildable: True
24 | hs-source-dirs: src
25 | ghc-options: -threaded -with-rtsopts=-N3
26 |
27 |
--------------------------------------------------------------------------------
/weblog/race/race.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE JavaScriptFFI, CPP #-}
2 | module Main where
3 |
4 | import Control.Monad
5 | import Control.Concurrent
6 | import GHCJS.Foreign
7 | import GHCJS.Types
8 |
9 | import Data.Text (pack)
10 |
11 | #ifdef __GHCJS__
12 | foreign import javascript unsafe "document.getElementById($1).style.left = '' + $2 + 'px'"
13 | setPos :: JSString -> Int -> IO ()
14 | #else
15 | setPos = error "setPos: only available in JavaScript"
16 | #endif
17 |
18 | main :: IO ()
19 | main = mapM_ runRacer [1..10]
20 |
21 | runRacer :: Int -> IO ()
22 | runRacer n = void $ forkIO $ do
23 | doRace (toJSString $ "racer" ++ show n)
24 |
25 | doRace :: JSString -> IO ()
26 | doRace str = go (0::Int)
27 | where
28 | go n | n > 800 = go 0
29 | | otherwise = do
30 | setPos str n
31 | threadDelay 1
32 | go (n+1)
33 |
34 | x = pack "abc" -- workaround, sometimes the dependencies for text aren't linked without this, need to investigate
35 |
--------------------------------------------------------------------------------
/weblog/sync/sync.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
--------------------------------------------------------------------------------
/weblog/counter/counter.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 |
3 | module Main where
4 |
5 | import Control.Monad
6 | import Control.Monad.IO.Class
7 | import Data.Default
8 | import Data.Text (Text)
9 | import qualified Data.Text as T
10 |
11 | import JavaScript.JQuery hiding (Event)
12 |
13 | import FRP.Sodium
14 |
15 | main :: IO ()
16 | main = do
17 | body <- select "body"
18 | buttonEvent <- reactiveButton "Click Me!" body
19 | counterDiv <- select ""
20 | appendJQuery counterDiv body
21 | sync $ do
22 | counter <- count buttonEvent
23 | listen (values counter) (\n -> void $
24 | setText (T.pack . show $ n) counterDiv)
25 | return ()
26 |
27 | reactiveButton :: Text -> JQuery -> IO (Event ())
28 | reactiveButton label parent = do
29 | (evt, a) <- sync newEvent
30 | button <- select ""
31 | setText label button
32 | appendJQuery button parent
33 | let handler _ = sync (a ())
34 | on handler "click" def button
35 | return evt
36 |
37 |
--------------------------------------------------------------------------------
/weblog/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # http://weblog.luite.com/wordpress/?p=14
4 |
5 | ( cd hello && ghcjs -o hello hello.hs )
6 | ( cd ffi && ghcjs -o ffi ffi.hs )
7 | ( cd fibonacci && ghcjs -O2 -o fibonacci fibonacci.hs)
8 | ( cd race && ghcjs -o race race.hs && cp race.html race.jsexe/index.html )
9 | ( cd event && ghcjs -o event event.hs && cp event.html event.jsexe/index.html )
10 | ( cd sync && ghcjs -o sync sync.hs && cp sync.html sync.jsexe/index.html )
11 |
12 | # http://weblog.luite.com/wordpress/?p=127
13 | cabal install --ghcjs sodium random vector-space data-default
14 |
15 | ( cd counter && ghcjs -o counter counter.hs && cp counter.html counter.jsexe/index.html )
16 | ( cd calculator && ghcjs -o calculator calculator.hs && cp calculator.html calculator.jsexe/index.html )
17 | ( cd mouse && ghcjs -o mouse mouse.hs && cp mouse.html mouse.jsexe/index.html )
18 | ( cd balls1 && ghcjs -O2 -o balls1 balls1.hs && cp balls1.html balls1.jsexe/index.html && cp ball.png balls1.jsexe )
19 | ( cd balls2 && ghcjs -O2 -o balls2 balls2.hs && cp balls2.html balls2.jsexe/index.html && cp ball.png balls2.jsexe )
20 |
21 |
--------------------------------------------------------------------------------
/try-purescript/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014 Luite Stegeman
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/try-purescript/codemirror/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (C) 2014 by Marijn Haverbeke and others
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/webkit-sodium/src/Game.hs:
--------------------------------------------------------------------------------
1 | module Game where
2 |
3 | import FRP.Sodium
4 |
5 |
6 | type Coord = Double
7 | type Point = (Coord, Coord)
8 | type Vector = (Coord, Coord)
9 | type Rect = (Point, Vector) -- Central point and size from centre to edge
10 | type Sprite = (Rect, String)
11 |
12 | data MouseEvent = MouseDown Point | MouseMove Point | MouseUp Point
13 | deriving Show
14 |
15 | plus :: Point -> Vector -> Point
16 | plus (x0, y0) (x1, y1) = (x0 + x1, y0 + y1)
17 |
18 | minus :: Point -> Point -> Vector
19 | minus (x0, y0) (x1, y1) = (x0 - x1, y0 - y1)
20 |
21 | -- | True if the point is inside the rectangle
22 | inside :: Point -> Rect -> Bool
23 | inside (x, y) ((ox, oy), (wx, wy)) =
24 | x >= ox - wx && x <= ox + wx &&
25 | y >= oy - wy && y <= oy + wy
26 |
27 | -- | This tree structure is used to give a list of behaviours from different
28 | -- parts of the FRP logic, so the engine can efficiently draw only the things
29 | -- that have changed. This could be done just as a plain old list, this is a
30 | -- bit more flexible, and also, it allows for us to later add the ability to
31 | -- switch subtrees by means of Sodium's switch primitive.
32 | data BehaviorTree a = BehaviorTree a :++ BehaviorTree a
33 | | BehaviorNode (Behavior a)
34 | infixr 5 :++
35 |
36 |
--------------------------------------------------------------------------------
/weblog/race/race.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/try-purescript/try-purescript.cabal:
--------------------------------------------------------------------------------
1 | name: try-purescript
2 | version: 0.1.0.0
3 | synopsis: Try PureScript!
4 | description: GHCJS example project: Try PureScript in the browser
5 | license: MIT
6 | license-file: LICENSE
7 | author: Luite Stegeman
8 | maintainer: stegeman@gmail.com
9 | category: Web
10 | build-type: Simple
11 | cabal-version: >=1.10
12 | extra-source-files: codemirror/lib/codemirror.css
13 | codemirror/lib/codemirror.js
14 | codemirror/theme/elegant.css
15 | codemirror/addon/edit/matchbrackets.js
16 | codemirror/mode/haskell/haskell.js
17 | codemirror/LICENSE
18 | data/index.html
19 | data/tryps.css
20 | data/run_button.png
21 | data/busy.gif
22 | prepare.sh
23 | README.markdown
24 |
25 | executable try-purescript
26 | js-sources: jsbits/tryps.js
27 | ghcjs-options: -O
28 | cpp-options: -DGHCJS_BROWSER -DGHCJS_BUSY_YIELD=30
29 | main-is: Main.hs
30 | build-depends: base >=4.7 && <5,
31 | ghcjs-base,
32 | ghcjs-ffiqq,
33 | mtl,
34 | purescript >= 0.6 && < 0.7
35 | default-language: Haskell2010
36 |
--------------------------------------------------------------------------------
/try-purescript/prepare.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # run this script after building the jsexe to copy data files and minify the sources
4 | # assumes that the tools described in the Deployment wiki page are installed.
5 | # https://github.com/ghcjs/ghcjs/wiki/Deployment
6 | #
7 | # this task really ought to be done with Cabal, but it doesn't support this yet
8 |
9 | set -e
10 |
11 | DISTDIR="dist"
12 | EXEDIR="$DISTDIR/build/try-purescript/try-purescript.jsexe"
13 |
14 | cp "data/index.html" "$EXEDIR/index.html"
15 | cp "data/tryps.css" "$EXEDIR/tryps.css"
16 | cp "data/run_button.png" "$EXEDIR/run_button.png"
17 | cp "data/busy.gif" "$EXEDIR/busy.gif"
18 |
19 | cp "codemirror/lib/codemirror.js" "$EXEDIR/codemirror.js"
20 | cat "codemirror/addon/edit/matchbrackets.js" >> "$EXEDIR/codemirror.js"
21 | cat "codemirror/mode/haskell/haskell.js" >> "$EXEDIR/codemirror.js"
22 |
23 | cp "codemirror/lib/codemirror.css" "$EXEDIR/codemirror.css"
24 | cat "codemirror/theme/elegant.css" >> "$EXEDIR/codemirror.css"
25 |
26 | (
27 | cd "$EXEDIR"
28 | # advanced optimizations are currently buggy for this program, enable them again when fixed
29 | echo "minifying all.js"
30 | ccjs "all.js" > "all.min.js"
31 | echo "compressing all.min.js"
32 | zopfli -i1000 "all.min.js"
33 | echo "minifying codemirror.js"
34 | ccjs "codemirror.js" > "codemirror.min.js"
35 | echo "compressing codemirror.min.js"
36 | zopfli -i1000 "codemirror.min.js"
37 | )
38 |
39 |
--------------------------------------------------------------------------------
/try-purescript/README.markdown:
--------------------------------------------------------------------------------
1 | # Try PureScript!
2 |
3 | Try PureScript! is an example that demonstrates the [ghcjs-ffiqq](https://github.com/ghcjs/ghcjs-ffiqq)
4 | library and shows how long-running background computations can be done in asynchronous threads,
5 | keeping the user interface responsive without the added complexity of web workers.
6 |
7 | [PureScript](http://www.purescript.org/) is statically typed language that compiles
8 | to JavaScript. Try PureScript! runs the PureScript compiler in the browser,
9 | it includes a simplified prelude.
10 |
11 | ### Installation
12 |
13 | Since Cabal support for web dependencies is not yet complete, you need to run a separate
14 | script to copy them into the jsexe directory:
15 |
16 | cabal install --ghcjs --only-dependencies
17 | cabal configure --ghcjs
18 | cabal build
19 | prepare.sh
20 |
21 | `prepare.sh` requires the tools described in the
22 | [Deployment wiki](https://github.com/ghcjs/ghcjs/wiki/Deployment) to be installed. It minifies
23 | the scripts, but `ADVANCED_OPTIMIZATIONS` (full renaming and dead code elimination) is currently
24 | disabled due to some problems with it. It should be enabled again when these have been fixed.
25 |
26 | to test locally with `warp-static`:
27 |
28 | cd dist/build/try-purescript/try-purescript.jsexe
29 | warp
30 |
31 | Edit `dist/build/try-purescript/try-purescript.jsexe/index.html` to load `all.js`
32 | instead of `all.min.js` for development. Then to rebuild, run:
33 |
34 | cabal build
35 |
36 |
--------------------------------------------------------------------------------
/mloc-js/mloc-js.cabal:
--------------------------------------------------------------------------------
1 | name: mloc-js
2 | version: 0.0.1
3 | cabal-version: >=1.8
4 | build-type: Simple
5 | license: BSD3
6 | license-file: ""
7 | data-dir: "data"
8 |
9 | library
10 | build-depends: base -any, blaze-html -any, blaze-markup -any,
11 | containers >=0.5.0.0 && <0.6, filepath -any, ghcjs-dom -any,
12 | hamlet -any, hscolour >=1.20.3 && <1.21,
13 | jsc >=0.1.0.0 && <0.2, lens >=3.8.5 && <3.11, mtl -any,
14 | random >=1.0.1.1 && <1.1, sodium -any, template-haskell -any,
15 | text -any, webkit-sodium -any, gloss -any, array -any,
16 | jmacro -any
17 | exposed-modules: Demo.DOM Demo.JavaScriptFFI Demo.LazyLoading
18 | Demo.Threading
19 | exposed: True
20 | buildable: True
21 | hs-source-dirs: src
22 | other-modules: Demo.Life WebKitUtils
23 |
24 | executable mloc-js
25 | build-depends: base -any, blaze-html -any, blaze-markup -any,
26 | containers >=0.5.0.0 && <0.6, filepath -any, ghcjs-dom -any,
27 | hamlet -any, hscolour >=1.20.3 && <1.21,
28 | jsc >=0.1.0.0 && <0.2, lens >=3.8.5 && <3.11, mtl -any,
29 | random >=1.0.1.1 && <1.1, sodium -any, template-haskell -any,
30 | text -any, webkit-sodium -any, gloss -any, array -any,
31 | jmacro -any
32 | main-is: Main.hs
33 | buildable: True
34 | hs-source-dirs: src
35 | other-modules: Demo.Life WebKitUtils
36 |
37 |
--------------------------------------------------------------------------------
/fay-hello/js/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2012, Hamish Mackenzie
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Hamish Mackenzie nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/webkit-hello/src/Main.hs:
--------------------------------------------------------------------------------
1 | module Main (
2 | main
3 | ) where
4 |
5 | import Graphics.UI.Gtk.WebKit.WebView
6 | (webViewNew, webViewGetDomDocument)
7 | import Graphics.UI.Gtk.WebKit.DOM.Document
8 | (documentCreateElement, documentGetElementById, documentGetBody)
9 | import Graphics.UI.Gtk.WebKit.DOM.HTMLElement
10 | (htmlElementGetInnerHTML, htmlElementInsertAdjacentElement,
11 | htmlElementSetInnerHTML)
12 | import Graphics.UI.Gtk.WebKit.Types (castToHTMLElement)
13 | import Control.Applicative ((<$>))
14 | import Control.Monad.Trans ( liftIO )
15 | import Graphics.UI.Gtk.General.Enums (WindowPosition(..))
16 | import Graphics.UI.Gtk.WebKit.DOM.Element (elementOnclick)
17 | import Graphics.UI.Gtk.WebKit.DOM.EventM (mouseXY, target)
18 | import Graphics.UI.Gtk.WebKit.GHCJS (runWebGUI)
19 |
20 | -- Comments show how what these FFI calls should work when the
21 | -- code compiled is compiled with GHCJS
22 | main = runWebGUI $ \ webView -> do
23 | doc <- webViewGetDomDocument webView -- webView.document
24 | Just body <- documentGetBody doc -- doc.body
25 | htmlElementSetInnerHTML body -- body.setInnerHTML
26 | ""
27 | Just button <- fmap castToHTMLElement <$>
28 | documentGetElementById doc "test" -- doc.getElementById
29 | elementOnclick button $ do -- button.onclick =
30 | target <- target -- window.event.target
31 | xy <- mouseXY -- window.event.X and window.event.Y
32 | liftIO $ do
33 | Just div <- fmap castToHTMLElement <$>
34 | documentCreateElement doc "div" -- doc.createElement
35 | -- doc.setInnerHTML
36 | htmlElementSetInnerHTML div ("You clicked at " ++ show xy)
37 | -- target.insertAdjacentElement
38 | htmlElementInsertAdjacentElement target "afterEnd" (Just div)
39 | return ()
40 | return ()
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/threads/threads.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE QuasiQuotes, OverloadedStrings, ScopedTypeVariables #-}
2 |
3 | {-
4 | start 10000 threads that each randomly update the color of a single cell in a table
5 | -}
6 |
7 | module Main where
8 |
9 | import Control.Applicative
10 | import Control.Concurrent
11 | import Control.Monad
12 | import System.Random
13 |
14 | import GHCJS.Foreign.QQ
15 | import GHCJS.Types
16 |
17 | addStyle :: [JSString] -> IO ()
18 | addStyle styles = do
19 | (sh :: JSRef ()) <-
20 | [jsu| var st = document.createElement('style');
21 | st.appendChild(document.createTextNode(''));
22 | document.head.appendChild(st);
23 | $r = st.sheet;
24 | |]
25 | forM_ styles $ \s -> [jsu_| `sh.insertRule(`s, 0); |]
26 |
27 | addChild :: JSRef () -> JSString -> IO (JSRef ())
28 | addChild parent tagName =
29 | [jsu| var elem = document.createElement(`tagName);
30 | `parent.appendChild(elem);
31 | $r = elem;
32 | |]
33 |
34 | setCol :: JSRef () -> Int -> IO ()
35 | setCol elem col = [jsu_| `elem.className = 'col-' + `col; |]
36 |
37 | main :: IO ()
38 | main = do
39 | let dim = 100
40 | addStyle [ "body { background-color: #666; }"
41 | , "table { border-collapse: collapse; }"
42 | , "td { width: 7px; height: 7px; padding: 0; margin: 0; border: none; }"
43 | , "td.col-0 { background-color: #000; }", "td.col-1 { background-color: #444; }"
44 | , "td.col-2 { background-color: #888; }", "td.col-3 { background-color: #bbb; }"
45 | , "td.col-4 { background-color: #fff; }"
46 | ]
47 | table <- addChild [jsu'| document.body |] "table"
48 | rows <- replicateM dim (addChild table "tr")
49 | cells <- concat <$> forM rows (\r -> replicateM dim (addChild r "td"))
50 | forM_ cells (void . forkIO . cellThread)
51 |
52 | cellThread :: JSRef () -> IO a
53 | cellThread elem = forever $ do
54 | setCol elem =<< randomRIO (0,4)
55 | threadDelay . (1000000+) =<< randomRIO (0,10000000)
56 |
--------------------------------------------------------------------------------
/webkit-sodium/src/Main.hs:
--------------------------------------------------------------------------------
1 | module Main (
2 | main
3 | ) where
4 |
5 | import Engine
6 | import Freecell
7 |
8 | import GHCJS.DOM.Document
9 | (documentCreateElement, documentGetElementById, documentGetBody)
10 | import GHCJS.DOM.HTMLElement
11 | (htmlElementInsertAdjacentElement, htmlElementSetInnerHTML,
12 | htmlElementInsertAdjacentHTML)
13 | import GHCJS.DOM.Types (castToHTMLDivElement)
14 | import GHCJS.DOM.CSSStyleDeclaration
15 | (cssStyleDeclarationSetProperty)
16 | import Control.Applicative ((<$>))
17 | import Control.Arrow
18 | import Control.Monad.Trans ( liftIO )
19 | import GHCJS.DOM.Element
20 | import GHCJS.DOM.Node
21 | import Control.Monad
22 | import System.Random
23 | import FRP.Sodium
24 | import GHCJS.DOM (runWebGUI, webViewGetDomDocument)
25 | import Control.Concurrent (threadDelay, forkIO)
26 |
27 | -- Comments show how what these FFI calls should work when the
28 | -- code compiled is compiled with GHCJS
29 | main = do
30 | runWebGUI $ \ webView -> do
31 | Just doc <- webViewGetDomDocument webView -- webView.document
32 | Just body <- documentGetBody doc -- doc.body
33 |
34 | -- If we are in the browser let's shrink the terminal window to make room
35 | mbTerminal <- fmap castToHTMLDivElement <$> documentGetElementById doc "terminal"
36 | case mbTerminal of
37 | Just terminal -> do
38 | Just style <- elementGetStyle terminal
39 | cssStyleDeclarationSetProperty style "height" "100" ""
40 | _ -> return ()
41 |
42 | Just div <- fmap castToHTMLDivElement <$> documentCreateElement doc "div"
43 | elementSetAttribute div "style" "position:relative;left:0px;top:0px;background-color:#e0d0ff;width:700px;height:500px"
44 | elementSetAttribute div "id" "freecell"
45 | nodeAppendChild body (Just div)
46 | unlisten <- engine webView "freecell" =<< mkFreecell
47 |
48 | -- Prevent finalizers running too soon
49 | forkIO $ forever (threadDelay 1000000000) >> unlisten
50 |
51 | return ()
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | GHCJS Examples
2 | ==============
3 |
4 | These examples demonstrate some of the features of GHCJS. Click on the links below to run
5 | the examples in your browser.
6 |
7 | [GHCJS Hello](http://ghcjs.github.com/bin/ghcjs-hello.trampoline.jsexe/)
8 | * Lazy Loading
9 | * Stdout and Stdin
10 | * Threading and MVars
11 | * Calling JavaScript from Haskell
12 | * Calling back into Haskell from JavaScript
13 | * Embedding JMacro code
14 | * Using Hamlet
15 | * Canvas
16 |
17 | [Freecell](http://ghcjs.github.com/bin/freecell.trampoline.jsexe/)
18 | * FRP in JavaScript
19 |
20 | [Multiple Pages](http://ghcjs.github.com/share/multiple-pages-0.0.1/)
21 | * Adding GHCJS to an existing website
22 | * Lazy Loading
23 |
24 | Compiled to Native with GHC and WebKitGTK
25 | -----------------------------------------
26 |
27 | [](https://travis-ci.org/ghcjs/ghcjs-examples)
28 |
29 | To build and run these examples using WebKitGTK+ then do the following
30 |
31 | sudo apt-get install libwebkitgtk-3.0-dev
32 | mkdir vendor
33 | cd vendor
34 | darcs get --lazy http://patch-tag.com/r/hamish/gtk2hs
35 | cabal install ./gtk2hs/tools
36 | cd ..
37 | cabal install cabal-meta cabal-src
38 | cabal-meta install -fgtk3 --force-reinstalls
39 |
40 | Due to an issue with gtk2hsC2hs you may have to run that last step twice.
41 | You may also need to add -fwebkit1-8 if you have an older version of webkit.
42 |
43 | Once this is done you should run the examples with
44 | ghcjs-hello
45 | freecell
46 | multiple-pages [URL to share/multiple-pages-0.0.1/index.html]
47 |
48 | Installing WebKitGTK+ is not well supported on OS X and Windows, so we
49 | strongly recommend using Linux (or a Linux VM).
50 |
51 | Compiled to JavaScript with Integrated GHCJS
52 | --------------------------------------------
53 | Follow the instructions in Integrated section of [GHCJS](https://github.com/ghcjs/ghcjs)
54 |
55 | Compiled to JavaScript with Stand Alone GHCJS
56 | ---------------------------------------------
57 | TODO Add instructions....
58 |
59 |
--------------------------------------------------------------------------------
/weblog/calculator/calculator.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}
2 |
3 | module Main where
4 |
5 | import Control.Applicative
6 | import Control.Monad
7 | import Data.Default
8 | import Data.Text (Text)
9 | import qualified Data.Text as T
10 | import Text.Read
11 |
12 | import JavaScript.JQuery
13 |
14 | import FRP.Sodium
15 |
16 | main :: IO ()
17 | main = do
18 | body <- select "body"
19 | [op1, op2] <- replicateM 2 $
20 | fmap (fmap (readMaybe . T.unpack)) (reactiveTextInput "0" body)
21 | let items = [ ("add" , arithBehaviour op1 op2 (+))
22 | , ("multiply", arithBehaviour op1 op2 (*))
23 | ]
24 | sel <- reactiveSelect items body
25 | output <- select ""
26 | appendJQuery output body
27 | sync $ do
28 | result <- switch sel
29 | listen (values result) $ \v -> void $
30 | setText (maybe "invalid input" (T.pack.show) v) output
31 | return ()
32 |
33 | arithBehaviour :: Behaviour (Maybe Integer)
34 | -> Behaviour (Maybe Integer)
35 | -> (Integer -> Integer -> Integer)
36 | -> Behaviour (Maybe Integer)
37 | arithBehaviour op1 op2 f = liftA2 f <$> op1 <*> op2
38 |
39 | reactiveTextInput :: Text -> JQuery -> IO (Behaviour Text)
40 | reactiveTextInput value parent = do
41 | (b, a) <- sync (newBehaviour value)
42 | input <- select ""
43 | setVal value input
44 | appendJQuery input parent
45 | let handler _ = sync . a =<< getVal input
46 | on handler "keyup change" def input
47 | return b
48 |
49 | reactiveSelect :: [(Text,a)] -> JQuery -> IO (Behaviour a)
50 | reactiveSelect items parent = do
51 | (b, a) <- sync (newBehaviour . snd . head $ items)
52 | sel <- select ""
53 | forM_ (zip [(0::Int)..] items) $ \(n,(name,_)) -> do
54 | opt <- select ""
55 | setAttr "value" (T.pack . show $ n) opt
56 | when (n == 0) $ void (setAttr "selected" "true" opt)
57 | setText name opt
58 | appendJQuery opt sel
59 | appendJQuery sel parent
60 | let handler _ = sync . a =<< snd.(items !!).read.T.unpack <$> getVal sel
61 | on handler "change" def sel
62 | return b
63 |
--------------------------------------------------------------------------------
/mloc-js/src/Demo/LazyLoading.hs:
--------------------------------------------------------------------------------
1 | -----------------------------------------------------------------------------
2 | --
3 | -- Module : Demo.LazyLoading
4 | -- Copyright : (c) Hamish Mackenzie, Luite Stegeman
5 | -- License : MIT
6 | --
7 | -- | For large applications it is usefule to strip out unused code and move
8 | -- code that is not always needed into separate files.
9 | --
10 | -- The GHCJS linker performs Forest Shaking. The function call graph is
11 | -- a directed graph. Given a set of root nodes (entry point functions
12 | -- that we want to trigger code loading), it sorts the functions into files
13 | -- such that we refer to as bundles.
14 | --
15 | -- * No function is in more than one bundle.
16 | --
17 | -- * Functions called by the same set of roots are put in the same bundle.
18 | --
19 | -- * No bundle is smaller than a threshold size (the smallest files are merged).
20 | --
21 | -- The goal is to minimize the amount of JavaScript that needs to be loaded
22 | -- while avoiding having too many small files fetched from the server.
23 | --
24 | -----------------------------------------------------------------------------
25 |
26 | module Demo.LazyLoading (
27 | lazyLoad_freecell
28 | ) where
29 |
30 | import GHCJS.DOM.HTMLElement
31 | (htmlElementSetInnerHTML)
32 | import Engine (engine)
33 | import Freecell (mkFreecell)
34 | import Control.Concurrent (threadDelay, forkIO)
35 | import Control.Monad (forever)
36 |
37 | -- | This function loads a FRP demo written by Stephen Blackheath.
38 | -- The NOINLINE pragma tells GHC not to inline it (which would
39 | -- make it imposible for the linker to replace it). The linker
40 | -- detects the lazyLoad_ prefix and replaces the function with
41 | -- a loader function that fetches the bundles.
42 | {-# NOINLINE lazyLoad_freecell #-}
43 | lazyLoad_freecell webView doc example = do
44 | htmlElementSetInnerHTML example $
45 | ""
47 | unlisten <- engine webView "freecell" =<< mkFreecell
48 | -- Prevent finalizers running too soon
49 | forkIO $ forever (threadDelay 1000000000) >> unlisten
50 | return ()
51 | where
52 | style = "position:relative;left:0px;top:0px;"
53 | ++ "background-color:#e0d0ff;width:700px;height:500px"
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/multiple-pages/data/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Multiple Pages
8 |
9 |
10 |
Multiple Pages
11 |
12 |
This example shows how you could use GHCJS with an existing web site.
13 | To keep it simple this example uses static HTML pages.
The source for this example is
21 | here.
22 |
23 |
Think of your GHCJS application as a custom browser for your web site.
24 |
25 |
In an ideal world your users would run a web browser with all the features
26 | you wanted and there would be no need for JavaScript. Instead we use JavaScript
27 | to add the features that are missing.
28 |
29 |
With GHCJS you can do this by writing a simple browser app like this one
30 | using WebKitGtk. Then GHCJS will compile it to JavaScript and you can
31 | include it in all your HTML.
32 |
33 |
One slight complication is that you don't want to wind up running your
34 | code twice when you open the HTML in the webkit browser. The ghcjs-dom
35 | package has a runWebGUI function. As well as creating a WebKitGtk browser
36 | window in native mode, it uses the UserAgent to identify when the
37 | JavaScript version is running inside the Native version.
38 |
39 |
Every time you load a page the Main.main function runs and looks
40 | for content on the page to augment. It can use lazy loading to limit
41 | the amount of JavaScript loaded for pages with little dynamic content.
42 |
43 |
To build & run in native code
44 |
45 |
With the regular GHC or integrated GHCJS installed.