JavaScript via JMacro

To use JMacro with happstack and hsx, you should install the hsx-jmacro and happstack-jmacro packages. You will also need to be sure that your version of happstack-hsp is >= 6.1.0.

JMacro is a library that makes it easy to include javascript in your templates. The syntax used by JMacro is almost identical to JavaScript. So, you do not have to learn some special DSL to use it. In fact, JMacro can work with most JavaScript you find in the wild. Using JMacro has a number of advantages over just using plain-old javascript. * syntax checking ensures that your JavaScript is syntactically valid at compile time. That eliminates many common JavaScript errors and reduces development time. * hygienic names and scoping automatically and transparently ensure that blocks of JavaScript code do not accidentally create variables and functions with conflicting names. * Antiquotation, marshalling, and shared scope make it easy to splice Haskell values into the JavaScript code. It also makes it easy to programmatically generate JavaScript code. The hsx-jmacro and happstack-jmacro libraries makes it easy to use JMacro with Happstack and HSP. The following examples demonstrate the basics of JMacro and how it interfaces with HSX and Happstack. The examples are intended to demonstrate what is possible with JMacro. The examples are not intended to demonstrate good javascript practices. For example, many developers frown on the use of the onclick attribute in html, or having <script> tags in the <body>. The JMacro library does not require any external pre-processors. Instead it uses the magic of QuasiQuotation. QuasiQuotes can be enabled via the LANGUAGE extension:
> {-# LANGUAGE CPP, FlexibleInstances, GeneralizedNewtypeDeriving, > TypeSynonymInstances, QuasiQuotes #-}
In this example we are also using HSX, which does require a pre-processor. (A crash course section on HSX itself will be coming soon). The following line will automatically run the pre-processor for us (and also suppress warnings about orphan instances):
> {-# OPTIONS_GHC -F -pgmFtrhsx -fno-warn-orphans #-}
Next we have a boatload of imports. Not all of these are required to use JMacro. Many are just used for the demos.
> import Control.Applicative ((<$>), optional) > import Control.Monad (msum) > import Control.Monad.State (StateT, evalStateT) > import Control.Monad.Trans (liftIO) > import qualified Data.Map as Map > import Data.Maybe (fromMaybe) > import Happstack.Server (Response, ServerPartT, dir, mapServerPartT, look, nullConf, > ok, simpleHTTP, toResponse) > import Happstack.Server.HSP.HTML (defaultTemplate) -- ^ also imports 'ToMessage XML' > import Happstack.Server.JMacro () -- ToMessage instance for JStat > import HSP ( Attr(..), EmbedAsAttr(..), EmbedAsChild(..), genElement > , genEElement) > import HSP.ServerPartT () -- ^ instance 'XMLGenerator ServerPartT' > import HSX.JMacro (IntegerSupply(..), nextInteger') -- ^ EmbedAsChild and EmbedAsAttr for JStat > import Language.Javascript.JMacro ( ToJExpr(..), Ident(..), JStat(..), JExpr(..), JVal(..) > , jmacro, jsv, jLam, jVarTy) > import System.Random (Random(..)) >
In order to ensure that each <script> tag generates unique variables names, we need a source of unique prefixes. An easy way to do that is to wrap the ServerPartT monad around a StateT monad that supplies integers:
> type JMacroPart = ServerPartT (StateT Integer IO) > > instance IntegerSupply JMacroPart where > nextInteger = nextInteger' >
The nextInteger' helper function has the type:
#ifdef HsColour > nextInteger' :: (MonadState Integer m) => m Integer #endif
To use JMacroPart with simpleHTTP, we just evaluate the StateT monad:
> main :: IO () > main = simpleHTTP nullConf $ flatten handlers > where > flatten :: JMacroPart a -> ServerPartT IO a > flatten = mapServerPartT (flip evalStateT 0) >

JMacro in a <script> tag

Now that we have the scene set, we can actually look at some JMacro usage. In this example we embed a single JavaScript block inside the page:
> helloJMacro :: JMacroPart Response > helloJMacro = > toResponse <$> defaultTemplate "Hello JMacro" () >
> <% [$jmacro| > var helloNode = document.createElement('h1'); > helloNode.appendChild(document.createTextNode("Hello, JMacro!")); > document.body.appendChild(helloNode); > |] %> >
>
We do not need to specify the <script> tag explicitly, it will automatically be created for us. The syntax `[$jmacro| ... |]` is the magic incantation for running the `jmacro` quasiquoter. In GHC 7.x, the $ is no longer required, so in theory you could write, `[jmacro| ... |]`. However, HSX has not been updated to support the $ free syntax. So, for now you will need to stick with the $ syntax, despite the compiler warnings saying, Warning: Deprecated syntax: quasiquotes no longer need a dollar sign: $jmacro.

JMacro in an HTML attribute (onclick, etc)

We can also use JMacro inside html attributes, such as onclick.
> helloAttr :: JMacroPart Response > helloAttr = > toResponse <$> defaultTemplate "Hello Attr" () >

!") |]>Click me!

>
Note that we do not have to worry about escaping the ", < or > in the onclick handler. It is taken care of for us automatically! The code is automatically escaped as:
onclick="alert(&quot;that &lt;/tickles&gt;!&quot;);"

Automatice escaping of </

According to the HTML spec it is invalid for </ to appear anywhere inside the <script> tag. The JMacro embedding also takes care of handling </ appearing in string literals. So we can just write this:
> helloEndTag :: JMacroPart Response > helloEndTag = > toResponse <$> defaultTemplate "Hello End Tag" () > <%> >

Tricky End Tag

> <% [$jmacro| alert("this won't mess things up!") |] %> > >
And it will generate:
<script type="text/javascript">alert("this <\/script> won't mess things up!");</script>

Hygienic Variable Names

So far, using HSP with JMacro looks almost exactly like using HSP with plain-old JavaScript. That's actually pretty exciting. It means that the mental tax for using JMacro over straight JavaScript is very low. Now let's look at an example of hygienic naming. Let's say we write the following block of JavaScript code:
> clickMe :: JStat > clickMe = > [$jmacro| > > var clickNode = document.createElement('p'); > clickNode.appendChild(document.createTextNode("Click me!")); > document.body.appendChild(clickNode); > var clickCnt = 0; > clickNode.setAttribute('style', 'cursor: pointer'); > clickNode.onclick = function () { clickCnt++; > alert ('Been clicked ' + clickCnt + ' time(s).'); > }; > |] >
That block of code tracks how many times you have clicked on the Click me! text. It uses a global variable to keep track of the number of clicks. Normally that would spell trouble. If we tried to use that code twice on the same page, both copies would end up writing to the same global variable `clickCnt`. But, JMacro automatically renames the variables for us so that the names are unique. In the following code each Click me! tracks its counts separately:
> clickPart :: JMacroPart Response > clickPart = > toResponse <$> defaultTemplate "Hygienic Naming" () >
>

A Demo of Happstack+HSP+JMacro

> <% clickMe %> > <% clickMe %> >
>

Non-Hygienic Variable Names

Of course, sometimes we want the code blocks to share a global variable. We can easily do that by changing the line:
#ifdef HsColour > var clickCnt = 0; #endif
to
#ifdef HsColour > var !clickCnt = 0; #endif
The use of ! when declaring a variable disables hygienic naming. Now all the copies of clickMe2 will share the same counter:
> clickMe2Init :: JStat > clickMe2Init = > [$jmacro| var !clickCnt = 0; |]; > > clickMe2 :: JStat > clickMe2 = > [$jmacro| > > var clickNode = document.createElement('p'); > clickNode.appendChild(document.createTextNode("Click me!")); > document.body.appendChild(clickNode); > clickNode.setAttribute("style", "cursor: pointer"); > clickNode.onclick = function () { clickCnt++; > alert ('Been clicked ' + clickCnt + ' time(s).'); > }; > |] > > clickPart2 :: JMacroPart Response > clickPart2 = > toResponse <$> defaultTemplate "Hygienic Naming" > <% clickMe2Init %> >
>

A Demo of Happstack+HSP+JMacro

> <% clickMe2 %> > <% clickMe2 %> >
>

Declaring Functions

Hygienic naming affects function declarations as well. If we want to define a function in <head>, but call the function from the <body>, then we need to disable hygienic naming. We can do that using the ! trick again:
#ifdef HsColour > function !hello(noun) { alert('hello ' + noun); } #endif
JMacro also has some syntax extensions for declaring functions. We can create an anonymous function using Haskell-like syntax assign it to a variable:
#ifdef HsColour > var !helloAgain = \noun ->alert('hello again, ' + noun); #endif
Another option is to use the ML-like fun keyword to declare a function. When using fun we do not need the !.
#ifdef HsColour > fun goodbye noun { alert('goodbye ' + noun); } #endif
Or we can do both:
#ifdef HsColour > fun goodbyeAgain noun -> alert('goodbye again, ' + noun); #endif
Here they all are in an example:
> functionNames :: JMacroPart Response > functionNames = > toResponse <$> defaultTemplate "Function Names" > <% [$jmacro| > function !hello(noun) { alert('hello, ' + noun); } > var !helloAgain = \noun ->alert('hello again, ' + noun); > fun goodbye noun { alert('goodbye ' + noun); } > fun goodbyeAgain noun -> alert('goodbye again, ' + noun); > |] > %> > <%> > > > > > >

Splicing Haskell Values into JavaScript (Antiquotation)

We can also splice Haskell values into the JavaScript code by using `( )`. In the following example, the `onclick` action for the <button> calls `revealFortune()`. The argument to `revealForture` is the `String` returned by evaluating the Haskell expression `fortunes !! n`.
> fortunePart :: JMacroPart Response > fortunePart = > do let fortunes = ["You will be cursed to write Java for the rest of your days." > , "Fortune smiles upon you, your future will be filled with lambdas" > ] > n <- liftIO $ randomRIO (0, (length fortunes) - 1) > > toResponse <$> defaultTemplate "Fortune" > <% [$jmacro| > fun revealFortune fortune > { > var b = document.getElementById("button"); > b.setAttribute('disabled', 'disabled'); > var p = document.getElementById("fortune"); > p.appendChild(document.createTextNode(fortune)); > } > |] > %> >
>

Your Fortune

>

> >
>

Using ToJExpr to convert Haskell values to JavaScript

JMacro can embed common types such as `Int`, `Bool`, `Char`, `String`, etc, by default. But we can also embed other types by creating a `ToJExpr` instance for them. For example, let's say we create some types for reporting the weather:
> data Skies = Cloudy | Clear > deriving (Bounded, Enum, Eq, Ord, Read, Show) > > newtype Fahrenheit = Fahrenheit Double > deriving (Num, Enum, Eq, Ord, Read, Show, ToJExpr, Random) > > data Weather = Weather > { skies :: Skies > , temp :: Fahrenheit > } > deriving (Eq, Ord, Read, Show) > > instance Random Skies where > randomR (lo, hi) g = > case randomR (fromEnum lo, fromEnum hi) g of > (c, g') -> (toEnum c, g') > random g = randomR (minBound, maxBound) g > > instance Random Weather where > randomR (Weather skiesLo tempLo, Weather skiesHi tempHi) g = > let (skies, g') = randomR (skiesLo, skiesHi) g > (temp, g'') = randomR (tempLo, tempHi) g' > in ((Weather skies temp), g'') > random g = > let (skies, g') = random g > (temp, g'') = random g' > in ((Weather skies temp), g'') >
To pass these values into the generated JavaScript, we simply create a `ToJExpr` instance:
#ifdef HsColour > class ToJExpr a where > toJExpr :: a -> JExpr #endif
For `Fahrenheit`, we were actually able to derive the `ToJExpr` instance automatically (aka, `deriving (ToJExpr)`), because it is a `newtype` wrapper around `Double` which already has a `ToExpr` instance. For `Skies`, we can just convert the constructors into JavaScript strings:
> instance ToJExpr Skies where > toJExpr = toJExpr . show >
For the `Weather` type, we create a JavaScript object/hash/associative array/record/whatever you want to call it:
> instance ToJExpr Weather where > toJExpr (Weather skies temp) = > toJExpr (Map.fromList [ ("skies", toJExpr skies) > , ("temp", toJExpr temp) > ]) >
Now we can splice a random weather report into our JavaScript:
> weatherPart :: JMacroPart Response > weatherPart = > do weather <- liftIO $ randomRIO ((Weather minBound (-40)), (Weather maxBound 100)) > toResponse <$> defaultTemplate "Weather Report" () >
> <% [$jmacro| > var w = `(weather)`; > var p = document.createElement('p'); > p.appendChild(document.createTextNode("The skies will be " + w.skies + > " and the temperature will be " + > w.temp.toFixed(1) + "°F")); > document.body.appendChild(p); > |] %> >
>
`ToJExpr` has an instance for `JSValue` from the json library. So, if your type already has a `JSON` istance, you can trivially create a `ToJExpr` instance for it:
#ifdef HsColour > instance ToJExpr Foo where > toJExpr = toJExpr . showJSON #endif

Using JMacro in external .js scripts

So far we have used JMacro to generate JavaScript that is embedded in HTML. We can also use it to create standalone JavaScript. First we have a script template that is parameterized by a greeting.
> externalJs :: String -> JStat > externalJs greeting = > [$jmacro| > fun greet noun > { > alert(`(greeting)` + ' ' + noun); > } > |] >
Then we have a server part with two sub-parts:
> externalPart :: JMacroPart Response > externalPart = dir "external" $ msum [
If external/script.js is requested, then we check for a query string parameter `greeting` and generate the script. `toResponse` will automatically convert the script to a `Response` and serve it with the content-type, text/javascript; charset=UTF-8:
> dir "script.js" $ > do greeting <- optional $ look "greeting" > ok $ toResponse $ externalJs (fromMaybe "hello" greeting)
Next we have an html page that includes the external script, and calls the `greet` function:
> , toResponse <$> defaultTemplate "external" >